1 /*
2 * Copyright (c) 2021 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 #ifdef __clang__
17 #pragma clang diagnostic push
18 #pragma clang diagnostic ignored "-Wextern-c-compat"
19 #endif
20
21 #include "dfx_unwind_local.h"
22
23 #include <cerrno>
24 #include <cinttypes>
25 #include <csignal>
26 #include <cstdio>
27 #include <cstdlib>
28 #include <cstring>
29 #include <ctime>
30 #include <fcntl.h>
31 #include <iostream>
32 #include <libunwind.h>
33 #include <libunwind_i-ohos.h>
34 #include <pthread.h>
35 #include <sched.h>
36 #include <securec.h>
37 #include <sstream>
38 #include <sys/capability.h>
39 #include <sys/prctl.h>
40 #include <sys/syscall.h>
41 #include <sys/types.h>
42 #include <sys/uio.h>
43 #include <sys/wait.h>
44 #include <unistd.h>
45 #include "dfx_symbols_cache.h"
46 #include "file_ex.h"
47
48 namespace OHOS {
49 namespace HiviewDFX {
50 namespace {
51 static constexpr int MIN_VALID_FRAME_COUNT = 3;
52 }
GetInstance()53 DfxUnwindLocal &DfxUnwindLocal::GetInstance()
54 {
55 static DfxUnwindLocal ins;
56 return ins;
57 }
58
DfxUnwindLocal()59 DfxUnwindLocal::DfxUnwindLocal()
60 {
61 as_ = nullptr;
62 curIndex_ = 0;
63 insideSignalHandler_ = false;
64 sigemptyset(&mask_);
65 (void)memset_s(&oldSigaction_, sizeof(oldSigaction_), 0, sizeof(oldSigaction_));
66 (void)memset_s(&context_, sizeof(context_), 0, sizeof(context_));
67 }
68
Init()69 bool DfxUnwindLocal::Init()
70 {
71 std::unique_lock<std::mutex> lck(localDumperMutex_);
72 initTimes_++;
73
74 if (isInited_) {
75 DfxLogError("local handler has been inited.");
76 return isInited_;
77 }
78
79 unw_init_local_address_space(&as_);
80 if (as_ == nullptr) {
81 DfxLogError("Failed to init local address aspace.");
82 return false;
83 }
84
85 (void)memset_s(&context_, sizeof(context_), 0, sizeof(context_));
86 frames_ = std::vector<DfxFrame>(BACK_STACK_MAX_STEPS);
87 InstallLocalDumper(SIGLOCAL_DUMP);
88 sigemptyset(&mask_);
89 sigset_t mask;
90 sigfillset(&mask);
91 sigdelset(&mask, SIGABRT);
92 sigdelset(&mask, SIGBUS);
93 sigdelset(&mask, SIGILL);
94 sigdelset(&mask, SIGSEGV);
95 sigprocmask(SIG_SETMASK, &mask, &mask_);
96 std::unique_ptr<DfxSymbolsCache> cache(new DfxSymbolsCache());
97 cache_ = std::move(cache);
98 isInited_ = true;
99 return isInited_;
100 }
101
Destroy()102 void DfxUnwindLocal::Destroy()
103 {
104 std::unique_lock<std::mutex> lck(localDumperMutex_);
105 if (initTimes_ >= 0) {
106 initTimes_--;
107 } else {
108 DfxLogError("%s :: Init must be called before Destroy.", __func__);
109 }
110
111 if (!isInited_) {
112 return;
113 }
114
115 if (initTimes_ > 0) {
116 return;
117 }
118
119 frames_.clear();
120 frames_.shrink_to_fit();
121 UninstallLocalDumper(SIGLOCAL_DUMP);
122 sigprocmask(SIG_SETMASK, &mask_, nullptr);
123 unw_destroy_local_address_space(as_);
124 as_ = nullptr;
125 cache_ = nullptr;
126 isInited_ = false;
127 }
128
HasInit()129 bool DfxUnwindLocal::HasInit()
130 {
131 return isInited_;
132 }
133
SendAndWaitRequest(int32_t tid)134 bool DfxUnwindLocal::SendAndWaitRequest(int32_t tid)
135 {
136 if (SendLocalDumpRequest(tid)) {
137 return WaitLocalDumpRequest();
138 }
139 return false;
140 }
141
SendLocalDumpRequest(int32_t tid)142 bool DfxUnwindLocal::SendLocalDumpRequest(int32_t tid)
143 {
144 insideSignalHandler_ = false;
145 return syscall(SYS_tkill, tid, SIGLOCAL_DUMP) == 0;
146 }
147
CollectUnwindResult(int32_t tid)148 std::string DfxUnwindLocal::CollectUnwindResult(int32_t tid)
149 {
150 if (tid < 0) {
151 return std::string("");
152 }
153
154 std::ostringstream result;
155 result << "Tid:" << tid;
156 std::string path = "/proc/self/task/" + std::to_string(tid) + "/comm";
157 std::string threadComm;
158 if (OHOS::LoadStringFromFile(path, threadComm)) {
159 result << " comm:" << threadComm;
160 } else {
161 result << std::endl;
162 }
163
164 if (curIndex_ == 0) {
165 result << "Failed to get stacktrace." << std::endl;
166 return result.str();
167 }
168
169 for (uint32_t i = 0; i < curIndex_; ++i) {
170 ResolveFrameInfo(i, frames_[i]);
171 result << frames_[i].PrintFrame();
172 }
173
174 result << std::endl;
175 return result.str();
176 }
177
CollectUnwindFrames(std::vector<std::shared_ptr<DfxFrame>> & frames)178 void DfxUnwindLocal::CollectUnwindFrames(std::vector<std::shared_ptr<DfxFrame>>& frames)
179 {
180 if (curIndex_ == 0) {
181 return;
182 }
183
184 for (uint32_t i = 0; i <= curIndex_; ++i) {
185 ResolveFrameInfo(i, frames_[i]);
186 frames.emplace_back(std::make_shared<DfxFrame>(frames_[i]));
187 }
188 }
189
ResolveFrameInfo(size_t index,DfxFrame & frame)190 void DfxUnwindLocal::ResolveFrameInfo(size_t index, DfxFrame& frame)
191 {
192 if (cache_ == nullptr) {
193 return;
194 }
195
196 frame.SetFrameIndex(index);
197 uint64_t pc = frame.GetFramePc();
198 uint64_t funcOffset = frame.GetFrameFuncOffset();
199 std::string funcName = frame.GetFrameFuncName();
200 if (!cache_->GetNameAndOffsetByPc(as_, pc, funcName, funcOffset)) {
201 frame.SetFrameFuncName("");
202 frame.SetFrameFuncOffset(0);
203 } else {
204 frame.SetFramePc(pc);
205 frame.SetFrameFuncName(funcName);
206 frame.SetFrameFuncOffset(funcOffset);
207 }
208 }
209
ExecLocalDumpUnwindByWait()210 bool DfxUnwindLocal::ExecLocalDumpUnwindByWait()
211 {
212 bool ret = false;
213 if (!insideSignalHandler_) {
214 DfxLogError("%s :: must be send request first.", __func__);
215 return ret;
216 }
217 std::unique_lock<std::mutex> lck(localDumperMutex_);
218 ret = ExecLocalDumpUnwinding(&context_, 0);
219 localDumperCV_.notify_one();
220 return ret;
221 }
222
WaitLocalDumpRequest()223 bool DfxUnwindLocal::WaitLocalDumpRequest()
224 {
225 int left = 1000; // 1000 : 1000us
226 constexpr int pollTime = 10; // 10 : 10us
227 while (!insideSignalHandler_) {
228 int ret = usleep(pollTime);
229 if (ret == 0) {
230 left -= pollTime;
231 } else {
232 left -= ret;
233 }
234 if (left <= 0) {
235 return false;
236 }
237 }
238 return true;
239 }
240
ExecLocalDumpUnwind(size_t skipFramNum)241 bool DfxUnwindLocal::ExecLocalDumpUnwind(size_t skipFramNum)
242 {
243 unw_context_t context;
244 unw_getcontext(&context);
245 return ExecLocalDumpUnwinding(&context, skipFramNum);
246 }
247
ExecLocalDumpUnwinding(unw_context_t * ctx,size_t skipFramNum)248 bool DfxUnwindLocal::ExecLocalDumpUnwinding(unw_context_t *ctx, size_t skipFramNum)
249 {
250 unw_cursor_t cursor;
251 unw_init_local_with_as(as_, &cursor, ctx);
252
253 int ret = 0;
254 size_t index = 0;
255 curIndex_ = 0;
256 unw_word_t pc = 0;
257 unw_word_t sp = 0;
258 unw_word_t prevPc = 0;
259 char mapName[SYMBOL_BUF_SIZE] = {0};
260 do {
261 // skip 0 stack, as this is dump catcher. Caller don't need it.
262 if (index < skipFramNum) {
263 index++;
264 continue;
265 }
266
267 if (unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t*)(&(pc)))) {
268 DfxLogWarn("%s :: Failed to get current pc, stop.", __func__);
269 break;
270 }
271
272 if (unw_get_reg(&cursor, UNW_REG_SP, (unw_word_t*)(&(sp)))) {
273 DfxLogWarn("%s :: Failed to get current sp, stop.", __func__);
274 break;
275 }
276
277 curIndex_ = static_cast<uint32_t>(index - skipFramNum);
278 DfxLogDebug("%s :: curIndex_: %d", __func__, curIndex_);
279 if (curIndex_ > 1 && prevPc == pc) {
280 break;
281 }
282 prevPc = pc;
283
284 unw_word_t relPc = unw_get_rel_pc(&cursor);
285 unw_word_t sz = unw_get_previous_instr_sz(&cursor);
286 if ((curIndex_ > 0) && (relPc > sz)) {
287 relPc -= sz;
288 pc -= sz;
289 #if defined(__arm__)
290 unw_set_adjust_pc(&cursor, pc);
291 #endif
292 }
293
294 struct map_info* map = unw_get_map(&cursor);
295 errno_t err = EOK;
296 bool isValidFrame = true;
297 (void)memset_s(mapName, SYMBOL_BUF_SIZE, 0, SYMBOL_BUF_SIZE);
298 if ((map != NULL) && (strlen(map->path) < SYMBOL_BUF_SIZE - 1)) {
299 err = strcpy_s(mapName, SYMBOL_BUF_SIZE, map->path);
300 } else {
301 isValidFrame = false;
302 err = strcpy_s(mapName, SYMBOL_BUF_SIZE, "Unknown");
303 }
304 if (err != EOK) {
305 DfxLogError("%s :: strcpy_s failed.", __func__);
306 break;
307 }
308
309 if (curIndex_ < MIN_VALID_FRAME_COUNT || isValidFrame) {
310 auto& curFrame = frames_[curIndex_];
311 curFrame.SetFrameIndex((size_t)curIndex_);
312 curFrame.SetFramePc((uint64_t)pc);
313 curFrame.SetFrameSp((uint64_t)sp);
314 curFrame.SetFrameRelativePc((uint64_t)relPc);
315 curFrame.SetFrameMapName(std::string(mapName));
316 } else {
317 curIndex_--;
318 DfxLogError("%s :: unw_get_map failed.", __func__);
319 break;
320 }
321
322 index++;
323 } while ((unw_step(&cursor) > 0) && (index < BACK_STACK_MAX_STEPS));
324 return true;
325 }
326
LocalDumper(int sig,siginfo_t * si,void * context)327 void DfxUnwindLocal::LocalDumper(int sig, siginfo_t *si, void *context)
328 {
329 std::unique_lock<std::mutex> lck(localDumperMutex_);
330 insideSignalHandler_ = true;
331 #if defined(__arm__)
332 (void)memset_s(&context_, sizeof(context_), 0, sizeof(context_));
333 ucontext_t *uc = (ucontext_t *)context;
334 context_.regs[UNW_ARM_R0] = uc->uc_mcontext.arm_r0;
335 context_.regs[UNW_ARM_R1] = uc->uc_mcontext.arm_r1;
336 context_.regs[UNW_ARM_R2] = uc->uc_mcontext.arm_r2;
337 context_.regs[UNW_ARM_R3] = uc->uc_mcontext.arm_r3;
338 context_.regs[UNW_ARM_R4] = uc->uc_mcontext.arm_r4;
339 context_.regs[UNW_ARM_R5] = uc->uc_mcontext.arm_r5;
340 context_.regs[UNW_ARM_R6] = uc->uc_mcontext.arm_r6;
341 context_.regs[UNW_ARM_R7] = uc->uc_mcontext.arm_r7;
342 context_.regs[UNW_ARM_R8] = uc->uc_mcontext.arm_r8;
343 context_.regs[UNW_ARM_R9] = uc->uc_mcontext.arm_r9;
344 context_.regs[UNW_ARM_R10] = uc->uc_mcontext.arm_r10;
345 context_.regs[UNW_ARM_R11] = uc->uc_mcontext.arm_fp;
346 context_.regs[UNW_ARM_R12] = uc->uc_mcontext.arm_ip;
347 context_.regs[UNW_ARM_R13] = uc->uc_mcontext.arm_sp;
348 context_.regs[UNW_ARM_R14] = uc->uc_mcontext.arm_lr;
349 context_.regs[UNW_ARM_R15] = uc->uc_mcontext.arm_pc;
350 #else
351 // the ucontext.uc_mcontext.__reserved of libunwind is simplified with the system's own in aarch64
352 if (memcpy_s(&context_, sizeof(unw_context_t), context, sizeof(unw_context_t)) != 0) {
353 DfxLogToSocket("Failed to copy local unwind context.");
354 }
355 #endif
356
357 localDumperCV_.wait_for(lck, std::chrono::milliseconds(2000)); // 2000 : 2000ms
358 }
359
LocalDumpering(int sig,siginfo_t * si,void * context)360 void DfxUnwindLocal::LocalDumpering(int sig, siginfo_t *si, void *context)
361 {
362 DfxUnwindLocal::GetInstance().LocalDumper(sig, si, context);
363 }
364
InstallLocalDumper(int sig)365 void DfxUnwindLocal::InstallLocalDumper(int sig)
366 {
367 struct sigaction action;
368 memset_s(&action, sizeof(action), 0, sizeof(action));
369 memset_s(&oldSigaction_, sizeof(oldSigaction_), 0, sizeof(oldSigaction_));
370 sigfillset(&action.sa_mask);
371 action.sa_sigaction = DfxUnwindLocal::LocalDumpering;
372 action.sa_flags = SA_RESTART | SA_SIGINFO;
373
374 if (sigaction(sig, &action, &oldSigaction_) != EOK) {
375 DfxLogWarn("InstallLocalDumper :: Failed to register signal.");
376 }
377 }
378
UninstallLocalDumper(int sig)379 void DfxUnwindLocal::UninstallLocalDumper(int sig)
380 {
381 if (oldSigaction_.sa_sigaction == nullptr) {
382 signal(sig, SIG_DFL);
383 return;
384 }
385
386 if (sigaction(sig, &oldSigaction_, NULL) != EOK) {
387 DfxLogWarn("UninstallLocalDumper :: Failed to reset signal.");
388 signal(sig, SIG_DFL);
389 }
390 }
391 } // namespace HiviewDFX
392 } // namespace OHOS
393