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