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