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