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 "fp_unwinder.h"
30 #if defined(__aarch64__)
31 #include "unwind_arm64_define.h"
32 #endif
33
34 namespace OHOS {
35 namespace HiviewDFX {
36 namespace {
37 #undef LOG_DOMAIN
38 #undef LOG_TAG
39 #define LOG_DOMAIN 0xD002D11
40 #define LOG_TAG "DfxThreadContext"
41
42 std::mutex g_localMutex;
43 std::map<int32_t, std::shared_ptr<ThreadContext>> g_contextMap {};
44 const std::chrono::seconds g_timeOut = std::chrono::seconds(1);
45
CreateContext(std::shared_ptr<ThreadContext> & threadContext)46 void CreateContext(std::shared_ptr<ThreadContext>& threadContext)
47 {
48 #ifndef __aarch64__
49 std::unique_lock<std::mutex> lock(threadContext->mtx);
50 if (threadContext->ctx == nullptr) {
51 threadContext->ctx = new ucontext_t;
52 }
53 (void)memset_s(threadContext->ctx, sizeof(ucontext_t), 0, sizeof(ucontext_t));
54 #endif
55 }
56
ReleaseContext(std::shared_ptr<ThreadContext> threadContext)57 void ReleaseContext(std::shared_ptr<ThreadContext> threadContext)
58 {
59 #ifndef __aarch64__
60 std::unique_lock<std::mutex> lock(threadContext->mtx);
61 if (threadContext->ctx != nullptr) {
62 delete threadContext->ctx;
63 threadContext->ctx = nullptr;
64 }
65 #endif
66 }
67
GetContextLocked(int32_t tid)68 std::shared_ptr<ThreadContext> GetContextLocked(int32_t tid)
69 {
70 auto it = g_contextMap.find(tid);
71 if (it == g_contextMap.end() || it->second == nullptr) {
72 auto threadContext = std::make_shared<ThreadContext>();
73 threadContext->tid = tid;
74 threadContext->frameSz = 0;
75 CreateContext(threadContext);
76 g_contextMap[tid] = threadContext;
77 return threadContext;
78 }
79
80 if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
81 it->second->tid = tid;
82 it->second->frameSz = 0;
83 CreateContext(it->second);
84 return it->second;
85 }
86 LOGE("GetContextLocked nullptr, tid: %d", tid);
87 return nullptr;
88 }
89
RemoveContextLocked(int32_t tid)90 AT_UNUSED bool RemoveContextLocked(int32_t tid)
91 {
92 auto it = g_contextMap.find(tid);
93 if (it == g_contextMap.end()) {
94 LOGW("Context of tid(%d) is already removed.", tid);
95 return true;
96 }
97 if (it->second == nullptr) {
98 g_contextMap.erase(it);
99 return true;
100 }
101
102 // only release ucontext_t object
103 if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
104 ReleaseContext(it->second);
105 return true;
106 }
107
108 LOGW("Failed to release context of tid(%d), still using?", tid);
109 return false;
110 }
111
RemoveAllContextLocked()112 bool RemoveAllContextLocked()
113 {
114 auto it = g_contextMap.begin();
115 while (it != g_contextMap.end()) {
116 if (it->second == nullptr) {
117 it = g_contextMap.erase(it);
118 continue;
119 }
120 #ifndef __aarch64__
121 if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
122 ReleaseContext(it->second);
123 }
124 #endif
125 it++;
126 }
127 return true;
128 }
129 }
130
GetInstance()131 LocalThreadContext& LocalThreadContext::GetInstance()
132 {
133 static LocalThreadContext instance;
134 return instance;
135 }
136
GetThreadContext(int32_t tid)137 NO_SANITIZE std::shared_ptr<ThreadContext> LocalThreadContext::GetThreadContext(int32_t tid)
138 {
139 std::unique_lock<std::mutex> lock(localMutex_);
140 auto it = g_contextMap.find(tid);
141 if (it != g_contextMap.end()) {
142 return it->second;
143 }
144 LOGW("Failed to get context of tid(%d)", tid);
145 return nullptr;
146 }
147
ReleaseThread(int32_t tid)148 void LocalThreadContext::ReleaseThread(int32_t tid)
149 {
150 std::unique_lock<std::mutex> lock(localMutex_);
151 auto it = g_contextMap.find(tid);
152 if (it == g_contextMap.end() || it->second == nullptr) {
153 return;
154 }
155 it->second->cv.notify_all();
156 }
157
CleanUp()158 void LocalThreadContext::CleanUp()
159 {
160 std::unique_lock<std::mutex> lock(localMutex_);
161 RemoveAllContextLocked();
162 }
163
CollectThreadContext(int32_t tid)164 std::shared_ptr<ThreadContext> LocalThreadContext::CollectThreadContext(int32_t tid)
165 {
166 std::unique_lock<std::mutex> lock(localMutex_);
167 auto threadContext = GetContextLocked(tid);
168 if (threadContext == nullptr) {
169 LOGW("Failed to get context of tid(%d), still using?", tid);
170 return nullptr;
171 }
172
173 InitSignalHandler();
174 if (!SignalRequestThread(tid, threadContext.get())) {
175 return nullptr;
176 }
177 if (threadContext->cv.wait_for(lock, g_timeOut) == std::cv_status::timeout) {
178 LOGE("wait_for timeout. tid = %d", tid);
179 return nullptr;
180 }
181 return threadContext;
182 }
183
CopyContextAndWaitTimeout(int sig,siginfo_t * si,void * context)184 NO_SANITIZE void LocalThreadContext::CopyContextAndWaitTimeout(int sig, siginfo_t *si, void *context)
185 {
186 if (si == nullptr || si->si_code != DUMP_TYPE_LOCAL || si->si_value.sival_ptr == nullptr || context == nullptr) {
187 return;
188 }
189
190 LOGU("tid(%d) recv sig(%d)", gettid(), sig);
191 auto ctxPtr = static_cast<ThreadContext *>(si->si_value.sival_ptr);
192 #if defined(__aarch64__)
193 uintptr_t fp = reinterpret_cast<ucontext_t*>(context)->uc_mcontext.regs[REG_FP];
194 uintptr_t pc = reinterpret_cast<ucontext_t*>(context)->uc_mcontext.pc;
195 ctxPtr->firstFrameSp = reinterpret_cast<ucontext_t*>(context)->uc_mcontext.sp;
196 ctxPtr->frameSz = FpUnwinder::GetPtr()->UnwindSafe(pc, fp, ctxPtr->pcs, DEFAULT_MAX_LOCAL_FRAME_NUM);
197 ctxPtr->cv.notify_all();
198 ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
199 return;
200 #else
201
202 std::unique_lock<std::mutex> lock(ctxPtr->mtx);
203 if (ctxPtr->ctx == nullptr) {
204 ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
205 return;
206 }
207 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
208 int tid = gettid();
209 if (memcpy_s(&ctxPtr->ctx->uc_mcontext, sizeof(ucontext->uc_mcontext),
210 &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext)) != 0) {
211 LOGW("Failed to copy local ucontext with tid(%d)", tid);
212 }
213
214 if (tid != getpid()) {
215 if (!GetSelfStackRange(ctxPtr->stackBottom, ctxPtr->stackTop)) {
216 LOGW("Failed to get stack range with tid(%d)", tid);
217 }
218 }
219
220 ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_READY);
221 ctxPtr->cv.notify_all();
222 ctxPtr->cv.wait_for(lock, g_timeOut);
223 ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
224 #endif
225 }
226
GetStackRange(int32_t tid,uintptr_t & stackBottom,uintptr_t & stackTop)227 bool LocalThreadContext::GetStackRange(int32_t tid, uintptr_t& stackBottom, uintptr_t& stackTop)
228 {
229 auto ctxPtr = LocalThreadContext::GetInstance().GetThreadContext(tid);
230 if (ctxPtr == nullptr) {
231 return false;
232 }
233 stackBottom = ctxPtr->stackBottom;
234 stackTop = ctxPtr->stackTop;
235 return true;
236 }
237
InitSignalHandler()238 void LocalThreadContext::InitSignalHandler()
239 {
240 static std::once_flag flag;
241 std::call_once(flag, [&]() {
242 FpUnwinder::GetPtr();
243 struct sigaction action;
244 (void)memset_s(&action, sizeof(action), 0, sizeof(action));
245 sigemptyset(&action.sa_mask);
246 sigaddset(&action.sa_mask, SIGLOCAL_DUMP);
247 action.sa_flags = SA_RESTART | SA_SIGINFO;
248 action.sa_sigaction = LocalThreadContext::CopyContextAndWaitTimeout;
249 LOGU("Install local signal handler: %d", SIGLOCAL_DUMP);
250 sigaction(SIGLOCAL_DUMP, &action, nullptr);
251 });
252 }
253
SignalRequestThread(int32_t tid,ThreadContext * threadContext)254 bool LocalThreadContext::SignalRequestThread(int32_t tid, ThreadContext* threadContext)
255 {
256 siginfo_t si {0};
257 si.si_signo = SIGLOCAL_DUMP;
258 si.si_errno = 0;
259 si.si_code = DUMP_TYPE_LOCAL;
260 si.si_value.sival_ptr = reinterpret_cast<void *>(threadContext);
261 #if defined(is_ohos) && is_ohos
262 if (syscall(SYS_rt_tgsigqueueinfo, getprocpid(), tid, si.si_signo, &si) != 0) {
263 #else
264 if (syscall(SYS_rt_tgsigqueueinfo, getpid(), tid, si.si_signo, &si) != 0) {
265 #endif
266 LOGW("Failed to send signal(%d) to tid(%d), errno(%d).", si.si_signo, tid, errno);
267 threadContext->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
268 return false;
269 }
270 return true;
271 }
272 } // namespace HiviewDFX
273 } // namespace OHOS
274