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