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