1800b99b8Sopenharmony_ci/*
2800b99b8Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
3800b99b8Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4800b99b8Sopenharmony_ci * you may not use this file except in compliance with the License.
5800b99b8Sopenharmony_ci * You may obtain a copy of the License at
6800b99b8Sopenharmony_ci *
7800b99b8Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0
8800b99b8Sopenharmony_ci *
9800b99b8Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10800b99b8Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11800b99b8Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12800b99b8Sopenharmony_ci * See the License for the specific language governing permissions and
13800b99b8Sopenharmony_ci * limitations under the License.
14800b99b8Sopenharmony_ci */
15800b99b8Sopenharmony_ci
16800b99b8Sopenharmony_ci#include "thread_context.h"
17800b99b8Sopenharmony_ci
18800b99b8Sopenharmony_ci#include <chrono>
19800b99b8Sopenharmony_ci#include <csignal>
20800b99b8Sopenharmony_ci#include <map>
21800b99b8Sopenharmony_ci#include <memory>
22800b99b8Sopenharmony_ci#include <mutex>
23800b99b8Sopenharmony_ci#include <securec.h>
24800b99b8Sopenharmony_ci#include <sigchain.h>
25800b99b8Sopenharmony_ci#include <unistd.h>
26800b99b8Sopenharmony_ci
27800b99b8Sopenharmony_ci#include "dfx_define.h"
28800b99b8Sopenharmony_ci#include "dfx_log.h"
29800b99b8Sopenharmony_ci#include "fp_unwinder.h"
30800b99b8Sopenharmony_ci#if defined(__aarch64__)
31800b99b8Sopenharmony_ci#include "unwind_arm64_define.h"
32800b99b8Sopenharmony_ci#endif
33800b99b8Sopenharmony_ci
34800b99b8Sopenharmony_cinamespace OHOS {
35800b99b8Sopenharmony_cinamespace HiviewDFX {
36800b99b8Sopenharmony_cinamespace {
37800b99b8Sopenharmony_ci#undef LOG_DOMAIN
38800b99b8Sopenharmony_ci#undef LOG_TAG
39800b99b8Sopenharmony_ci#define LOG_DOMAIN 0xD002D11
40800b99b8Sopenharmony_ci#define LOG_TAG "DfxThreadContext"
41800b99b8Sopenharmony_ci
42800b99b8Sopenharmony_cistd::mutex g_localMutex;
43800b99b8Sopenharmony_cistd::map<int32_t, std::shared_ptr<ThreadContext>> g_contextMap {};
44800b99b8Sopenharmony_ciconst std::chrono::seconds g_timeOut = std::chrono::seconds(1);
45800b99b8Sopenharmony_ci
46800b99b8Sopenharmony_civoid CreateContext(std::shared_ptr<ThreadContext>& threadContext)
47800b99b8Sopenharmony_ci{
48800b99b8Sopenharmony_ci#ifndef __aarch64__
49800b99b8Sopenharmony_ci    std::unique_lock<std::mutex> lock(threadContext->mtx);
50800b99b8Sopenharmony_ci    if (threadContext->ctx == nullptr) {
51800b99b8Sopenharmony_ci        threadContext->ctx = new ucontext_t;
52800b99b8Sopenharmony_ci    }
53800b99b8Sopenharmony_ci    (void)memset_s(threadContext->ctx, sizeof(ucontext_t), 0, sizeof(ucontext_t));
54800b99b8Sopenharmony_ci#endif
55800b99b8Sopenharmony_ci}
56800b99b8Sopenharmony_ci
57800b99b8Sopenharmony_civoid ReleaseContext(std::shared_ptr<ThreadContext> threadContext)
58800b99b8Sopenharmony_ci{
59800b99b8Sopenharmony_ci#ifndef __aarch64__
60800b99b8Sopenharmony_ci    std::unique_lock<std::mutex> lock(threadContext->mtx);
61800b99b8Sopenharmony_ci    if (threadContext->ctx != nullptr) {
62800b99b8Sopenharmony_ci        delete threadContext->ctx;
63800b99b8Sopenharmony_ci        threadContext->ctx = nullptr;
64800b99b8Sopenharmony_ci    }
65800b99b8Sopenharmony_ci#endif
66800b99b8Sopenharmony_ci}
67800b99b8Sopenharmony_ci
68800b99b8Sopenharmony_cistd::shared_ptr<ThreadContext> GetContextLocked(int32_t tid)
69800b99b8Sopenharmony_ci{
70800b99b8Sopenharmony_ci    auto it = g_contextMap.find(tid);
71800b99b8Sopenharmony_ci    if (it == g_contextMap.end() || it->second == nullptr) {
72800b99b8Sopenharmony_ci        auto threadContext = std::make_shared<ThreadContext>();
73800b99b8Sopenharmony_ci        threadContext->tid = tid;
74800b99b8Sopenharmony_ci        threadContext->frameSz = 0;
75800b99b8Sopenharmony_ci        CreateContext(threadContext);
76800b99b8Sopenharmony_ci        g_contextMap[tid] = threadContext;
77800b99b8Sopenharmony_ci        return threadContext;
78800b99b8Sopenharmony_ci    }
79800b99b8Sopenharmony_ci
80800b99b8Sopenharmony_ci    if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
81800b99b8Sopenharmony_ci        it->second->tid = tid;
82800b99b8Sopenharmony_ci        it->second->frameSz = 0;
83800b99b8Sopenharmony_ci        CreateContext(it->second);
84800b99b8Sopenharmony_ci        return it->second;
85800b99b8Sopenharmony_ci    }
86800b99b8Sopenharmony_ci    DFXLOGE("GetContextLocked nullptr, tid: %{public}d", tid);
87800b99b8Sopenharmony_ci    return nullptr;
88800b99b8Sopenharmony_ci}
89800b99b8Sopenharmony_ci
90800b99b8Sopenharmony_ciAT_UNUSED bool RemoveContextLocked(int32_t tid)
91800b99b8Sopenharmony_ci{
92800b99b8Sopenharmony_ci    auto it = g_contextMap.find(tid);
93800b99b8Sopenharmony_ci    if (it == g_contextMap.end()) {
94800b99b8Sopenharmony_ci        DFXLOGW("Context of tid(%{public}d) is already removed.", tid);
95800b99b8Sopenharmony_ci        return true;
96800b99b8Sopenharmony_ci    }
97800b99b8Sopenharmony_ci    if (it->second == nullptr) {
98800b99b8Sopenharmony_ci        g_contextMap.erase(it);
99800b99b8Sopenharmony_ci        return true;
100800b99b8Sopenharmony_ci    }
101800b99b8Sopenharmony_ci
102800b99b8Sopenharmony_ci    // only release ucontext_t object
103800b99b8Sopenharmony_ci    if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
104800b99b8Sopenharmony_ci        ReleaseContext(it->second);
105800b99b8Sopenharmony_ci        return true;
106800b99b8Sopenharmony_ci    }
107800b99b8Sopenharmony_ci
108800b99b8Sopenharmony_ci    DFXLOGW("Failed to release context of tid(%{public}d), still using?", tid);
109800b99b8Sopenharmony_ci    return false;
110800b99b8Sopenharmony_ci}
111800b99b8Sopenharmony_ci
112800b99b8Sopenharmony_cibool RemoveAllContextLocked()
113800b99b8Sopenharmony_ci{
114800b99b8Sopenharmony_ci    auto it = g_contextMap.begin();
115800b99b8Sopenharmony_ci    while (it != g_contextMap.end()) {
116800b99b8Sopenharmony_ci        if (it->second == nullptr) {
117800b99b8Sopenharmony_ci            it = g_contextMap.erase(it);
118800b99b8Sopenharmony_ci            continue;
119800b99b8Sopenharmony_ci        }
120800b99b8Sopenharmony_ci#ifndef __aarch64__
121800b99b8Sopenharmony_ci        if (it->second->tid == ThreadContextStatus::CONTEXT_UNUSED) {
122800b99b8Sopenharmony_ci            ReleaseContext(it->second);
123800b99b8Sopenharmony_ci        }
124800b99b8Sopenharmony_ci#endif
125800b99b8Sopenharmony_ci        it++;
126800b99b8Sopenharmony_ci    }
127800b99b8Sopenharmony_ci    return true;
128800b99b8Sopenharmony_ci}
129800b99b8Sopenharmony_ci}
130800b99b8Sopenharmony_ci
131800b99b8Sopenharmony_ciLocalThreadContext& LocalThreadContext::GetInstance()
132800b99b8Sopenharmony_ci{
133800b99b8Sopenharmony_ci    static LocalThreadContext instance;
134800b99b8Sopenharmony_ci    return instance;
135800b99b8Sopenharmony_ci}
136800b99b8Sopenharmony_ci
137800b99b8Sopenharmony_ciNO_SANITIZE std::shared_ptr<ThreadContext> LocalThreadContext::GetThreadContext(int32_t tid)
138800b99b8Sopenharmony_ci{
139800b99b8Sopenharmony_ci    std::unique_lock<std::mutex> lock(localMutex_);
140800b99b8Sopenharmony_ci    auto it = g_contextMap.find(tid);
141800b99b8Sopenharmony_ci    if (it != g_contextMap.end()) {
142800b99b8Sopenharmony_ci        return it->second;
143800b99b8Sopenharmony_ci    }
144800b99b8Sopenharmony_ci    DFXLOGW("Failed to get context of tid(%{public}d)", tid);
145800b99b8Sopenharmony_ci    return nullptr;
146800b99b8Sopenharmony_ci}
147800b99b8Sopenharmony_ci
148800b99b8Sopenharmony_civoid LocalThreadContext::ReleaseThread(int32_t tid)
149800b99b8Sopenharmony_ci{
150800b99b8Sopenharmony_ci    std::unique_lock<std::mutex> lock(localMutex_);
151800b99b8Sopenharmony_ci    auto it = g_contextMap.find(tid);
152800b99b8Sopenharmony_ci    if (it == g_contextMap.end() || it->second == nullptr) {
153800b99b8Sopenharmony_ci        return;
154800b99b8Sopenharmony_ci    }
155800b99b8Sopenharmony_ci    it->second->cv.notify_all();
156800b99b8Sopenharmony_ci}
157800b99b8Sopenharmony_ci
158800b99b8Sopenharmony_civoid LocalThreadContext::CleanUp()
159800b99b8Sopenharmony_ci{
160800b99b8Sopenharmony_ci    std::unique_lock<std::mutex> lock(localMutex_);
161800b99b8Sopenharmony_ci    RemoveAllContextLocked();
162800b99b8Sopenharmony_ci}
163800b99b8Sopenharmony_ci
164800b99b8Sopenharmony_cistd::shared_ptr<ThreadContext> LocalThreadContext::CollectThreadContext(int32_t tid)
165800b99b8Sopenharmony_ci{
166800b99b8Sopenharmony_ci    std::unique_lock<std::mutex> lock(localMutex_);
167800b99b8Sopenharmony_ci    auto threadContext = GetContextLocked(tid);
168800b99b8Sopenharmony_ci    if (threadContext == nullptr) {
169800b99b8Sopenharmony_ci        DFXLOGW("Failed to get context of tid(%{public}d), still using?", tid);
170800b99b8Sopenharmony_ci        return nullptr;
171800b99b8Sopenharmony_ci    }
172800b99b8Sopenharmony_ci
173800b99b8Sopenharmony_ci    InitSignalHandler();
174800b99b8Sopenharmony_ci    if (!SignalRequestThread(tid, threadContext.get())) {
175800b99b8Sopenharmony_ci        return nullptr;
176800b99b8Sopenharmony_ci    }
177800b99b8Sopenharmony_ci    if (threadContext->cv.wait_for(lock, g_timeOut) == std::cv_status::timeout) {
178800b99b8Sopenharmony_ci        DFXLOGE("wait_for timeout. tid = %{public}d", tid);
179800b99b8Sopenharmony_ci        return nullptr;
180800b99b8Sopenharmony_ci    }
181800b99b8Sopenharmony_ci    return threadContext;
182800b99b8Sopenharmony_ci}
183800b99b8Sopenharmony_ci
184800b99b8Sopenharmony_ciNO_SANITIZE void LocalThreadContext::CopyContextAndWaitTimeout(int sig, siginfo_t *si, void *context)
185800b99b8Sopenharmony_ci{
186800b99b8Sopenharmony_ci    if (si == nullptr || si->si_code != DUMP_TYPE_LOCAL || si->si_value.sival_ptr == nullptr || context == nullptr) {
187800b99b8Sopenharmony_ci        return;
188800b99b8Sopenharmony_ci    }
189800b99b8Sopenharmony_ci
190800b99b8Sopenharmony_ci    DFXLOGU("tid(%{public}d) recv sig(%{public}d)", gettid(), sig);
191800b99b8Sopenharmony_ci    auto ctxPtr = static_cast<ThreadContext *>(si->si_value.sival_ptr);
192800b99b8Sopenharmony_ci#if defined(__aarch64__)
193800b99b8Sopenharmony_ci    uintptr_t fp = reinterpret_cast<ucontext_t*>(context)->uc_mcontext.regs[REG_FP];
194800b99b8Sopenharmony_ci    uintptr_t pc = reinterpret_cast<ucontext_t*>(context)->uc_mcontext.pc;
195800b99b8Sopenharmony_ci    ctxPtr->firstFrameSp = reinterpret_cast<ucontext_t*>(context)->uc_mcontext.sp;
196800b99b8Sopenharmony_ci    ctxPtr->frameSz = FpUnwinder::GetPtr()->UnwindSafe(pc, fp, ctxPtr->pcs, DEFAULT_MAX_LOCAL_FRAME_NUM);
197800b99b8Sopenharmony_ci    ctxPtr->cv.notify_all();
198800b99b8Sopenharmony_ci    ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
199800b99b8Sopenharmony_ci    return;
200800b99b8Sopenharmony_ci#else
201800b99b8Sopenharmony_ci
202800b99b8Sopenharmony_ci    std::unique_lock<std::mutex> lock(ctxPtr->mtx);
203800b99b8Sopenharmony_ci    if (ctxPtr->ctx == nullptr) {
204800b99b8Sopenharmony_ci        ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
205800b99b8Sopenharmony_ci        return;
206800b99b8Sopenharmony_ci    }
207800b99b8Sopenharmony_ci    ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
208800b99b8Sopenharmony_ci    int tid = gettid();
209800b99b8Sopenharmony_ci    if (memcpy_s(&ctxPtr->ctx->uc_mcontext, sizeof(ucontext->uc_mcontext),
210800b99b8Sopenharmony_ci        &ucontext->uc_mcontext, sizeof(ucontext->uc_mcontext)) != 0) {
211800b99b8Sopenharmony_ci        DFXLOGW("Failed to copy local ucontext with tid(%{public}d)", tid);
212800b99b8Sopenharmony_ci    }
213800b99b8Sopenharmony_ci
214800b99b8Sopenharmony_ci    if (tid != getpid()) {
215800b99b8Sopenharmony_ci        if (!GetSelfStackRange(ctxPtr->stackBottom, ctxPtr->stackTop)) {
216800b99b8Sopenharmony_ci            DFXLOGW("Failed to get stack range with tid(%{public}d)", tid);
217800b99b8Sopenharmony_ci        }
218800b99b8Sopenharmony_ci    }
219800b99b8Sopenharmony_ci
220800b99b8Sopenharmony_ci    ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_READY);
221800b99b8Sopenharmony_ci    ctxPtr->cv.notify_all();
222800b99b8Sopenharmony_ci    ctxPtr->cv.wait_for(lock, g_timeOut);
223800b99b8Sopenharmony_ci    ctxPtr->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
224800b99b8Sopenharmony_ci#endif
225800b99b8Sopenharmony_ci}
226800b99b8Sopenharmony_ci
227800b99b8Sopenharmony_cibool LocalThreadContext::GetStackRange(int32_t tid, uintptr_t& stackBottom, uintptr_t& stackTop)
228800b99b8Sopenharmony_ci{
229800b99b8Sopenharmony_ci    auto ctxPtr = LocalThreadContext::GetInstance().GetThreadContext(tid);
230800b99b8Sopenharmony_ci    if (ctxPtr == nullptr) {
231800b99b8Sopenharmony_ci        return false;
232800b99b8Sopenharmony_ci    }
233800b99b8Sopenharmony_ci    stackBottom = ctxPtr->stackBottom;
234800b99b8Sopenharmony_ci    stackTop = ctxPtr->stackTop;
235800b99b8Sopenharmony_ci    return true;
236800b99b8Sopenharmony_ci}
237800b99b8Sopenharmony_ci
238800b99b8Sopenharmony_civoid LocalThreadContext::InitSignalHandler()
239800b99b8Sopenharmony_ci{
240800b99b8Sopenharmony_ci    static std::once_flag flag;
241800b99b8Sopenharmony_ci    std::call_once(flag, [&]() {
242800b99b8Sopenharmony_ci        FpUnwinder::GetPtr();
243800b99b8Sopenharmony_ci        struct sigaction action;
244800b99b8Sopenharmony_ci        (void)memset_s(&action, sizeof(action), 0, sizeof(action));
245800b99b8Sopenharmony_ci        sigemptyset(&action.sa_mask);
246800b99b8Sopenharmony_ci        sigaddset(&action.sa_mask, SIGLOCAL_DUMP);
247800b99b8Sopenharmony_ci        action.sa_flags = SA_RESTART | SA_SIGINFO;
248800b99b8Sopenharmony_ci        action.sa_sigaction = LocalThreadContext::CopyContextAndWaitTimeout;
249800b99b8Sopenharmony_ci        DFXLOGU("Install local signal handler: %{public}d", SIGLOCAL_DUMP);
250800b99b8Sopenharmony_ci        sigaction(SIGLOCAL_DUMP, &action, nullptr);
251800b99b8Sopenharmony_ci    });
252800b99b8Sopenharmony_ci}
253800b99b8Sopenharmony_ci
254800b99b8Sopenharmony_cibool LocalThreadContext::SignalRequestThread(int32_t tid, ThreadContext* threadContext)
255800b99b8Sopenharmony_ci{
256800b99b8Sopenharmony_ci    siginfo_t si {0};
257800b99b8Sopenharmony_ci    si.si_signo = SIGLOCAL_DUMP;
258800b99b8Sopenharmony_ci    si.si_errno = 0;
259800b99b8Sopenharmony_ci    si.si_code = DUMP_TYPE_LOCAL;
260800b99b8Sopenharmony_ci    si.si_value.sival_ptr = reinterpret_cast<void *>(threadContext);
261800b99b8Sopenharmony_ci    if (syscall(SYS_rt_tgsigqueueinfo, getpid(), tid, si.si_signo, &si) != 0) {
262800b99b8Sopenharmony_ci        DFXLOGW("Failed to send signal(%{public}d) to tid(%{public}d), errno(%{public}d).", si.si_signo, tid, errno);
263800b99b8Sopenharmony_ci        threadContext->tid = static_cast<int32_t>(ThreadContextStatus::CONTEXT_UNUSED);
264800b99b8Sopenharmony_ci        return false;
265800b99b8Sopenharmony_ci    }
266800b99b8Sopenharmony_ci    return true;
267800b99b8Sopenharmony_ci}
268800b99b8Sopenharmony_ci} // namespace HiviewDFX
269800b99b8Sopenharmony_ci} // namespace OHOS
270