1/*
2 * Copyright (c) 2023 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 <benchmark/benchmark.h>
17
18#include <string>
19#include <vector>
20#include "dfx_log.h"
21#include "dfx_ptrace.h"
22#include "dfx_regs_qut.h"
23#include "dfx_test_util.h"
24#include "unwinder.h"
25#include "unwinder_config.h"
26
27using namespace OHOS::HiviewDFX;
28using namespace std;
29
30#undef LOG_DOMAIN
31#undef LOG_TAG
32#define LOG_DOMAIN 0xD002D11
33#define LOG_TAG "DfxUnwinderRemote"
34
35static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5;
36
37struct UnwindData {
38    bool isCache = false;
39    bool isFillFrames = false;
40    bool isFp = false;
41};
42
43NOINLINE static void TestFunc6(MAYBE_UNUSED void (*func)(void*), MAYBE_UNUSED void* data)
44{
45    while (true) {};
46    DFXLOGE("Not be run here!!!");
47}
48
49NOINLINE static void TestFunc5(void (*func)(void*), void* data)
50{
51    return TestFunc6(func, data);
52}
53
54NOINLINE static void TestFunc4(void (*func)(void*), void* data)
55{
56    return TestFunc5(func, data);
57}
58
59NOINLINE static void TestFunc3(void (*func)(void*), void* data)
60{
61    return TestFunc4(func, data);
62}
63
64NOINLINE static void TestFunc2(void (*func)(void*), void* data)
65{
66    return TestFunc3(func, data);
67}
68
69NOINLINE static void TestFunc1(void (*func)(void*), void* data)
70{
71    return TestFunc2(func, data);
72}
73
74static size_t UnwinderRemote(std::shared_ptr<Unwinder> unwinder, const pid_t tid)
75{
76    if (unwinder == nullptr) {
77        return 0;
78    }
79    MAYBE_UNUSED bool unwRet = unwinder->UnwindRemote(tid);
80    auto frames = unwinder->GetFrames();
81    DFXLOGU("%{public}s frames.size: %{public}zu", __func__, frames.size());
82    return frames.size();
83}
84
85static size_t UnwinderRemoteFp(std::shared_ptr<Unwinder> unwinder, const pid_t tid)
86{
87    if (unwinder == nullptr) {
88        return 0;
89    }
90    auto regs = DfxRegs::CreateRemoteRegs(tid);
91    unwinder->SetRegs(regs);
92    UnwindContext context;
93    context.pid = tid;
94    unwinder->EnableFpCheckMapExec(false);
95    unwinder->UnwindByFp(&context);
96    auto frames = unwinder->GetPcs();
97    DFXLOGU("%{public}s frames.size: %{public}zu", __func__, frames.size());
98    return frames.size();
99}
100
101static bool GetUnwinder(pid_t pid, void* data, std::shared_ptr<Unwinder>& unwinder, bool& isFp)
102{
103    static std::unordered_map<pid_t, std::shared_ptr<Unwinder>> unwinders_;
104    auto iter = unwinders_.find(pid);
105    if (iter != unwinders_.end()) {
106        unwinder = iter->second;
107    } else {
108        unwinder = std::make_shared<Unwinder>(pid);
109        unwinders_[pid] = unwinder;
110    }
111
112    UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
113    if ((dataPtr != nullptr) && (unwinder != nullptr)) {
114        unwinder->EnableFillFrames(dataPtr->isFillFrames);
115        unwinder->EnableUnwindCache(dataPtr->isCache);
116        isFp = dataPtr->isFp;
117    }
118    return true;
119}
120
121static void Run(benchmark::State& state, void* data)
122{
123    pid_t pid = fork();
124    if (pid == 0) {
125        TestFunc1(nullptr, nullptr);
126        _exit(0);
127    } else if (pid < 0) {
128        return;
129    }
130    if (!DfxPtrace::Attach(pid)) {
131        DFXLOGE("Failed to attach pid: %{public}d", pid);
132        TestScopedPidReaper::Kill(pid);
133        return;
134    }
135    DFXLOGU("pid: %{public}d", pid);
136    TestScopedPidReaper reap(pid);
137
138    std::shared_ptr<Unwinder> unwinder = nullptr;
139    bool isFp = false;
140    if (!GetUnwinder(pid, data, unwinder, isFp) || (unwinder == nullptr)) {
141        state.SkipWithError("Failed to get unwinder.");
142        return;
143    }
144
145    for (const auto& _ : state) {
146        size_t unwSize = 0;
147        if (isFp) {
148            unwSize = UnwinderRemoteFp(unwinder, pid);
149        } else {
150            unwSize = UnwinderRemote(unwinder, pid);
151        }
152
153        if (unwSize < TEST_MIN_UNWIND_FRAMES) {
154            state.SkipWithError("Failed to unwind.");
155        }
156    }
157    DFXLOGU("Detach pid: %{public}d", pid);
158    DfxPtrace::Detach(pid);
159}
160
161/**
162* @tc.name: BenchmarkUnwinderRemoteFull
163* @tc.desc: Unwind remote full
164* @tc.type: FUNC
165*/
166static void BenchmarkUnwinderRemoteFull(benchmark::State& state)
167{
168    std::vector<uint16_t> qutRegs;
169    for (uint16_t i = REG_EH; i < REG_LAST; ++i) {
170        qutRegs.emplace_back(i);
171    }
172    DfxRegsQut::SetQutRegs(qutRegs);
173    UnwindData data;
174    data.isCache = false;
175    data.isFillFrames = false;
176    Run(state, &data);
177}
178BENCHMARK(BenchmarkUnwinderRemoteFull);
179
180/**
181* @tc.name: BenchmarkUnwinderRemoteQut
182* @tc.desc: Unwind remote qut
183* @tc.type: FUNC
184*/
185static void BenchmarkUnwinderRemoteQut(benchmark::State& state)
186{
187    DfxRegsQut::SetQutRegs(QUT_REGS);
188    UnwindData data;
189    data.isCache = false;
190    data.isFillFrames = false;
191    Run(state, &data);
192}
193BENCHMARK(BenchmarkUnwinderRemoteQut);
194
195/**
196* @tc.name: BenchmarkUnwinderRemoteQutCache
197* @tc.desc: Unwind remote qut cache
198* @tc.type: FUNC
199*/
200static void BenchmarkUnwinderRemoteQutCache(benchmark::State& state)
201{
202    DfxRegsQut::SetQutRegs(QUT_REGS);
203    UnwindData data;
204    data.isCache = true;
205    data.isFillFrames = false;
206    Run(state, &data);
207}
208BENCHMARK(BenchmarkUnwinderRemoteQutCache);
209
210/**
211* @tc.name: BenchmarkUnwinderRemoteQutFrames
212* @tc.desc: Unwind remote qut frames
213* @tc.type: FUNC
214*/
215static void BenchmarkUnwinderRemoteQutFrames(benchmark::State& state)
216{
217    DfxRegsQut::SetQutRegs(QUT_REGS);
218    UnwindData data;
219    data.isCache = false;
220    data.isFillFrames = true;
221    Run(state, &data);
222}
223BENCHMARK(BenchmarkUnwinderRemoteQutFrames);
224
225/**
226* @tc.name: BenchmarkUnwinderRemoteQutFramesCache
227* @tc.desc: Unwind remote qut frames cache
228* @tc.type: FUNC
229*/
230static void BenchmarkUnwinderRemoteQutFramesCache(benchmark::State& state)
231{
232    DfxRegsQut::SetQutRegs(QUT_REGS);
233    UnwindData data;
234    data.isCache = true;
235    data.isFillFrames = true;
236    Run(state, &data);
237}
238BENCHMARK(BenchmarkUnwinderRemoteQutFramesCache);
239
240/**
241* @tc.name: BenchmarkUnwinderRemoteQutMiniDebugInfos
242* @tc.desc: Unwind remote qut minidebuginfo
243* @tc.type: FUNC
244*/
245static void BenchmarkUnwinderRemoteQutMiniDebugInfos(benchmark::State& state)
246{
247    DfxRegsQut::SetQutRegs(QUT_REGS);
248    UnwinderConfig::SetEnableMiniDebugInfo(true);
249    UnwindData data;
250    data.isCache = false;
251    data.isFillFrames = true;
252    Run(state, &data);
253    UnwinderConfig::SetEnableMiniDebugInfo(false);
254}
255BENCHMARK(BenchmarkUnwinderRemoteQutMiniDebugInfos);
256
257/**
258* @tc.name: BenchmarkUnwinderRemoteQutMiniDebugInfosLazily
259* @tc.desc: Unwind remote qut minidebuginfo lazily
260* @tc.type: FUNC
261*/
262static void BenchmarkUnwinderRemoteQutMiniDebugInfosLazily(benchmark::State& state)
263{
264    DfxRegsQut::SetQutRegs(QUT_REGS);
265    UnwinderConfig::SetEnableMiniDebugInfo(true);
266    UnwinderConfig::SetEnableLoadSymbolLazily(true);
267    UnwindData data;
268    data.isCache = false;
269    data.isFillFrames = true;
270    Run(state, &data);
271    UnwinderConfig::SetEnableMiniDebugInfo(false);
272    UnwinderConfig::SetEnableLoadSymbolLazily(false);
273}
274BENCHMARK(BenchmarkUnwinderRemoteQutMiniDebugInfosLazily);
275
276#if defined(__aarch64__)
277/**
278* @tc.name: BenchmarkUnwinderRemoteFp
279* @tc.desc: Unwind remote fp
280* @tc.type: FUNC
281*/
282static void BenchmarkUnwinderRemoteFp(benchmark::State& state)
283{
284    UnwindData data;
285    data.isFp = true;
286    Run(state, &data);
287}
288BENCHMARK(BenchmarkUnwinderRemoteFp);
289#endif