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