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