• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sys/syscall.h>
25 #include <securec.h>
26 #include <unistd.h>
27 #include "dfx_log.h"
28 #include "dfx_define.h"
29 #include "libunwind.h"
30 
31 namespace OHOS {
32 namespace HiviewDFX {
33 namespace {
34 #undef LOG_DOMAIN
35 #undef LOG_TAG
36 #define LOG_DOMAIN 0xD002D11
37 #define LOG_TAG "DfxBacktraceLocal"
38 static struct sigaction g_sigaction;
39 static std::mutex g_localMutex;
40 static std::map<int32_t, std::shared_ptr<ThreadContext>> g_contextMap;
41 static std::chrono::seconds g_timeOut = std::chrono::seconds(2); // 2 : 2 seconds
CreateContext(int32_t tid)42 static std::shared_ptr<ThreadContext> CreateContext(int32_t tid)
43 {
44     auto threadContext = std::make_shared<ThreadContext>();
45     threadContext->tid = tid;
46     std::unique_lock<std::mutex> mlock(threadContext->lock);
47     threadContext->ctx = new unw_context_t;
48     (void)memset_s(threadContext->ctx, sizeof(unw_context_t), 0, sizeof(unw_context_t));
49     return threadContext;
50 }
51 
GetContextLocked(int32_t tid)52 static std::shared_ptr<ThreadContext> GetContextLocked(int32_t tid)
53 {
54     auto it = g_contextMap.find(tid);
55     if (it == g_contextMap.end() || it->second == nullptr) {
56         auto threadContext = CreateContext(tid);
57         g_contextMap[tid] = threadContext;
58         return threadContext;
59     }
60 
61     if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
62         it->second->tid = tid;
63         std::unique_lock<std::mutex> mlock(it->second->lock);
64         if (it->second->ctx == nullptr) {
65             it->second->ctx = new unw_context_t;
66         }
67         (void)memset_s(it->second->ctx, sizeof(unw_context_t), 0, sizeof(unw_context_t));
68         return it->second;
69     }
70 
71     return nullptr;
72 }
73 
ReleaseUnwindContext(std::shared_ptr<ThreadContext> context)74 static void ReleaseUnwindContext(std::shared_ptr<ThreadContext> context)
75 {
76     std::unique_lock<std::mutex> mlock(context->lock);
77     if (context->ctx != nullptr) {
78         delete context->ctx;
79         context->ctx = nullptr;
80     }
81 }
82 
RemoveContextLocked(int32_t tid)83 static bool RemoveContextLocked(int32_t tid)
84 {
85     auto it = g_contextMap.find(tid);
86     if (it == g_contextMap.end()) {
87         DFXLOG_WARN("Context of %d is already removed.", tid);
88         return true;
89     }
90     if (it->second == nullptr) {
91         g_contextMap.erase(it);
92         return true;
93     }
94 
95     // only release unw_context_t object
96     if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
97         ReleaseUnwindContext(it->second);
98         return true;
99     }
100 
101     DFXLOG_WARN("Failed to release unwind context of %d, still using?.", tid);
102     return false;
103 }
104 }
105 
GetInstance()106 BacktraceLocalContext& BacktraceLocalContext::GetInstance()
107 {
108     static BacktraceLocalContext instance;
109     return instance;
110 }
111 
GetThreadContext(int32_t tid)112 std::shared_ptr<ThreadContext> BacktraceLocalContext::GetThreadContext(int32_t tid)
113 {
114     std::unique_lock<std::mutex> lock(g_localMutex);
115     auto context = GetContextLocked(tid);
116     if (context == nullptr) {
117         DFXLOG_WARN("Failed to get context of %d, still using?", tid);
118         return nullptr;
119     }
120 
121     if (!InstallSigHandler()) {
122         RemoveContextLocked(tid);
123         return nullptr;
124     }
125 
126     if (!SignalRequestThread(tid, context.get())) {
127         UninstallSigHandler();
128         return nullptr;
129     }
130     UninstallSigHandler();
131     return context;
132 }
133 
ReleaseThread(int32_t tid)134 void BacktraceLocalContext::ReleaseThread(int32_t tid)
135 {
136     std::unique_lock<std::mutex> lock(g_localMutex);
137     auto it = g_contextMap.find(tid);
138     if (it == g_contextMap.end() || it->second == nullptr) {
139         return;
140     }
141 
142     it->second->cv.notify_one();
143 }
144 
CleanUp()145 void BacktraceLocalContext::CleanUp()
146 {
147     std::unique_lock<std::mutex> lock(g_localMutex);
148     auto it = g_contextMap.begin();
149     while (it != g_contextMap.end()) {
150         if (it->second == nullptr) {
151             it = g_contextMap.erase(it);
152             continue;
153         }
154         if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
155             ReleaseUnwindContext(it->second);
156         }
157         it++;
158     }
159 }
160 
CopyContextAndWaitTimeout(int sig,siginfo_t * si,void * context)161 void BacktraceLocalContext::CopyContextAndWaitTimeout(int sig, siginfo_t *si, void *context)
162 {
163     auto ctxPtr = static_cast<ThreadContext*>(si->si_value.sival_ptr);
164     if (ctxPtr == nullptr) {
165         // should never happen
166         return;
167     }
168     std::unique_lock<std::mutex> lock(ctxPtr->lock);
169     if (ctxPtr->ctx != nullptr) {
170 #if defined(__arm__)
171         ucontext_t *uc = static_cast<ucontext_t *>(context);
172         ctxPtr->ctx->regs[UNW_ARM_R0] = uc->uc_mcontext.arm_r0;
173         ctxPtr->ctx->regs[UNW_ARM_R1] = uc->uc_mcontext.arm_r1;
174         ctxPtr->ctx->regs[UNW_ARM_R2] = uc->uc_mcontext.arm_r2;
175         ctxPtr->ctx->regs[UNW_ARM_R3] = uc->uc_mcontext.arm_r3;
176         ctxPtr->ctx->regs[UNW_ARM_R4] = uc->uc_mcontext.arm_r4;
177         ctxPtr->ctx->regs[UNW_ARM_R5] = uc->uc_mcontext.arm_r5;
178         ctxPtr->ctx->regs[UNW_ARM_R6] = uc->uc_mcontext.arm_r6;
179         ctxPtr->ctx->regs[UNW_ARM_R7] = uc->uc_mcontext.arm_r7;
180         ctxPtr->ctx->regs[UNW_ARM_R8] = uc->uc_mcontext.arm_r8;
181         ctxPtr->ctx->regs[UNW_ARM_R9] = uc->uc_mcontext.arm_r9;
182         ctxPtr->ctx->regs[UNW_ARM_R10] = uc->uc_mcontext.arm_r10;
183         ctxPtr->ctx->regs[UNW_ARM_R11] = uc->uc_mcontext.arm_fp;
184         ctxPtr->ctx->regs[UNW_ARM_R12] = uc->uc_mcontext.arm_ip;
185         ctxPtr->ctx->regs[UNW_ARM_R13] = uc->uc_mcontext.arm_sp;
186         ctxPtr->ctx->regs[UNW_ARM_R14] = uc->uc_mcontext.arm_lr;
187         ctxPtr->ctx->regs[UNW_ARM_R15] = uc->uc_mcontext.arm_pc;
188 #else
189         // the ucontext.uc_mcontext.__reserved of libunwind is simplified with the system's own in aarch64
190         if (memcpy_s(ctxPtr->ctx, sizeof(unw_context_t), context, sizeof(unw_context_t)) != 0) {
191             DFXLOG_WARN("Failed to copy local unwind context.");
192         }
193 #endif
194     } else {
195         ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
196         return;
197     }
198     ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_READY);
199     ctxPtr->cv.wait_for(lock, g_timeOut);
200     ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
201 }
202 
InstallSigHandler()203 bool BacktraceLocalContext::InstallSigHandler()
204 {
205     struct sigaction action;
206     (void)memset_s(&action, sizeof(action), 0, sizeof(action));
207     (void)memset_s(&g_sigaction, sizeof(g_sigaction), 0, sizeof(g_sigaction));
208     sigfillset(&action.sa_mask);
209     // do not block crash signals
210     sigdelset(&action.sa_mask, SIGABRT);
211     sigdelset(&action.sa_mask, SIGBUS);
212     sigdelset(&action.sa_mask, SIGILL);
213     sigdelset(&action.sa_mask, SIGSEGV);
214     action.sa_sigaction = BacktraceLocalContext::CopyContextAndWaitTimeout;
215     action.sa_flags = SA_RESTART | SA_SIGINFO;
216     if (sigaction(SIGLOCAL_DUMP, &action, &g_sigaction) != EOK) {
217         DFXLOG_WARN("Failed to install SigHandler for local backtrace(%d).", errno);
218         return false;
219     }
220     return true;
221 }
222 
UninstallSigHandler()223 void BacktraceLocalContext::UninstallSigHandler()
224 {
225     if (g_sigaction.sa_sigaction == nullptr) {
226         signal(SIGLOCAL_DUMP, SIG_DFL);
227         return;
228     }
229 
230     if (sigaction(SIGLOCAL_DUMP, &g_sigaction, nullptr) != EOK) {
231         DFXLOG_WARN("UninstallSigHandler :: Failed to reset signal(%d).", errno);
232         signal(SIGLOCAL_DUMP, SIG_DFL);
233     }
234 }
235 
SignalRequestThread(int32_t tid,ThreadContext * ctx)236 bool BacktraceLocalContext::SignalRequestThread(int32_t tid, ThreadContext* ctx)
237 {
238     siginfo_t si {0};
239     si.si_signo = SIGLOCAL_DUMP;
240     si.si_value.sival_ptr = ctx; // pass context pointer by sival_ptr
241     si.si_errno = 0;
242     si.si_code = -SIGLOCAL_DUMP;
243     if (syscall(SYS_rt_tgsigqueueinfo, getpid(), tid, si.si_signo, &si) != 0) {
244         DFXLOG_WARN("Failed to queue signal(%d) to %d, errno(%d).", si.si_signo, tid, errno);
245         ctx->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
246         return false;
247     }
248 
249     int left = 10000; // 10000 : max wait 10ms for single thread
250     constexpr int pollTime = 10; // 10 : 10us
251     while (ctx->tid.load() != ThreadContextStatus::CONTEXT_READY) {
252         int ret = usleep(pollTime);
253         if (ret == 0) {
254             left -= pollTime;
255         } else {
256             left -= ret;
257         }
258 
259         if (left <= 0 &&
260             ctx->tid.compare_exchange_strong(tid, static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED))) {
261             DFXLOG_WARN("Failed to wait for %d to write context.", tid);
262             return false;
263         }
264     }
265     return true;
266 }
267 } // namespace HiviewDFX
268 } // namespace OHOS
269