• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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 #include "backtrace_local_context.h"
17 
18 #include <cinttypes>
19 #include <condition_variable>
20 #include <csignal>
21 #include <map>
22 #include <mutex>
23 
24 #include <fcntl.h>
25 #include <hilog/log.h>
26 #include <securec.h>
27 #include <sigchain.h>
28 #include <sys/syscall.h>
29 #include <unistd.h>
30 
31 #include "dfx_log.h"
32 #include "dfx_define.h"
33 #include "libunwind.h"
34 
35 namespace OHOS {
36 namespace HiviewDFX {
37 namespace {
38 #undef LOG_DOMAIN
39 #undef LOG_TAG
40 #define LOG_DOMAIN 0xD002D11
41 #define LOG_TAG "DfxBacktraceLocal"
42 static struct sigaction g_sigaction;
43 static std::mutex g_localMutex;
44 static std::map<int32_t, std::shared_ptr<ThreadContext>> g_contextMap;
45 static std::chrono::seconds g_timeOut = std::chrono::seconds(1);
CreateContext(int32_t tid)46 static std::shared_ptr<ThreadContext> CreateContext(int32_t tid)
47 {
48     auto threadContext = std::make_shared<ThreadContext>();
49     threadContext->tid = tid;
50     std::unique_lock<std::mutex> mlock(threadContext->lock);
51     threadContext->frameSz = 0;
52     threadContext->ctx = new unw_context_t;
53     (void)memset_s(threadContext->ctx, sizeof(unw_context_t), 0, sizeof(unw_context_t));
54     return threadContext;
55 }
56 
GetContextLocked(int32_t tid)57 static std::shared_ptr<ThreadContext> GetContextLocked(int32_t tid)
58 {
59     auto it = g_contextMap.find(tid);
60     if (it == g_contextMap.end() || it->second == nullptr) {
61         auto threadContext = CreateContext(tid);
62         g_contextMap[tid] = threadContext;
63         return threadContext;
64     }
65 
66     if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
67         it->second->tid = tid;
68         it->second->frameSz = 0;
69         std::unique_lock<std::mutex> mlock(it->second->lock);
70         if (it->second->ctx == nullptr) {
71             it->second->ctx = new unw_context_t;
72         }
73         (void)memset_s(it->second->ctx, sizeof(unw_context_t), 0, sizeof(unw_context_t));
74         return it->second;
75     }
76 
77     return nullptr;
78 }
79 
ReleaseUnwindContext(std::shared_ptr<ThreadContext> context)80 static void ReleaseUnwindContext(std::shared_ptr<ThreadContext> context)
81 {
82     std::unique_lock<std::mutex> mlock(context->lock);
83     if (context->ctx != nullptr) {
84         delete context->ctx;
85         context->ctx = nullptr;
86     }
87 }
88 
RemoveContextLocked(int32_t tid)89 static bool RemoveContextLocked(int32_t tid)
90 {
91     auto it = g_contextMap.find(tid);
92     if (it == g_contextMap.end()) {
93         DFXLOG_WARN("Context of %d is already removed.", tid);
94         return true;
95     }
96     if (it->second == nullptr) {
97         g_contextMap.erase(it);
98         return true;
99     }
100 
101     // only release unw_context_t object
102     if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
103         ReleaseUnwindContext(it->second);
104         return true;
105     }
106 
107     DFXLOG_WARN("Failed to release unwind context of %d, still using?.", tid);
108     return false;
109 }
110 }
111 
GetInstance()112 BacktraceLocalContext& BacktraceLocalContext::GetInstance()
113 {
114     static BacktraceLocalContext instance;
115     return instance;
116 }
117 
CollectThreadContext(int32_t tid)118 std::shared_ptr<ThreadContext> BacktraceLocalContext::CollectThreadContext(int32_t tid)
119 {
120     std::unique_lock<std::mutex> lock(g_localMutex);
121     auto context = GetContextLocked(tid);
122     if (context == nullptr) {
123         DFXLOG_WARN("Failed to get context of %d, still using?", tid);
124         return nullptr;
125     }
126 
127     if (!Init()) {
128         RemoveContextLocked(tid);
129         DFXLOG_WARN("Failed to install local dump signal handler.");
130         return nullptr;
131     }
132 
133     if (!SignalRequestThread(tid, context.get())) {
134         return nullptr;
135     }
136     context->cv.wait_for(lock, g_timeOut);
137     return context;
138 }
139 
GetThreadContext(int32_t tid)140 std::shared_ptr<ThreadContext> BacktraceLocalContext::GetThreadContext(int32_t tid)
141 {
142     std::unique_lock<std::mutex> lock(g_localMutex);
143     auto it = g_contextMap.find(tid);
144     if (it != g_contextMap.end()) {
145         return it->second;
146     }
147     return nullptr;
148 }
149 
ReleaseThread(int32_t tid)150 void BacktraceLocalContext::ReleaseThread(int32_t tid)
151 {
152     std::unique_lock<std::mutex> lock(g_localMutex);
153     auto it = g_contextMap.find(tid);
154     if (it == g_contextMap.end() || it->second == nullptr) {
155         return;
156     }
157 
158     it->second->cv.notify_all();
159 }
160 
CleanUp()161 void BacktraceLocalContext::CleanUp()
162 {
163     std::unique_lock<std::mutex> lock(g_localMutex);
164     auto it = g_contextMap.begin();
165     while (it != g_contextMap.end()) {
166         if (it->second == nullptr) {
167             it = g_contextMap.erase(it);
168             continue;
169         }
170         if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
171             ReleaseUnwindContext(it->second);
172         }
173         it++;
174     }
175 }
176 
CopyContextAndWaitTimeout(int sig,siginfo_t * si,void * context)177 bool BacktraceLocalContext::CopyContextAndWaitTimeout(int sig, siginfo_t *si, void *context)
178 {
179     if (si->si_code != DUMP_TYPE_LOCAL) {
180         return false;
181     }
182 
183     auto ctxPtr = BacktraceLocalContext::GetInstance().GetThreadContext(syscall(SYS_gettid));
184     if (ctxPtr == nullptr || context == nullptr) {
185         return true;
186     }
187 
188 #if defined(__aarch64__)
189     uintptr_t fp = static_cast<ucontext_t*>(context)->uc_mcontext.regs[UNW_AARCH64_X29];
190     ctxPtr->pcs[0] = static_cast<ucontext_t*>(context)->uc_mcontext.pc;
191     int index = 1;
192     for (; index < DEFAULT_MAX_LOCAL_FRAME_NUM; index++) {
193         uintptr_t prevFp = fp;
194         if (!BacktraceLocalContext::GetInstance().ReadUintptrSafe(prevFp, fp)) {
195             break;
196         }
197 
198         if (!BacktraceLocalContext::GetInstance().ReadUintptrSafe(fp + sizeof(uintptr_t), ctxPtr->pcs[index])) {
199             break;
200         }
201 
202         if (fp == prevFp || fp == 0 || ctxPtr->pcs[index] == 0) {
203             break;
204         }
205     }
206     ctxPtr->frameSz = index;
207     ctxPtr->cv.notify_all();
208     ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
209     return true;
210 #else
211     std::unique_lock<std::mutex> lock(ctxPtr->lock);
212     if (ctxPtr->ctx != nullptr) {
213 #if defined(__arm__)
214         ucontext_t *uc = static_cast<ucontext_t *>(context);
215         ctxPtr->ctx->regs[UNW_ARM_R0] = uc->uc_mcontext.arm_r0;
216         ctxPtr->ctx->regs[UNW_ARM_R1] = uc->uc_mcontext.arm_r1;
217         ctxPtr->ctx->regs[UNW_ARM_R2] = uc->uc_mcontext.arm_r2;
218         ctxPtr->ctx->regs[UNW_ARM_R3] = uc->uc_mcontext.arm_r3;
219         ctxPtr->ctx->regs[UNW_ARM_R4] = uc->uc_mcontext.arm_r4;
220         ctxPtr->ctx->regs[UNW_ARM_R5] = uc->uc_mcontext.arm_r5;
221         ctxPtr->ctx->regs[UNW_ARM_R6] = uc->uc_mcontext.arm_r6;
222         ctxPtr->ctx->regs[UNW_ARM_R7] = uc->uc_mcontext.arm_r7;
223         ctxPtr->ctx->regs[UNW_ARM_R8] = uc->uc_mcontext.arm_r8;
224         ctxPtr->ctx->regs[UNW_ARM_R9] = uc->uc_mcontext.arm_r9;
225         ctxPtr->ctx->regs[UNW_ARM_R10] = uc->uc_mcontext.arm_r10;
226         ctxPtr->ctx->regs[UNW_ARM_R11] = uc->uc_mcontext.arm_fp;
227         ctxPtr->ctx->regs[UNW_ARM_R12] = uc->uc_mcontext.arm_ip;
228         ctxPtr->ctx->regs[UNW_ARM_R13] = uc->uc_mcontext.arm_sp;
229         ctxPtr->ctx->regs[UNW_ARM_R14] = uc->uc_mcontext.arm_lr;
230         ctxPtr->ctx->regs[UNW_ARM_R15] = uc->uc_mcontext.arm_pc;
231 #else
232         // the ucontext.uc_mcontext.__reserved of libunwind is simplified with the system's own in aarch64
233         if (memcpy_s(ctxPtr->ctx, sizeof(unw_context_t), context, sizeof(unw_context_t)) != 0) {
234             DFXLOG_WARN("Failed to copy local unwind context.");
235         }
236 #endif
237     } else {
238         ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
239         return true;
240     }
241     ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_READY);
242     ctxPtr->cv.notify_all();
243     ctxPtr->cv.wait_for(lock, g_timeOut);
244     ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
245     return true;
246 #endif
247 }
248 
SignalRequestThread(int32_t tid,ThreadContext * ctx)249 bool BacktraceLocalContext::SignalRequestThread(int32_t tid, ThreadContext* ctx)
250 {
251     siginfo_t si {0};
252     si.si_signo = SIGDUMP;
253     si.si_errno = 0;
254     si.si_code = DUMP_TYPE_LOCAL;
255     if (syscall(SYS_rt_tgsigqueueinfo, getpid(), tid, si.si_signo, &si) != 0) {
256         DFXLOG_WARN("Failed to queue signal(%d) to %d, errno(%d).", si.si_signo, tid, errno);
257         ctx->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
258         return false;
259     }
260     return true;
261 }
262 
Init()263 bool BacktraceLocalContext::Init()
264 {
265     static std::once_flag flag;
266     std::call_once(flag, [&]() {
267         if (pipe2(pipe2_, O_CLOEXEC | O_NONBLOCK) != 0) {
268             DFXLOG_WARN("Failed to create pipe, errno(%d).", errno);
269             init_ = false;
270         } else {
271             struct signal_chain_action sigchain = {
272                 .sca_sigaction = BacktraceLocalContext::CopyContextAndWaitTimeout,
273                 .sca_mask = {},
274                 .sca_flags = 0,
275             };
276             add_special_signal_handler(SIGDUMP, &sigchain);
277             init_ = true;
278         }
279     });
280 
281     return init_;
282 }
283 
ReadUintptrSafe(uintptr_t addr,uintptr_t & value)284 bool BacktraceLocalContext::ReadUintptrSafe(uintptr_t addr, uintptr_t& value)
285 {
286     if (!init_) {
287         return false;
288     }
289 
290     if (OHOS_TEMP_FAILURE_RETRY(syscall(SYS_write, pipe2_[PIPE_WRITE], addr, sizeof(uintptr_t))) == -1) {
291         return false;
292     }
293 
294     uintptr_t dummy;
295     value = *reinterpret_cast<uintptr_t *>(addr);
296     return OHOS_TEMP_FAILURE_RETRY(syscall(SYS_read, pipe2_[PIPE_READ], &dummy, sizeof(uintptr_t))) ==
297         sizeof(uintptr_t);
298 }
299 } // namespace HiviewDFX
300 } // namespace OHOS
301