• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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