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