1209bc2fbSopenharmony_ci/*
2209bc2fbSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
3209bc2fbSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4209bc2fbSopenharmony_ci * you may not use this file except in compliance with the License.
5209bc2fbSopenharmony_ci * You may obtain a copy of the License at
6209bc2fbSopenharmony_ci *
7209bc2fbSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8209bc2fbSopenharmony_ci *
9209bc2fbSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10209bc2fbSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11209bc2fbSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12209bc2fbSopenharmony_ci * See the License for the specific language governing permissions and
13209bc2fbSopenharmony_ci * limitations under the License.
14209bc2fbSopenharmony_ci */
15209bc2fbSopenharmony_ci
16209bc2fbSopenharmony_ci#include "thread_sampler.h"
17209bc2fbSopenharmony_ci
18209bc2fbSopenharmony_ci#include <atomic>
19209bc2fbSopenharmony_ci#include <condition_variable>
20209bc2fbSopenharmony_ci#include <memory>
21209bc2fbSopenharmony_ci#include <queue>
22209bc2fbSopenharmony_ci#include <set>
23209bc2fbSopenharmony_ci#include <string>
24209bc2fbSopenharmony_ci
25209bc2fbSopenharmony_ci#include <sys/mman.h>
26209bc2fbSopenharmony_ci#include <sys/prctl.h>
27209bc2fbSopenharmony_ci#include <syscall.h>
28209bc2fbSopenharmony_ci#include <csignal>
29209bc2fbSopenharmony_ci
30209bc2fbSopenharmony_ci#include "unwinder.h"
31209bc2fbSopenharmony_ci#include "dfx_regs.h"
32209bc2fbSopenharmony_ci#include "dfx_elf.h"
33209bc2fbSopenharmony_ci#include "dfx_ark.h"
34209bc2fbSopenharmony_ci#include "dfx_frame_formatter.h"
35209bc2fbSopenharmony_ci#include "sample_stack_printer.h"
36209bc2fbSopenharmony_ci#include "thread_sampler_utils.h"
37209bc2fbSopenharmony_ci#include "file_ex.h"
38209bc2fbSopenharmony_ci
39209bc2fbSopenharmony_ci#define NO_SANITIZER __attribute__((no_sanitize("address"), no_sanitize("hwaddress")))
40209bc2fbSopenharmony_ci
41209bc2fbSopenharmony_cinamespace OHOS {
42209bc2fbSopenharmony_cinamespace HiviewDFX {
43209bc2fbSopenharmony_cistatic const int CRASH_SIGNAL_LIST[] = {
44209bc2fbSopenharmony_ci    SIGILL, SIGABRT, SIGBUS, SIGFPE,
45209bc2fbSopenharmony_ci    SIGSEGV, SIGSTKFLT, SIGSYS, SIGTRAP
46209bc2fbSopenharmony_ci};
47209bc2fbSopenharmony_cistatic pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
48209bc2fbSopenharmony_ciconstexpr int LOCK_TIMEOUT = 10;
49209bc2fbSopenharmony_ci
50209bc2fbSopenharmony_civoid ThreadSampler::ThreadSamplerSignalHandler(int sig, siginfo_t* si, void* context)
51209bc2fbSopenharmony_ci{
52209bc2fbSopenharmony_ci#if defined(__aarch64__)
53209bc2fbSopenharmony_ci    int preErrno = errno;
54209bc2fbSopenharmony_ci    int err = pthread_mutex_trylock(&mutex_);
55209bc2fbSopenharmony_ci    if (err != 0) {
56209bc2fbSopenharmony_ci        errno = preErrno;
57209bc2fbSopenharmony_ci        return;
58209bc2fbSopenharmony_ci    }
59209bc2fbSopenharmony_ci    ThreadSampler::GetInstance().WriteContext(context);
60209bc2fbSopenharmony_ci    pthread_mutex_unlock(&mutex_);
61209bc2fbSopenharmony_ci    errno = preErrno;
62209bc2fbSopenharmony_ci#endif
63209bc2fbSopenharmony_ci}
64209bc2fbSopenharmony_ci
65209bc2fbSopenharmony_ciThreadSampler::ThreadSampler()
66209bc2fbSopenharmony_ci{
67209bc2fbSopenharmony_ci    XCOLLIE_LOGI("Create ThreadSampler.\n");
68209bc2fbSopenharmony_ci}
69209bc2fbSopenharmony_ci
70209bc2fbSopenharmony_ciThreadSampler::~ThreadSampler()
71209bc2fbSopenharmony_ci{
72209bc2fbSopenharmony_ci    XCOLLIE_LOGI("Destroy ThreadSampler.\n");
73209bc2fbSopenharmony_ci}
74209bc2fbSopenharmony_ci
75209bc2fbSopenharmony_ciint ThreadSampler::FindUnwindTable(uintptr_t pc, UnwindTableInfo& outTableInfo, void *arg)
76209bc2fbSopenharmony_ci{
77209bc2fbSopenharmony_ci    UnwindInfo* unwindInfo = static_cast<UnwindInfo *>(arg);
78209bc2fbSopenharmony_ci    if (unwindInfo == nullptr) {
79209bc2fbSopenharmony_ci        XCOLLIE_LOGE("invalid FindUnwindTable param\n");
80209bc2fbSopenharmony_ci        return -1;
81209bc2fbSopenharmony_ci    }
82209bc2fbSopenharmony_ci
83209bc2fbSopenharmony_ci    std::shared_ptr<DfxMap> map;
84209bc2fbSopenharmony_ci    if (unwindInfo->maps->FindMapByAddr(pc, map)) {
85209bc2fbSopenharmony_ci        if (map == nullptr) {
86209bc2fbSopenharmony_ci            XCOLLIE_LOGE("FindUnwindTable: map is nullptr\n");
87209bc2fbSopenharmony_ci            return -1;
88209bc2fbSopenharmony_ci        }
89209bc2fbSopenharmony_ci        auto elf = map->GetElf(getpid());
90209bc2fbSopenharmony_ci        if (elf != nullptr) {
91209bc2fbSopenharmony_ci            return elf->FindUnwindTableInfo(pc, map, outTableInfo);
92209bc2fbSopenharmony_ci        }
93209bc2fbSopenharmony_ci    }
94209bc2fbSopenharmony_ci    return -1;
95209bc2fbSopenharmony_ci}
96209bc2fbSopenharmony_ci
97209bc2fbSopenharmony_ciint ThreadSampler::AccessMem(uintptr_t addr, uintptr_t *val, void *arg)
98209bc2fbSopenharmony_ci{
99209bc2fbSopenharmony_ci    UnwindInfo* unwindInfo = static_cast<UnwindInfo *>(arg);
100209bc2fbSopenharmony_ci    if (unwindInfo == nullptr || addr + sizeof(uintptr_t) < addr) {
101209bc2fbSopenharmony_ci        XCOLLIE_LOGE("invalid AccessMem param\n");
102209bc2fbSopenharmony_ci        return -1;
103209bc2fbSopenharmony_ci    }
104209bc2fbSopenharmony_ci
105209bc2fbSopenharmony_ci    *val = 0;
106209bc2fbSopenharmony_ci    if (addr < unwindInfo->context->sp ||
107209bc2fbSopenharmony_ci        addr + sizeof(uintptr_t) >= unwindInfo->context->sp + STACK_BUFFER_SIZE) {
108209bc2fbSopenharmony_ci        return ThreadSampler::GetInstance().AccessElfMem(addr, val);
109209bc2fbSopenharmony_ci    } else {
110209bc2fbSopenharmony_ci        size_t stackOffset = addr - unwindInfo->context->sp;
111209bc2fbSopenharmony_ci        if (stackOffset >= STACK_BUFFER_SIZE) {
112209bc2fbSopenharmony_ci            XCOLLIE_LOGE("limit stack\n");
113209bc2fbSopenharmony_ci            return -1;
114209bc2fbSopenharmony_ci        }
115209bc2fbSopenharmony_ci        *val = *(reinterpret_cast<uintptr_t *>(&unwindInfo->context->buffer[stackOffset]));
116209bc2fbSopenharmony_ci    }
117209bc2fbSopenharmony_ci    return 0;
118209bc2fbSopenharmony_ci}
119209bc2fbSopenharmony_ci
120209bc2fbSopenharmony_ciint ThreadSampler::GetMapByPc(uintptr_t pc, std::shared_ptr<DfxMap>& map, void *arg)
121209bc2fbSopenharmony_ci{
122209bc2fbSopenharmony_ci    UnwindInfo* unwindInfo = static_cast<UnwindInfo *>(arg);
123209bc2fbSopenharmony_ci    if (unwindInfo == nullptr) {
124209bc2fbSopenharmony_ci        XCOLLIE_LOGE("invalid GetMapByPc param\n");
125209bc2fbSopenharmony_ci        return -1;
126209bc2fbSopenharmony_ci    }
127209bc2fbSopenharmony_ci
128209bc2fbSopenharmony_ci    return unwindInfo->maps->FindMapByAddr(pc, map) ? 0 : -1;
129209bc2fbSopenharmony_ci}
130209bc2fbSopenharmony_ci
131209bc2fbSopenharmony_cibool ThreadSampler::Init(int collectStackCount)
132209bc2fbSopenharmony_ci{
133209bc2fbSopenharmony_ci    if (init_) {
134209bc2fbSopenharmony_ci        return true;
135209bc2fbSopenharmony_ci    }
136209bc2fbSopenharmony_ci
137209bc2fbSopenharmony_ci    if (!InitRecordBuffer()) {
138209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to InitRecordBuffer\n");
139209bc2fbSopenharmony_ci        Deinit();
140209bc2fbSopenharmony_ci        return false;
141209bc2fbSopenharmony_ci    }
142209bc2fbSopenharmony_ci
143209bc2fbSopenharmony_ci    if (!InitUnwinder()) {
144209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to InitUnwinder\n");
145209bc2fbSopenharmony_ci        Deinit();
146209bc2fbSopenharmony_ci        return false;
147209bc2fbSopenharmony_ci    }
148209bc2fbSopenharmony_ci
149209bc2fbSopenharmony_ci    pid_ = getprocpid();
150209bc2fbSopenharmony_ci    if (!InitUniqueStackTable()) {
151209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to InitUniqueStackTable\n");
152209bc2fbSopenharmony_ci        Deinit();
153209bc2fbSopenharmony_ci        return false;
154209bc2fbSopenharmony_ci    }
155209bc2fbSopenharmony_ci
156209bc2fbSopenharmony_ci    if (!InstallSignalHandler()) {
157209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to InstallSignalHandler\n");
158209bc2fbSopenharmony_ci        Deinit();
159209bc2fbSopenharmony_ci        return false;
160209bc2fbSopenharmony_ci    }
161209bc2fbSopenharmony_ci
162209bc2fbSopenharmony_ci    if (collectStackCount <= 0) {
163209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Invalid collectStackCount\n");
164209bc2fbSopenharmony_ci        Deinit();
165209bc2fbSopenharmony_ci        return false;
166209bc2fbSopenharmony_ci    }
167209bc2fbSopenharmony_ci    stackIdCount_.reserve(collectStackCount);
168209bc2fbSopenharmony_ci
169209bc2fbSopenharmony_ci    init_ = true;
170209bc2fbSopenharmony_ci    return true;
171209bc2fbSopenharmony_ci}
172209bc2fbSopenharmony_ci
173209bc2fbSopenharmony_cibool ThreadSampler::InitRecordBuffer()
174209bc2fbSopenharmony_ci{
175209bc2fbSopenharmony_ci    if (mmapStart_ != MAP_FAILED) {
176209bc2fbSopenharmony_ci        return true;
177209bc2fbSopenharmony_ci    }
178209bc2fbSopenharmony_ci    // create buffer
179209bc2fbSopenharmony_ci    bufferSize_ = SAMPLER_MAX_BUFFER_SZ * sizeof(struct ThreadUnwindContext);
180209bc2fbSopenharmony_ci    mmapStart_ = mmap(nullptr, bufferSize_,
181209bc2fbSopenharmony_ci        PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
182209bc2fbSopenharmony_ci    if (mmapStart_ == MAP_FAILED) {
183209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to create buffer for thread sampler!(%{public}d)\n", errno);
184209bc2fbSopenharmony_ci        return false;
185209bc2fbSopenharmony_ci    }
186209bc2fbSopenharmony_ci
187209bc2fbSopenharmony_ci    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, mmapStart_, bufferSize_, "sampler_buf");
188209bc2fbSopenharmony_ci    return true;
189209bc2fbSopenharmony_ci}
190209bc2fbSopenharmony_ci
191209bc2fbSopenharmony_civoid ThreadSampler::ReleaseRecordBuffer()
192209bc2fbSopenharmony_ci{
193209bc2fbSopenharmony_ci    if (mmapStart_ == MAP_FAILED) {
194209bc2fbSopenharmony_ci        return;
195209bc2fbSopenharmony_ci    }
196209bc2fbSopenharmony_ci    // release buffer
197209bc2fbSopenharmony_ci    if (munmap(mmapStart_, bufferSize_) != 0) {
198209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to release buffer!(%{public}d)\n", errno);
199209bc2fbSopenharmony_ci        return;
200209bc2fbSopenharmony_ci    }
201209bc2fbSopenharmony_ci    mmapStart_ = MAP_FAILED;
202209bc2fbSopenharmony_ci}
203209bc2fbSopenharmony_ci
204209bc2fbSopenharmony_cibool ThreadSampler::InitUnwinder()
205209bc2fbSopenharmony_ci{
206209bc2fbSopenharmony_ci    accessors_ = std::make_shared<OHOS::HiviewDFX::UnwindAccessors>();
207209bc2fbSopenharmony_ci    accessors_->AccessReg = nullptr;
208209bc2fbSopenharmony_ci    accessors_->AccessMem = &ThreadSampler::AccessMem;
209209bc2fbSopenharmony_ci    accessors_->GetMapByPc = &ThreadSampler::GetMapByPc;
210209bc2fbSopenharmony_ci    accessors_->FindUnwindTable = &ThreadSampler::FindUnwindTable;
211209bc2fbSopenharmony_ci    unwinder_ = std::make_shared<Unwinder>(accessors_, true);
212209bc2fbSopenharmony_ci
213209bc2fbSopenharmony_ci    maps_ = DfxMaps::Create();
214209bc2fbSopenharmony_ci    if (maps_ == nullptr) {
215209bc2fbSopenharmony_ci        XCOLLIE_LOGE("maps is nullptr\n");
216209bc2fbSopenharmony_ci        return false;
217209bc2fbSopenharmony_ci    }
218209bc2fbSopenharmony_ci    if (!maps_->GetStackRange(stackBegin_, stackEnd_)) {
219209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to get stack range\n");
220209bc2fbSopenharmony_ci        return false;
221209bc2fbSopenharmony_ci    }
222209bc2fbSopenharmony_ci    return true;
223209bc2fbSopenharmony_ci}
224209bc2fbSopenharmony_ci
225209bc2fbSopenharmony_cibool ThreadSampler::InitUniqueStackTable()
226209bc2fbSopenharmony_ci{
227209bc2fbSopenharmony_ci    uniqueStackTable_ = std::make_unique<UniqueStackTable>(pid_, uniqueStackTableSize_);
228209bc2fbSopenharmony_ci    if (!uniqueStackTable_->Init()) {
229209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to init unique_table\n");
230209bc2fbSopenharmony_ci        return false;
231209bc2fbSopenharmony_ci    }
232209bc2fbSopenharmony_ci    void* uniqueTableBufMMap = reinterpret_cast<void*>(uniqueStackTable_->GetHeadNode());
233209bc2fbSopenharmony_ci    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, uniqueTableBufMMap, uniqueStackTableSize_, uniTableMMapName_.c_str());
234209bc2fbSopenharmony_ci    return true;
235209bc2fbSopenharmony_ci}
236209bc2fbSopenharmony_ci
237209bc2fbSopenharmony_civoid ThreadSampler::DeinitUniqueStackTable()
238209bc2fbSopenharmony_ci{
239209bc2fbSopenharmony_ci    uniqueStackTable_.reset();
240209bc2fbSopenharmony_ci}
241209bc2fbSopenharmony_ci
242209bc2fbSopenharmony_civoid ThreadSampler::DestroyUnwinder()
243209bc2fbSopenharmony_ci{
244209bc2fbSopenharmony_ci    maps_.reset();
245209bc2fbSopenharmony_ci    DfxArk::ArkDestoryLocal();
246209bc2fbSopenharmony_ci    unwinder_.reset();
247209bc2fbSopenharmony_ci    accessors_.reset();
248209bc2fbSopenharmony_ci}
249209bc2fbSopenharmony_ci
250209bc2fbSopenharmony_cibool ThreadSampler::InstallSignalHandler()
251209bc2fbSopenharmony_ci{
252209bc2fbSopenharmony_ci    struct sigaction action {};
253209bc2fbSopenharmony_ci    sigfillset(&action.sa_mask);
254209bc2fbSopenharmony_ci    for (size_t i = 0; i < sizeof(CRASH_SIGNAL_LIST) / sizeof(CRASH_SIGNAL_LIST[0]); i++) {
255209bc2fbSopenharmony_ci        sigdelset(&action.sa_mask, CRASH_SIGNAL_LIST[i]);
256209bc2fbSopenharmony_ci    }
257209bc2fbSopenharmony_ci    action.sa_sigaction = ThreadSampler::ThreadSamplerSignalHandler;
258209bc2fbSopenharmony_ci    action.sa_flags = SA_RESTART | SA_SIGINFO;
259209bc2fbSopenharmony_ci    if (sigaction(MUSL_SIGNAL_SAMPLE_STACK, &action, nullptr) != 0) {
260209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to register signal(%{public}d:%{public}d)", MUSL_SIGNAL_SAMPLE_STACK, errno);
261209bc2fbSopenharmony_ci        return false;
262209bc2fbSopenharmony_ci    }
263209bc2fbSopenharmony_ci    return true;
264209bc2fbSopenharmony_ci}
265209bc2fbSopenharmony_ci
266209bc2fbSopenharmony_civoid ThreadSampler::UninstallSignalHandler()
267209bc2fbSopenharmony_ci{
268209bc2fbSopenharmony_ci    if (signal(MUSL_SIGNAL_SAMPLE_STACK, SIG_IGN) == SIG_ERR) {
269209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to unregister signal(%{public}d)", MUSL_SIGNAL_SAMPLE_STACK);
270209bc2fbSopenharmony_ci    }
271209bc2fbSopenharmony_ci}
272209bc2fbSopenharmony_ci
273209bc2fbSopenharmony_ciint ThreadSampler::AccessElfMem(uintptr_t addr, uintptr_t *val)
274209bc2fbSopenharmony_ci{
275209bc2fbSopenharmony_ci    std::shared_ptr<DfxMap> map;
276209bc2fbSopenharmony_ci    if (maps_->FindMapByAddr(addr, map)) {
277209bc2fbSopenharmony_ci        if (map == nullptr) {
278209bc2fbSopenharmony_ci            XCOLLIE_LOGE("AccessElfMem: map is nullptr\n");
279209bc2fbSopenharmony_ci            return -1;
280209bc2fbSopenharmony_ci        }
281209bc2fbSopenharmony_ci        auto elf = map->GetElf(getpid());
282209bc2fbSopenharmony_ci        if (elf != nullptr) {
283209bc2fbSopenharmony_ci            uint64_t foff = addr - map->begin + map->offset - elf->GetBaseOffset();
284209bc2fbSopenharmony_ci            if (elf->Read(foff, val, sizeof(uintptr_t))) {
285209bc2fbSopenharmony_ci                return 0;
286209bc2fbSopenharmony_ci            }
287209bc2fbSopenharmony_ci        }
288209bc2fbSopenharmony_ci    }
289209bc2fbSopenharmony_ci    return -1;
290209bc2fbSopenharmony_ci}
291209bc2fbSopenharmony_ci
292209bc2fbSopenharmony_ciThreadUnwindContext* ThreadSampler::GetReadContext()
293209bc2fbSopenharmony_ci{
294209bc2fbSopenharmony_ci    if (mmapStart_ == MAP_FAILED) {
295209bc2fbSopenharmony_ci        return nullptr;
296209bc2fbSopenharmony_ci    }
297209bc2fbSopenharmony_ci    ThreadUnwindContext* contextArray = static_cast<ThreadUnwindContext*>(mmapStart_);
298209bc2fbSopenharmony_ci    int32_t index = readIndex_;
299209bc2fbSopenharmony_ci    if (contextArray[index].requestTime == 0 || contextArray[index].snapshotTime == 0) {
300209bc2fbSopenharmony_ci        return nullptr;
301209bc2fbSopenharmony_ci    }
302209bc2fbSopenharmony_ci
303209bc2fbSopenharmony_ci    ThreadUnwindContext* ret = &contextArray[index];
304209bc2fbSopenharmony_ci    readIndex_ = (index + 1) % SAMPLER_MAX_BUFFER_SZ;
305209bc2fbSopenharmony_ci    return ret;
306209bc2fbSopenharmony_ci}
307209bc2fbSopenharmony_ci
308209bc2fbSopenharmony_ciThreadUnwindContext* ThreadSampler::GetWriteContext()
309209bc2fbSopenharmony_ci{
310209bc2fbSopenharmony_ci    if (mmapStart_ == MAP_FAILED) {
311209bc2fbSopenharmony_ci        return nullptr;
312209bc2fbSopenharmony_ci    }
313209bc2fbSopenharmony_ci    ThreadUnwindContext* contextArray = static_cast<ThreadUnwindContext*>(mmapStart_);
314209bc2fbSopenharmony_ci    int32_t index = writeIndex_;
315209bc2fbSopenharmony_ci    if (contextArray[index].requestTime > 0 &&
316209bc2fbSopenharmony_ci        (contextArray[index].snapshotTime == 0 || contextArray[index].processTime == 0)) {
317209bc2fbSopenharmony_ci        return nullptr;
318209bc2fbSopenharmony_ci    }
319209bc2fbSopenharmony_ci    return &contextArray[index];
320209bc2fbSopenharmony_ci}
321209bc2fbSopenharmony_ci
322209bc2fbSopenharmony_ciNO_SANITIZER void ThreadSampler::WriteContext(void* context)
323209bc2fbSopenharmony_ci{
324209bc2fbSopenharmony_ci#if defined(__aarch64__)
325209bc2fbSopenharmony_ci    if (!init_) {
326209bc2fbSopenharmony_ci        return;
327209bc2fbSopenharmony_ci    }
328209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
329209bc2fbSopenharmony_ci    uint64_t begin = GetCurrentTimeNanoseconds();
330209bc2fbSopenharmony_ci#endif
331209bc2fbSopenharmony_ci    if (mmapStart_ == MAP_FAILED) {
332209bc2fbSopenharmony_ci        return;
333209bc2fbSopenharmony_ci    }
334209bc2fbSopenharmony_ci    ThreadUnwindContext* contextArray = static_cast<ThreadUnwindContext*>(mmapStart_);
335209bc2fbSopenharmony_ci    int32_t index = writeIndex_;
336209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
337209bc2fbSopenharmony_ci    signalTimeCost_ += begin - contextArray[index].requestTime;
338209bc2fbSopenharmony_ci#endif
339209bc2fbSopenharmony_ci
340209bc2fbSopenharmony_ci    // current buffer has not been processed, stop copy
341209bc2fbSopenharmony_ci    if (contextArray[index].snapshotTime > 0 && contextArray[index].processTime == 0) {
342209bc2fbSopenharmony_ci        return;
343209bc2fbSopenharmony_ci    }
344209bc2fbSopenharmony_ci
345209bc2fbSopenharmony_ci    contextArray[index].fp = static_cast<ucontext_t*>(context)->uc_mcontext.regs[RegsEnumArm64::REG_FP];
346209bc2fbSopenharmony_ci    contextArray[index].lr = static_cast<ucontext_t*>(context)->uc_mcontext.regs[RegsEnumArm64::REG_LR];
347209bc2fbSopenharmony_ci    contextArray[index].sp = static_cast<ucontext_t*>(context)->uc_mcontext.sp;
348209bc2fbSopenharmony_ci    contextArray[index].pc = static_cast<ucontext_t*>(context)->uc_mcontext.pc;
349209bc2fbSopenharmony_ci    if (contextArray[index].sp < stackBegin_ ||
350209bc2fbSopenharmony_ci        contextArray[index].sp >= stackEnd_) {
351209bc2fbSopenharmony_ci        return;
352209bc2fbSopenharmony_ci    }
353209bc2fbSopenharmony_ci
354209bc2fbSopenharmony_ci    uintptr_t curStackSz = stackEnd_ - contextArray[index].sp;
355209bc2fbSopenharmony_ci    uintptr_t cpySz = curStackSz  > STACK_BUFFER_SIZE ? STACK_BUFFER_SIZE : curStackSz;
356209bc2fbSopenharmony_ci
357209bc2fbSopenharmony_ci    for (uintptr_t pos = 0; pos < cpySz; pos++) {
358209bc2fbSopenharmony_ci        reinterpret_cast<char*>(contextArray[index].buffer)[pos] =
359209bc2fbSopenharmony_ci            reinterpret_cast<const char*>(contextArray[index].sp)[pos];
360209bc2fbSopenharmony_ci    }
361209bc2fbSopenharmony_ci
362209bc2fbSopenharmony_ci    writeIndex_ = (index + 1) % SAMPLER_MAX_BUFFER_SZ;
363209bc2fbSopenharmony_ci    uint64_t end = GetCurrentTimeNanoseconds();
364209bc2fbSopenharmony_ci    contextArray[index].processTime.store(0, std::memory_order_relaxed);
365209bc2fbSopenharmony_ci    contextArray[index].snapshotTime.store(end, std::memory_order_release);
366209bc2fbSopenharmony_ci
367209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
368209bc2fbSopenharmony_ci    copyStackCount_++;
369209bc2fbSopenharmony_ci    copyStackTimeCost_ += end - begin;
370209bc2fbSopenharmony_ci#endif
371209bc2fbSopenharmony_ci#endif  // #if defined(__aarch64__)
372209bc2fbSopenharmony_ci}
373209bc2fbSopenharmony_ci
374209bc2fbSopenharmony_civoid ThreadSampler::SendSampleRequest()
375209bc2fbSopenharmony_ci{
376209bc2fbSopenharmony_ci    ThreadUnwindContext* ptr = GetWriteContext();
377209bc2fbSopenharmony_ci    if (ptr == nullptr) {
378209bc2fbSopenharmony_ci        return;
379209bc2fbSopenharmony_ci    }
380209bc2fbSopenharmony_ci
381209bc2fbSopenharmony_ci    uint64_t ts = GetCurrentTimeNanoseconds();
382209bc2fbSopenharmony_ci
383209bc2fbSopenharmony_ci    ptr->requestTime = ts;
384209bc2fbSopenharmony_ci    siginfo_t si {0};
385209bc2fbSopenharmony_ci    si.si_signo = MUSL_SIGNAL_SAMPLE_STACK;
386209bc2fbSopenharmony_ci    si.si_errno = 0;
387209bc2fbSopenharmony_ci    si.si_code = -1;
388209bc2fbSopenharmony_ci    if (syscall(SYS_rt_tgsigqueueinfo, pid_, pid_, si.si_signo, &si) != 0) {
389209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to queue signal(%{public}d) to %{public}d, errno(%{public}d).\n",
390209bc2fbSopenharmony_ci            si.si_signo, pid_, errno);
391209bc2fbSopenharmony_ci        return;
392209bc2fbSopenharmony_ci    }
393209bc2fbSopenharmony_ci#if defined (CONSUME_STATISTICS)
394209bc2fbSopenharmony_ci    requestCount_++;
395209bc2fbSopenharmony_ci#endif
396209bc2fbSopenharmony_ci}
397209bc2fbSopenharmony_ci
398209bc2fbSopenharmony_civoid ThreadSampler::ProcessStackBuffer()
399209bc2fbSopenharmony_ci{
400209bc2fbSopenharmony_ci#if defined(__aarch64__)
401209bc2fbSopenharmony_ci    if (!init_) {
402209bc2fbSopenharmony_ci        XCOLLIE_LOGE("sampler has not initialized.\n");
403209bc2fbSopenharmony_ci        return;
404209bc2fbSopenharmony_ci    }
405209bc2fbSopenharmony_ci    while (true) {
406209bc2fbSopenharmony_ci        ThreadUnwindContext* context = GetReadContext();
407209bc2fbSopenharmony_ci        if (context == nullptr) {
408209bc2fbSopenharmony_ci            break;
409209bc2fbSopenharmony_ci        }
410209bc2fbSopenharmony_ci
411209bc2fbSopenharmony_ci        UnwindInfo unwindInfo = {
412209bc2fbSopenharmony_ci            .context = context,
413209bc2fbSopenharmony_ci            .maps = maps_.get(),
414209bc2fbSopenharmony_ci        };
415209bc2fbSopenharmony_ci
416209bc2fbSopenharmony_ci        struct TimeAndFrames taf;
417209bc2fbSopenharmony_ci        taf.requestTime = unwindInfo.context->requestTime;
418209bc2fbSopenharmony_ci        taf.snapshotTime = unwindInfo.context->snapshotTime;
419209bc2fbSopenharmony_ci
420209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
421209bc2fbSopenharmony_ci        uint64_t unwindStart = GetCurrentTimeNanoseconds();
422209bc2fbSopenharmony_ci#endif
423209bc2fbSopenharmony_ci        DoUnwind(unwinder_, unwindInfo);
424209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
425209bc2fbSopenharmony_ci        uint64_t unwindEnd = GetCurrentTimeNanoseconds();
426209bc2fbSopenharmony_ci#endif
427209bc2fbSopenharmony_ci        /* for print full stack */
428209bc2fbSopenharmony_ci        auto frames = unwinder_->GetFrames();
429209bc2fbSopenharmony_ci        taf.frameList = frames;
430209bc2fbSopenharmony_ci        timeAndFrameList_.emplace_back(taf);
431209bc2fbSopenharmony_ci        /* for print tree format stack */
432209bc2fbSopenharmony_ci        auto pcs = unwinder_->GetPcs();
433209bc2fbSopenharmony_ci        uint64_t stackId = 0;
434209bc2fbSopenharmony_ci        auto stackIdPtr = reinterpret_cast<OHOS::HiviewDFX::StackId*>(&stackId);
435209bc2fbSopenharmony_ci        uniqueStackTable_->PutPcsInTable(stackIdPtr, pcs.data(), pcs.size());
436209bc2fbSopenharmony_ci        PutStackId(stackIdCount_, stackId);
437209bc2fbSopenharmony_ci
438209bc2fbSopenharmony_ci        uint64_t ts = GetCurrentTimeNanoseconds();
439209bc2fbSopenharmony_ci
440209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
441209bc2fbSopenharmony_ci        processTimeCost_ += ts - unwindStart;
442209bc2fbSopenharmony_ci        processCount_++;
443209bc2fbSopenharmony_ci        unwindCount_++;
444209bc2fbSopenharmony_ci        unwindTimeCost_ += unwindEnd - unwindStart;
445209bc2fbSopenharmony_ci#endif  //#if defined(CONSUME_STATISTICS)
446209bc2fbSopenharmony_ci        context->requestTime.store(0, std::memory_order_release);
447209bc2fbSopenharmony_ci        context->snapshotTime.store(0, std::memory_order_release);
448209bc2fbSopenharmony_ci        context->processTime.store(ts, std::memory_order_release);
449209bc2fbSopenharmony_ci    }
450209bc2fbSopenharmony_ci#endif  // #if defined(__aarch64__)
451209bc2fbSopenharmony_ci}
452209bc2fbSopenharmony_ci
453209bc2fbSopenharmony_ciint32_t ThreadSampler::Sample()
454209bc2fbSopenharmony_ci{
455209bc2fbSopenharmony_ci    if (!init_) {
456209bc2fbSopenharmony_ci        XCOLLIE_LOGE("sampler has not initialized.\n");
457209bc2fbSopenharmony_ci        return -1;
458209bc2fbSopenharmony_ci    }
459209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
460209bc2fbSopenharmony_ci    sampleCount_++;
461209bc2fbSopenharmony_ci#endif
462209bc2fbSopenharmony_ci    SendSampleRequest();
463209bc2fbSopenharmony_ci    ProcessStackBuffer();
464209bc2fbSopenharmony_ci    return 0;
465209bc2fbSopenharmony_ci}
466209bc2fbSopenharmony_ci
467209bc2fbSopenharmony_civoid ThreadSampler::ResetConsumeInfo()
468209bc2fbSopenharmony_ci{
469209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
470209bc2fbSopenharmony_ci    sampleCount_ = 0;
471209bc2fbSopenharmony_ci    requestCount_ = 0;
472209bc2fbSopenharmony_ci    copyStackCount_ = 0;
473209bc2fbSopenharmony_ci    copyStackTimeCost_ = 0;
474209bc2fbSopenharmony_ci    processTimeCost_ = 0;
475209bc2fbSopenharmony_ci    processCount_ = 0;
476209bc2fbSopenharmony_ci    unwindCount_ = 0;
477209bc2fbSopenharmony_ci    unwindTimeCost_ = 0;
478209bc2fbSopenharmony_ci    signalTimeCost_ = 0;
479209bc2fbSopenharmony_ci#endif // #if defined(CONSUME_STATISTICS)
480209bc2fbSopenharmony_ci}
481209bc2fbSopenharmony_ci
482209bc2fbSopenharmony_cibool ThreadSampler::CollectStack(std::string& stack, bool treeFormat)
483209bc2fbSopenharmony_ci{
484209bc2fbSopenharmony_ci    ProcessStackBuffer();
485209bc2fbSopenharmony_ci
486209bc2fbSopenharmony_ci    if (!init_) {
487209bc2fbSopenharmony_ci        XCOLLIE_LOGE("sampler has not initialized.\n");
488209bc2fbSopenharmony_ci    }
489209bc2fbSopenharmony_ci
490209bc2fbSopenharmony_ci    stack.clear();
491209bc2fbSopenharmony_ci    if (timeAndFrameList_.empty() && stackIdCount_.empty()) {
492209bc2fbSopenharmony_ci        if (!LoadStringFromFile("/proc/self/wchan", stack)) {
493209bc2fbSopenharmony_ci            XCOLLIE_LOGE("read file failed.\n");
494209bc2fbSopenharmony_ci        }
495209bc2fbSopenharmony_ci        if (stack.empty()) {
496209bc2fbSopenharmony_ci            stack += "empty";
497209bc2fbSopenharmony_ci        }
498209bc2fbSopenharmony_ci        stack += "\n";
499209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
500209bc2fbSopenharmony_ci        ResetConsumeInfo();
501209bc2fbSopenharmony_ci#endif
502209bc2fbSopenharmony_ci        return false;
503209bc2fbSopenharmony_ci    }
504209bc2fbSopenharmony_ci
505209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
506209bc2fbSopenharmony_ci    uint64_t collectStart = GetCurrentTimeNanoseconds();
507209bc2fbSopenharmony_ci#endif
508209bc2fbSopenharmony_ci    auto printer = std::make_unique<SampleStackPrinter>(unwinder_, maps_);
509209bc2fbSopenharmony_ci    if (!treeFormat) {
510209bc2fbSopenharmony_ci        stack = printer->GetFullStack(timeAndFrameList_);
511209bc2fbSopenharmony_ci    } else {
512209bc2fbSopenharmony_ci        stack = printer->GetTreeStack(stackIdCount_, uniqueStackTable_);
513209bc2fbSopenharmony_ci    }
514209bc2fbSopenharmony_ci    timeAndFrameList_.clear();
515209bc2fbSopenharmony_ci    stackIdCount_.clear();
516209bc2fbSopenharmony_ci
517209bc2fbSopenharmony_ci#if defined(CONSUME_STATISTICS)
518209bc2fbSopenharmony_ci    uint64_t collectEnd = GetCurrentTimeNanoseconds();
519209bc2fbSopenharmony_ci    uint64_t elapse = collectEnd - collectStart;
520209bc2fbSopenharmony_ci    XCOLLIE_LOGI("Sample count:%{public}llu\nRequest count:%{public}llu\n\
521209bc2fbSopenharmony_ci        Snapshot count:%{public}llu\nAverage copy stack time:%{public}llu ns\n",
522209bc2fbSopenharmony_ci        (unsigned long long)sampleCount_, (unsigned long long)requestCount_,
523209bc2fbSopenharmony_ci        (unsigned long long)copyStackCount_, (unsigned long long)copyStackTimeCost_ / copyStackCount_);
524209bc2fbSopenharmony_ci    XCOLLIE_LOGI("Average process time:%{public}llu ns\n", (unsigned long long)processTimeCost_/processCount_);
525209bc2fbSopenharmony_ci    XCOLLIE_LOGI("Average unwind time:%{public}llu ns\n", (unsigned long long)unwindTimeCost_/unwindCount_);
526209bc2fbSopenharmony_ci    XCOLLIE_LOGI("FormatStack time:%{public}llu ns\n", (unsigned long long)elapse);
527209bc2fbSopenharmony_ci    ResetConsumeInfo();
528209bc2fbSopenharmony_ci#endif
529209bc2fbSopenharmony_ci    return true;
530209bc2fbSopenharmony_ci}
531209bc2fbSopenharmony_ci
532209bc2fbSopenharmony_cibool ThreadSampler::Deinit()
533209bc2fbSopenharmony_ci{
534209bc2fbSopenharmony_ci    if (!init_) {
535209bc2fbSopenharmony_ci        return true;
536209bc2fbSopenharmony_ci    }
537209bc2fbSopenharmony_ci    UninstallSignalHandler();
538209bc2fbSopenharmony_ci    struct timespec timeout;
539209bc2fbSopenharmony_ci    timeout.tv_sec = time(nullptr) + LOCK_TIMEOUT;
540209bc2fbSopenharmony_ci    timeout.tv_nsec = 0;
541209bc2fbSopenharmony_ci    int err = pthread_mutex_timedlock(&mutex_, &timeout);
542209bc2fbSopenharmony_ci    if (err != 0) {
543209bc2fbSopenharmony_ci        XCOLLIE_LOGE("Failed to get lock when deinit thread_sampler.\n");
544209bc2fbSopenharmony_ci        return false;
545209bc2fbSopenharmony_ci    }
546209bc2fbSopenharmony_ci    DeinitUniqueStackTable();
547209bc2fbSopenharmony_ci    DestroyUnwinder();
548209bc2fbSopenharmony_ci    ReleaseRecordBuffer();
549209bc2fbSopenharmony_ci    init_ = false;
550209bc2fbSopenharmony_ci    pthread_mutex_unlock(&mutex_);
551209bc2fbSopenharmony_ci    return !init_;
552209bc2fbSopenharmony_ci}
553209bc2fbSopenharmony_ci} // end of namespace HiviewDFX
554209bc2fbSopenharmony_ci} // end of namespace OHOS
555