• 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     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