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     DFXLOGE("GetContextLocked nullptr, tid: %{public}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         DFXLOGW("Context of tid(%{public}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     DFXLOGW("Failed to release context of tid(%{public}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     DFXLOGW("Failed to get context of tid(%{public}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         DFXLOGW("Failed to get context of tid(%{public}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         DFXLOGE("wait_for timeout. tid = %{public}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     DFXLOGU("tid(%{public}d) recv sig(%{public}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         DFXLOGW("Failed to copy local ucontext with tid(%{public}d)", tid);
212     }
213 
214     if (tid != getpid()) {
215         if (!GetSelfStackRange(ctxPtr->stackBottom, ctxPtr->stackTop)) {
216             DFXLOGW("Failed to get stack range with tid(%{public}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         DFXLOGU("Install local signal handler: %{public}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 (syscall(SYS_rt_tgsigqueueinfo, getpid(), tid, si.si_signo, &si) != 0) {
262         DFXLOGW("Failed to send signal(%{public}d) to tid(%{public}d), errno(%{public}d).", si.si_signo, tid, errno);
263         threadContext->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
264         return false;
265     }
266     return true;
267 }
268 } // namespace HiviewDFX
269 } // namespace OHOS
270