• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 /* This files real do unwind. */
17 
18 #include "dfx_unwind_remote.h"
19 
20 #include <cstdio>
21 #include <cstring>
22 #include <elf.h>
23 #include <link.h>
24 #include <securec.h>
25 #include <sys/ptrace.h>
26 #include "dfx_config.h"
27 #include "dfx_define.h"
28 #include "dfx_logger.h"
29 #include "dfx_maps.h"
30 #include "dfx_process.h"
31 #include "dfx_regs.h"
32 #include "dfx_ring_buffer_wrapper.h"
33 #include "dfx_symbols_cache.h"
34 #include "dfx_thread.h"
35 #include "dfx_util.h"
36 #include "libunwind.h"
37 #include "libunwind_i-ohos.h"
38 #include "process_dumper.h"
39 
40 namespace OHOS {
41 namespace HiviewDFX {
42 namespace {
43 // we should have at least 2 frames, one is pc and the other is lr
44 // if pc and lr are both invalid, just try fp
45 static const int MIN_VALID_FRAME_COUNT = 3;
46 static constexpr size_t MAX_BUILD_ID_LENGTH = 32;
47 }
48 
GetInstance()49 DfxUnwindRemote &DfxUnwindRemote::GetInstance()
50 {
51     static DfxUnwindRemote ins;
52     return ins;
53 }
54 
DfxUnwindRemote()55 DfxUnwindRemote::DfxUnwindRemote()
56 {
57     as_ = nullptr;
58     std::unique_ptr<DfxSymbolsCache> cache(new DfxSymbolsCache());
59     cache_ = std::move(cache);
60 }
61 
UnwindProcess(std::shared_ptr<DfxProcess> process)62 bool DfxUnwindRemote::UnwindProcess(std::shared_ptr<DfxProcess> process)
63 {
64     bool ret = false;
65     if (!process) {
66         DfxLogWarn("%s::can not unwind null process.", __func__);
67         return ret;
68     }
69 
70     auto threads = process->GetThreads();
71     if (threads.empty()) {
72         DfxLogWarn("%s::no thread under target process.", __func__);
73         return ret;
74     }
75 
76     as_ = unw_create_addr_space(&_UPT_accessors, 0);
77     if (!as_) {
78         DfxLogWarn("%s::failed to create address space.", __func__);
79         return ret;
80     }
81 
82     unw_set_target_pid(as_, ProcessDumper::GetInstance().GetTargetPid());
83     unw_set_caching_policy(as_, UNW_CACHE_GLOBAL);
84     do {
85         // only need to unwind crash thread in crash scenario
86         if (!process->GetIsSignalDump() && !DfxConfig::GetInstance().GetDumpOtherThreads()) {
87             ret = UnwindThread(process, threads[0]);
88             if (!ret) {
89                 UnwindThreadFallback(process, threads[0]);
90             }
91 
92             if (threads[0]->GetIsCrashThread() && (!process->GetIsSignalDump())) {
93                 process->PrintProcessMapsByConfig();
94             }
95             break;
96         }
97 
98         size_t index = 0;
99         for (auto thread : threads) {
100             if (index == 1) {
101                 process->PrintThreadsHeaderByConfig();
102             }
103 
104             if (thread->Attach()) {
105                 UnwindThread(process, thread);
106                 thread->Detach();
107             }
108 
109             if (thread->GetIsCrashThread() && (!process->GetIsSignalDump())) {
110                 process->PrintProcessMapsByConfig();
111             }
112             index++;
113         }
114         ret = true;
115     } while (false);
116 
117     PrintBuildIds();
118     unw_destroy_addr_space(as_);
119     as_ = nullptr;
120     return ret;
121 }
122 
DfxUnwindRemoteDoAdjustPc(unw_cursor_t & cursor,uint64_t pc)123 uint64_t DfxUnwindRemote::DfxUnwindRemoteDoAdjustPc(unw_cursor_t & cursor, uint64_t pc)
124 {
125     DfxLogDebug("%s :: pc(0x%x).", __func__, pc);
126 
127     uint64_t ret = 0;
128 
129     if (pc <= ARM_EXEC_STEP_NORMAL) {
130         ret = pc; // pc zero is abnormal case, so we don't adjust pc.
131     } else {
132 #if defined(__arm__)
133         ret = pc - unw_get_previous_instr_sz(&cursor);
134 #elif defined(__aarch64__)
135         ret = pc - ARM_EXEC_STEP_NORMAL;
136 #elif defined(__x86_64__)
137         ret = pc - 1;
138 #endif
139     }
140 
141     DfxLogDebug("%s :: ret(0x%x).", __func__, ret);
142     return ret;
143 }
144 
GetReadableBuildId(uint8_t * buildId,size_t length)145 std::string DfxUnwindRemote::GetReadableBuildId(uint8_t* buildId, size_t length)
146 {
147     if (length > MAX_BUILD_ID_LENGTH) {
148         std::string ret = "Wrong Build-Id length:" + std::to_string(length);
149         return ret;
150     }
151 
152     static const char hexTable[] = "0123456789abcdef";
153     uint8_t* buildIdPtr = buildId;
154     std::string buildIdStr;
155     for (size_t i = 0; i < length; i++) {
156         buildIdStr.push_back(hexTable[*buildIdPtr >> 4]); // 4 : higher 4 bit of uint8
157         buildIdStr.push_back(hexTable[*buildIdPtr & 0xf]);
158         buildIdPtr++;
159     }
160     return buildIdStr;
161 }
162 
DfxUnwindRemoteDoUnwindStep(size_t const & index,std::shared_ptr<DfxThread> & thread,unw_cursor_t & cursor,std::shared_ptr<DfxProcess> process)163 bool DfxUnwindRemote::DfxUnwindRemoteDoUnwindStep(size_t const & index,
164     std::shared_ptr<DfxThread> & thread, unw_cursor_t & cursor, std::shared_ptr<DfxProcess> process)
165 {
166     bool ret = false;
167     uint64_t framePc;
168     static unw_word_t oldPc = 0;
169     if (unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t*)(&framePc))) {
170         DfxLogWarn("Fail to get current pc.");
171         return ret;
172     }
173 
174     uint64_t frameSp;
175     if (unw_get_reg(&cursor, UNW_REG_SP, (unw_word_t*)(&frameSp))) {
176         DfxLogWarn("Fail to get stack pointer.");
177         return ret;
178     }
179 
180     if (oldPc == framePc && index != 0) {
181         DfxLogWarn("%s :: repeated pc(0x%lx), stop.", __func__, framePc);
182         return ret;
183     }
184     oldPc = framePc;
185 
186     uint64_t relPc = unw_get_rel_pc(&cursor);
187     if (index != 0) {
188         relPc = DfxUnwindRemoteDoAdjustPc(cursor, relPc);
189         framePc = DfxUnwindRemoteDoAdjustPc(cursor, framePc);
190     }
191 
192     if (relPc == 0) {
193         relPc = framePc;
194     }
195 
196     auto frame = std::make_shared<DfxFrame>();
197     frame->SetFrameRelativePc(relPc);
198     frame->SetFramePc(framePc);
199     frame->SetFrameSp(frameSp);
200     frame->SetFrameIndex(index);
201     ret = UpdateAndPrintFrameInfo(cursor, thread, frame,
202         (thread->GetIsCrashThread() && !process->GetIsSignalDump()));
203     if (ret) {
204         thread->AddFrame(frame);
205     }
206 
207     DfxLogDebug("%s :: index(%d), framePc(0x%x), frameSp(0x%x).", __func__, index, framePc, frameSp);
208     return ret;
209 }
210 
UpdateAndPrintFrameInfo(unw_cursor_t & cursor,std::shared_ptr<DfxThread> thread,std::shared_ptr<DfxFrame> frame,bool enableBuildId)211 bool DfxUnwindRemote::UpdateAndPrintFrameInfo(unw_cursor_t& cursor, std::shared_ptr<DfxThread> thread,
212     std::shared_ptr<DfxFrame> frame, bool enableBuildId)
213 {
214     struct map_info* mapInfo = unw_get_map(&cursor);
215     bool isValidFrame = true;
216     if (mapInfo != nullptr) {
217         std::string mapPath = std::string(mapInfo->path);
218         frame->SetFrameMapName(mapPath);
219         std::string funcName;
220         uint64_t funcOffset;
221         if (cache_->GetNameAndOffsetByPc(as_, frame->GetFramePc(), funcName, funcOffset)) {
222             frame->SetFrameFuncName(funcName);
223             frame->SetFrameFuncOffset(funcOffset);
224         }
225 
226         if (enableBuildId && (buildIds_.find(mapPath) == buildIds_.end())) {
227             uint8_t* buildId = nullptr;
228             size_t length = 0;
229             if (unw_get_build_id(mapInfo, &buildId, &length)) {
230                 buildIds_.insert(std::pair<std::string, std::string>(std::string(mapPath),
231                     GetReadableBuildId(buildId, length)));
232             } else {
233                 buildIds_.insert(std::pair<std::string, std::string>(std::string(mapPath), "No GNU BuildId"));
234             }
235         }
236     } else {
237         std::string tips = "Not mapped ";
238         std::shared_ptr<DfxRegs> regs = thread->GetThreadRegs();
239         if (regs != nullptr) {
240             tips.append(regs->GetSpecialRegisterName(frame->GetFramePc()));
241         }
242         frame->SetFrameMapName(tips);
243         isValidFrame = false;
244     }
245 
246     bool ret = frame->GetFrameIndex() < MIN_VALID_FRAME_COUNT || isValidFrame;
247     if (ret) {
248         DfxRingBufferWrapper::GetInstance().AppendMsg(frame->PrintFrame());
249     }
250     return ret;
251 }
252 
PrintBuildIds() const253 void DfxUnwindRemote::PrintBuildIds() const
254 {
255     if (buildIds_.size() == 0) {
256         return;
257     }
258 
259     DfxRingBufferWrapper::GetInstance().AppendMsg("Related elf build-id:\n");
260     for (auto const& buildId : buildIds_) {
261         std::string line = buildId.first + ":" + buildId.second + "\n";
262         DfxRingBufferWrapper::GetInstance().AppendMsg(line);
263     }
264 }
265 
UnwindThread(std::shared_ptr<DfxProcess> process,std::shared_ptr<DfxThread> thread)266 bool DfxUnwindRemote::UnwindThread(std::shared_ptr<DfxProcess> process, std::shared_ptr<DfxThread> thread)
267 {
268     if (!thread) {
269         DfxLogError("NULL thread needs unwind.");
270         return false;
271     }
272 
273     bool isCrash = thread->GetIsCrashThread() && (process->GetIsSignalDump() == false);
274     pid_t nsTid = isCrash ? ProcessDumper::GetInstance().GetTargetNsPid() : thread->GetRealTid();
275     pid_t tid = thread->GetThreadId();
276     char buf[LOG_BUF_LEN] = {0};
277     int ret = snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "Tid:%d, Name:%s\n", tid, thread->GetThreadName().c_str());
278     if (ret <= 0) {
279         DfxLogError("%s :: snprintf_s failed, line: %d.", __func__, __LINE__);
280     }
281     DfxRingBufferWrapper::GetInstance().AppendMsg(std::string(buf));
282     void *context = _UPT_create(nsTid);
283     if (!context) {
284         DfxRingBufferWrapper::GetInstance().AppendBuf("Failed to create unwind context for %d.\n", nsTid);
285         return false;
286     }
287 
288     if (!as_) {
289         as_ = unw_create_addr_space(&_UPT_accessors, 0);
290         if (!as_) {
291             DfxRingBufferWrapper::GetInstance().AppendBuf("Unwind address space is not exist for %d.\n", nsTid);
292             _UPT_destroy(context);
293             return false;
294         }
295         unw_set_caching_policy(as_, UNW_CACHE_GLOBAL);
296     }
297 
298     unw_cursor_t cursor;
299     int unwRet = unw_init_remote(&cursor, as_, context);
300     if (unwRet != 0) {
301         DfxRingBufferWrapper::GetInstance().AppendBuf("Failed to init cursor for thread:%d code:%d.\n", nsTid, unwRet);
302         _UPT_destroy(context);
303         return false;
304     }
305 
306     std::shared_ptr<DfxRegs> regs = thread->GetThreadRegs();
307     if (regs != nullptr) {
308         std::vector<uintptr_t> regsVector = regs->GetRegsData();
309         unw_set_context(&cursor, regsVector.data(), regsVector.size());
310     }
311 
312     size_t index = 0;
313     do {
314         // store current frame
315         if (!DfxUnwindRemoteDoUnwindStep(index, thread, cursor, process)) {
316             break;
317         }
318         index++;
319 
320         // find next frame
321         unwRet = unw_step(&cursor);
322     } while ((unwRet > 0) && (index < BACK_STACK_MAX_STEPS));
323     thread->SetThreadUnwStopReason(unwRet);
324     if (isCrash) {
325         if (regs != nullptr) {
326             DfxRingBufferWrapper::GetInstance().AppendMsg(regs->PrintRegs());
327         }
328         if (DfxConfig::GetInstance().GetDisplayFaultStack()) {
329             thread->CreateFaultStack(nsTid);
330             thread->PrintThreadFaultStackByConfig();
331         }
332     }
333     _UPT_destroy(context);
334     return true;
335 }
336 
UnwindThreadFallback(std::shared_ptr<DfxProcess> process,std::shared_ptr<DfxThread> thread)337 void DfxUnwindRemote::UnwindThreadFallback(std::shared_ptr<DfxProcess> process, std::shared_ptr<DfxThread> thread)
338 {
339     // As we failed to init libunwind, just print pc and lr for first two frames
340     std::shared_ptr<DfxRegs> regs = thread->GetThreadRegs();
341     if (regs == nullptr) {
342         DfxRingBufferWrapper::GetInstance().AppendMsg("RegisterInfo is not existed for crash process");
343         return;
344     }
345 
346     std::shared_ptr<DfxElfMaps> maps = process->GetMaps();
347     if (maps == nullptr) {
348         DfxRingBufferWrapper::GetInstance().AppendMsg("MapsInfo is not existed for crash process");
349         return;
350     }
351 
352     auto createFrame = [maps, thread] (int index, uintptr_t pc) {
353         std::shared_ptr<DfxElfMap> map;
354         auto frame = std::make_shared<DfxFrame>();
355         frame->SetFramePc(pc);
356         frame->SetFrameIndex(index);
357         thread->AddFrame(frame);
358         if (maps->FindMapByAddr(pc, map)) {
359             frame->SetFrameMap(map);
360             frame->CalculateRelativePc(map);
361             frame->SetFrameMapName(map->GetMapPath());
362         } else {
363             frame->SetFrameRelativePc(pc);
364             frame->SetFrameMapName(index == 0 ? "Not mapped pc" : "Not mapped lr");
365         }
366         DfxRingBufferWrapper::GetInstance().AppendMsg(frame->PrintFrame());
367     };
368 
369     createFrame(0, regs->GetPC());
370     createFrame(1, regs->GetLR());
371     DfxRingBufferWrapper::GetInstance().AppendMsg(regs->PrintRegs());
372 }
373 } // namespace HiviewDFX
374 } // namespace OHOS
375