1 /*
2 * Copyright (c) 2024 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 "thread_context.h"
17
18 #include <chrono>
19 #include <csignal>
20 #include <map>
21 #include <memory>
22 #include <mutex>
23 #include <securec.h>
24 #include <sigchain.h>
25 #include <unistd.h>
26
27 #include "dfx_define.h"
28 #include "dfx_log.h"
29 #ifndef is_ohos_lite
30 #include "file_ex.h"
31 #else
32 #include "file_util.h"
33 #endif
34 #include "fp_unwinder.h"
35 #include "string_printf.h"
36 #if defined(__aarch64__)
37 #include "unwind_arm64_define.h"
38 #endif
39
40 namespace OHOS {
41 namespace HiviewDFX {
42 namespace {
43 #undef LOG_DOMAIN
44 #undef LOG_TAG
45 #define LOG_DOMAIN 0xD002D11
46 #define LOG_TAG "DfxThreadContext"
47
48 std::mutex g_localMutex;
49 std::map<int32_t, std::shared_ptr<ThreadContext>> g_contextMap {};
50 constexpr std::chrono::seconds TIME_OUT = std::chrono::seconds(1);
51
CreateContext(std::shared_ptr<ThreadContext> & threadContext)52 void CreateContext(std::shared_ptr<ThreadContext>& threadContext)
53 {
54 #ifndef __aarch64__
55 std::unique_lock<std::mutex> lock(threadContext->mtx);
56 if (threadContext->ctx == nullptr) {
57 threadContext->ctx = new ucontext_t;
58 }
59 (void)memset_s(threadContext->ctx, sizeof(ucontext_t), 0, sizeof(ucontext_t));
60 #endif
61 }
62
63 #ifndef __aarch64__
ReleaseContext(std::shared_ptr<ThreadContext> threadContext)64 void ReleaseContext(std::shared_ptr<ThreadContext> threadContext)
65 {
66 std::unique_lock<std::mutex> lock(threadContext->mtx);
67 if (threadContext->ctx != nullptr) {
68 delete threadContext->ctx;
69 threadContext->ctx = nullptr;
70 }
71 }
72 #endif
73
PrintThreadStatus(int32_t tid)74 void PrintThreadStatus(int32_t tid)
75 {
76 std::string content;
77 std::string path = StringPrintf("/proc/%d/status", tid);
78 LoadStringFromFile(path, content);
79 DFXLOGI("%{public}s", content.c_str());
80 }
81
GetContextLocked(int32_t tid)82 std::shared_ptr<ThreadContext> GetContextLocked(int32_t tid)
83 {
84 auto it = g_contextMap.find(tid);
85 if (it == g_contextMap.end() || it->second == nullptr) {
86 auto threadContext = std::make_shared<ThreadContext>();
87 threadContext->tid = tid;
88 threadContext->frameSz = 0;
89 CreateContext(threadContext);
90 g_contextMap[tid] = threadContext;
91 return threadContext;
92 }
93
94 if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
95 it->second->tid = tid;
96 it->second->frameSz = 0;
97 CreateContext(it->second);
98 return it->second;
99 }
100 DFXLOGE("GetContextLocked nullptr, tid: %{public}d", tid);
101 return nullptr;
102 }
103
RemoveAllContextLocked()104 bool RemoveAllContextLocked()
105 {
106 auto it = g_contextMap.begin();
107 while (it != g_contextMap.end()) {
108 if (it->second == nullptr) {
109 it = g_contextMap.erase(it);
110 continue;
111 }
112 #ifndef __aarch64__
113 if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
114 ReleaseContext(it->second);
115 }
116 #endif
117 it++;
118 }
119 return true;
120 }
121
CopyContextAndWaitTimeoutMix(int sig,siginfo_t * si,void * context)122 NO_SANITIZE void CopyContextAndWaitTimeoutMix(int sig, siginfo_t *si, void *context)
123 {
124 if (si == nullptr || context == nullptr) {
125 return;
126 }
127 auto& instance = LocalThreadContextMix::GetInstance();
128 if (!instance.CheckStatusValidate(SyncStatus::WAIT_CTX, gettid())) {
129 return;
130 }
131 if ((gettid() != getpid()) && !instance.GetSelfStackRangeInSignal()) {
132 return;
133 }
134 instance.CopyRegister(context);
135 instance.CopyStackBuf();
136 }
137
CopyContextAndWaitTimeout(int sig,siginfo_t * si,void * context)138 NO_SANITIZE void CopyContextAndWaitTimeout(int sig, siginfo_t *si, void *context)
139 {
140 if (si == nullptr || si->si_value.sival_ptr == nullptr || context == nullptr) {
141 return;
142 }
143
144 DFXLOGU("tid(%{public}d) recv sig(%{public}d)", gettid(), sig);
145 auto ctxPtr = static_cast<ThreadContext *>(si->si_value.sival_ptr);
146 #if defined(__aarch64__)
147 uintptr_t fp = reinterpret_cast<ucontext_t*>(context)->uc_mcontext.regs[REG_FP];
148 uintptr_t pc = reinterpret_cast<ucontext_t*>(context)->uc_mcontext.pc;
149 ctxPtr->firstFrameSp = reinterpret_cast<ucontext_t*>(context)->uc_mcontext.sp;
150 ctxPtr->frameSz = FpUnwinder::GetPtr()->UnwindSafe(pc, fp, ctxPtr->pcs, DEFAULT_MAX_LOCAL_FRAME_NUM);
151 ctxPtr->cv.notify_all();
152 ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
153 return;
154 #else
155
156 std::unique_lock<std::mutex> lock(ctxPtr->mtx);
157 if (ctxPtr->ctx == nullptr) {
158 ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
159 return;
160 }
161 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
162 int tid = gettid();
163 if (memcpy_s(&ctxPtr->ctx->uc_mcontext, sizeof(ucontext->uc_mcontext),
164 &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext)) != 0) {
165 DFXLOGW("Failed to copy local ucontext with tid(%{public}d)", tid);
166 }
167
168 if (tid != getpid()) {
169 if (!GetSelfStackRange(ctxPtr->stackBottom, ctxPtr->stackTop)) {
170 DFXLOGW("Failed to get stack range with tid(%{public}d)", tid);
171 }
172 }
173
174 ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_READY);
175 ctxPtr->cv.notify_all();
176 ctxPtr->cv.wait_for(lock, TIME_OUT);
177 ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
178 #endif
179 }
180
DfxBacktraceLocalSignalHandler(int sig,siginfo_t * si,void * context)181 void DfxBacktraceLocalSignalHandler(int sig, siginfo_t *si, void *context)
182 {
183 if (si == nullptr) {
184 return;
185 }
186 if (si->si_code == DUMP_TYPE_LOCAL) {
187 CopyContextAndWaitTimeout(sig, si, context);
188 } else if (si->si_code == DUMP_TYPE_LOCAL_MIX) {
189 CopyContextAndWaitTimeoutMix(sig, si, context);
190 }
191 }
192
InitSignalHandler()193 void InitSignalHandler()
194 {
195 static std::once_flag flag;
196 std::call_once(flag, [&]() {
197 FpUnwinder::GetPtr();
198 struct sigaction action;
199 (void)memset_s(&action, sizeof(action), 0, sizeof(action));
200 sigemptyset(&action.sa_mask);
201 sigaddset(&action.sa_mask, SIGLOCAL_DUMP);
202 action.sa_flags = SA_RESTART | SA_SIGINFO;
203 action.sa_sigaction = DfxBacktraceLocalSignalHandler;
204 DFXLOGU("Install local signal handler: %{public}d", SIGLOCAL_DUMP);
205 sigaction(SIGLOCAL_DUMP, &action, nullptr);
206 });
207 }
208 }
209
GetInstance()210 LocalThreadContext& LocalThreadContext::GetInstance()
211 {
212 static LocalThreadContext instance;
213 return instance;
214 }
215
GetThreadContext(int32_t tid)216 NO_SANITIZE std::shared_ptr<ThreadContext> LocalThreadContext::GetThreadContext(int32_t tid)
217 {
218 std::unique_lock<std::mutex> lock(localMutex_);
219 auto it = g_contextMap.find(tid);
220 if (it != g_contextMap.end()) {
221 return it->second;
222 }
223 DFXLOGW("Failed to get context of tid(%{public}d)", tid);
224 return nullptr;
225 }
226
ReleaseThread(int32_t tid)227 void LocalThreadContext::ReleaseThread(int32_t tid)
228 {
229 std::unique_lock<std::mutex> lock(localMutex_);
230 auto it = g_contextMap.find(tid);
231 if (it == g_contextMap.end() || it->second == nullptr) {
232 return;
233 }
234 it->second->cv.notify_all();
235 }
236
CleanUp()237 void LocalThreadContext::CleanUp()
238 {
239 std::unique_lock<std::mutex> lock(localMutex_);
240 RemoveAllContextLocked();
241 }
242
CollectThreadContext(int32_t tid)243 std::shared_ptr<ThreadContext> LocalThreadContext::CollectThreadContext(int32_t tid)
244 {
245 std::unique_lock<std::mutex> lock(localMutex_);
246 auto threadContext = GetContextLocked(tid);
247 if (threadContext == nullptr) {
248 DFXLOGW("Failed to get context of tid(%{public}d), still using?", tid);
249 return nullptr;
250 }
251
252 InitSignalHandler();
253 if (!SignalRequestThread(tid, threadContext.get())) {
254 return nullptr;
255 }
256 if (threadContext->cv.wait_for(lock, TIME_OUT) == std::cv_status::timeout) {
257 DFXLOGE("wait_for timeout. tid = %{public}d", tid);
258 PrintThreadStatus(tid);
259 return nullptr;
260 }
261 return threadContext;
262 }
263
GetStackRange(int32_t tid,uintptr_t & stackBottom,uintptr_t & stackTop)264 bool LocalThreadContext::GetStackRange(int32_t tid, uintptr_t& stackBottom, uintptr_t& stackTop)
265 {
266 auto ctxPtr = LocalThreadContext::GetInstance().GetThreadContext(tid);
267 if (ctxPtr == nullptr) {
268 return false;
269 }
270 stackBottom = ctxPtr->stackBottom;
271 stackTop = ctxPtr->stackTop;
272 return true;
273 }
274
SignalRequestThread(int32_t tid,ThreadContext * threadContext)275 bool LocalThreadContext::SignalRequestThread(int32_t tid, ThreadContext* threadContext)
276 {
277 siginfo_t si {0};
278 si.si_signo = SIGLOCAL_DUMP;
279 si.si_errno = 0;
280 si.si_code = DUMP_TYPE_LOCAL;
281 si.si_value.sival_ptr = reinterpret_cast<void *>(threadContext);
282 if (syscall(SYS_rt_tgsigqueueinfo, getpid(), tid, si.si_signo, &si) != 0) {
283 DFXLOGW("Failed to send signal(%{public}d) to tid(%{public}d), errno(%{public}d).", si.si_signo, tid, errno);
284 threadContext->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
285 return false;
286 }
287 return true;
288 }
289
GetInstance()290 NO_SANITIZE LocalThreadContextMix& LocalThreadContextMix::GetInstance()
291 {
292 static LocalThreadContextMix instance;
293 return instance;
294 }
295
CollectThreadContext(int32_t tid)296 bool LocalThreadContextMix::CollectThreadContext(int32_t tid)
297 {
298 maps_ = DfxMaps::Create();
299 StartCollectThreadContext(tid);
300 InitSignalHandler();
301 if (!SignalRequestThread(tid)) {
302 return false;
303 }
304 {
305 std::unique_lock<std::mutex> lock(mtx_);
306 if (cv_.wait_for(lock, TIME_OUT) == std::cv_status::timeout) {
307 DFXLOGE("wait_for timeout. tid = %{public}d, status = %{public}d", tid, status_);
308 PrintThreadStatus(tid);
309 return false;
310 }
311 }
312 return CheckStatusValidate(SyncStatus::COPY_SUCCESS, tid);
313 }
314
SignalRequestThread(int32_t tid)315 bool LocalThreadContextMix::SignalRequestThread(int32_t tid)
316 {
317 siginfo_t si {0};
318 si.si_signo = SIGLOCAL_DUMP;
319 si.si_errno = 0;
320 si.si_code = DUMP_TYPE_LOCAL_MIX;
321 if (syscall(SYS_rt_tgsigqueueinfo, getpid(), tid, si.si_signo, &si) != 0) {
322 return false;
323 }
324 return true;
325 }
326
StartCollectThreadContext(int32_t tid)327 void LocalThreadContextMix::StartCollectThreadContext(int32_t tid)
328 {
329 std::unique_lock<std::mutex> lock(mtx_);
330 tid_ = tid;
331 status_ = SyncStatus::WAIT_CTX;
332 stackBuf_.resize(STACK_BUFFER_SIZE, 0);
333 }
334
ReleaseCollectThreadContext()335 void LocalThreadContextMix::ReleaseCollectThreadContext()
336 {
337 std::unique_lock<std::mutex> lock(mtx_);
338 pc_ = 0;
339 sp_ = 0;
340 fp_ = 0;
341 lr_ = 0;
342 stackBottom_ = 0;
343 stackTop_ = 0;
344 stackBuf_ = {};
345 tid_ = -1;
346 status_ = SyncStatus::INIT;
347 maps_ = nullptr;
348 }
349
CheckStatusValidate(int status,int32_t tid)350 NO_SANITIZE bool LocalThreadContextMix::CheckStatusValidate(int status, int32_t tid)
351 {
352 std::unique_lock<std::mutex> lock(mtx_);
353 return status_ == status && tid_ == tid;
354 }
355
GetSelfStackRangeInSignal()356 NO_SANITIZE bool LocalThreadContextMix::GetSelfStackRangeInSignal()
357 {
358 std::unique_lock<std::mutex> lock(mtx_);
359 return GetSelfStackRange(stackBottom_, stackTop_);
360 }
361
CopyRegister(void * context)362 NO_SANITIZE void LocalThreadContextMix::CopyRegister(void *context)
363 {
364 std::unique_lock<std::mutex> lock(mtx_);
365 #if defined(__arm__)
366 fp_ = static_cast<ucontext_t*>(context)->uc_mcontext.arm_fp;
367 lr_ = static_cast<ucontext_t*>(context)->uc_mcontext.arm_lr;
368 sp_ = static_cast<ucontext_t*>(context)->uc_mcontext.arm_sp;
369 pc_ = static_cast<ucontext_t*>(context)->uc_mcontext.arm_pc;
370 #elif defined(__aarch64__)
371 fp_ = static_cast<ucontext_t*>(context)->uc_mcontext.regs[RegsEnumArm64::REG_FP];
372 lr_ = static_cast<ucontext_t*>(context)->uc_mcontext.regs[RegsEnumArm64::REG_LR];
373 sp_ = static_cast<ucontext_t*>(context)->uc_mcontext.sp;
374 pc_ = static_cast<ucontext_t*>(context)->uc_mcontext.pc;
375 #endif
376 }
377
CopyStackBuf()378 NO_SANITIZE void LocalThreadContextMix::CopyStackBuf()
379 {
380 if (stackTop_ <= sp_) {
381 return;
382 }
383 uintptr_t curStackSz = stackTop_ - sp_;
384 size_t cpySz = std::min(static_cast<size_t>(curStackSz), static_cast<size_t>(STACK_BUFFER_SIZE));
385 std::unique_lock<std::mutex> lock(mtx_);
386 if (stackBuf_.size() >= cpySz) {
387 for (size_t i = 0; i < cpySz; i++) {
388 stackBuf_[i] = reinterpret_cast<uint8_t*>(sp_)[i];
389 }
390 status_ = SyncStatus::COPY_SUCCESS;
391 } else {
392 status_ = SyncStatus::COPY_FAILED;
393 }
394 cv_.notify_all();
395 }
396
SetRegister(std::shared_ptr<DfxRegs> regs)397 void LocalThreadContextMix::SetRegister(std::shared_ptr<DfxRegs> regs)
398 {
399 #if defined(__arm__) || defined(__aarch64__)
400 std::unique_lock<std::mutex> lock(mtx_);
401 regs->SetSp(sp_);
402 regs->SetPc(pc_);
403 regs->SetFp(fp_);
404 regs->SetReg(REG_LR, &(lr_));
405 #endif
406 }
407
SetStackRang(uintptr_t stackTop,uintptr_t stackBottom)408 void LocalThreadContextMix::SetStackRang(uintptr_t stackTop, uintptr_t stackBottom)
409 {
410 std::unique_lock<std::mutex> lock(mtx_);
411 stackTop_ = stackTop;
412 stackBottom_ = stackBottom;
413 }
414
GetMapByPc(uintptr_t pc,std::shared_ptr<DfxMap> & map) const415 int LocalThreadContextMix::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map) const
416 {
417 if (maps_ == nullptr) {
418 DFXLOGE("maps_ is nullptr.");
419 return -1;
420 }
421 return maps_->FindMapByAddr(pc, map) ? 0 : -1;
422 }
423
FindUnwindTable(uintptr_t pc,UnwindTableInfo & outTableInfo) const424 int LocalThreadContextMix::FindUnwindTable(uintptr_t pc, UnwindTableInfo& outTableInfo) const
425 {
426 std::shared_ptr<DfxMap> dfxMap;
427 if (maps_->FindMapByAddr(pc, dfxMap)) {
428 if (dfxMap == nullptr) {
429 return -1;
430 }
431 auto elf = dfxMap->GetElf(getpid());
432 if (elf != nullptr) {
433 return elf->FindUnwindTableInfo(pc, dfxMap, outTableInfo);
434 }
435 }
436 return -1;
437 }
438
AccessMem(uintptr_t addr,uintptr_t * val)439 int LocalThreadContextMix::AccessMem(uintptr_t addr, uintptr_t *val)
440 {
441 if (val == nullptr) {
442 return -1;
443 }
444 *val = 0;
445 if (addr < sp_ || addr + sizeof(uintptr_t) >= sp_ + STACK_BUFFER_SIZE) {
446 std::shared_ptr<DfxMap> map;
447 if (!(maps_->FindMapByAddr(addr, map)) || map == nullptr) {
448 return -1;
449 }
450 auto elf = map->GetElf(getpid());
451 if (elf != nullptr) {
452 uint64_t foff = addr - map->begin + map->offset - elf->GetBaseOffset();
453 if (elf->Read(foff, val, sizeof(uintptr_t))) {
454 return 0;
455 }
456 }
457 return -1;
458 }
459 size_t stackOffset = addr - sp_;
460 *val = *(reinterpret_cast<uintptr_t *>(&stackBuf_[stackOffset]));
461 return 0;
462 }
463
GetMaps() const464 std::shared_ptr<DfxMaps> LocalThreadContextMix::GetMaps() const
465 {
466 return maps_;
467 }
468 } // namespace HiviewDFX
469 } // namespace OHOS
470