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