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 <sys/ptrace.h>
21#include <unwindstack/Maps.h>
22#include <unwindstack/Memory.h>
23#include <unwindstack/Regs.h>
24#include <unwindstack/Unwinder.h>
25#include "dfx_define.h"
26#include "dfx_log.h"
27#include "dfx_test_util.h"
28#include "MemoryRemote.h"
29#include "pid_utils.h"
30
31using namespace OHOS::HiviewDFX;
32using namespace std;
33
34static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5;
35static constexpr size_t MAX_FRAMES = 32;
36
37struct UnwindData {
38    bool isCache = false;
39    bool isFillFrames = false;
40};
41
42NOINLINE static void TestFunc6(MAYBE_UNUSED void (*func)(void*), MAYBE_UNUSED volatile bool* ready)
43{
44    *ready = true;
45    while (true) {};
46    DFXLOGE("Not be run here!!!");
47}
48
49NOINLINE static void TestFunc5(void (*func)(void*), volatile bool* ready)
50{
51    return TestFunc6(func, ready);
52}
53
54NOINLINE static void TestFunc4(void (*func)(void*), volatile bool* ready)
55{
56    return TestFunc5(func, ready);
57}
58
59NOINLINE static void TestFunc3(void (*func)(void*), volatile bool* ready)
60{
61    return TestFunc4(func, ready);
62}
63
64NOINLINE static void TestFunc2(void (*func)(void*), volatile bool* ready)
65{
66    return TestFunc3(func, ready);
67}
68
69NOINLINE static void TestFunc1(void (*func)(void*), volatile bool* ready)
70{
71    return TestFunc2(func, ready);
72}
73
74static bool WaitForRemote(pid_t pid, volatile bool* readyPtr)
75{
76    return PidUtils::WaitForPidState(pid, [pid, readyPtr]() {
77        unwindstack::MemoryRemote memory(pid);
78        bool ready;
79        uint64_t readyAddr = reinterpret_cast<uint64_t>(readyPtr);
80        if (memory.ReadFully(readyAddr, &ready, sizeof(ready)) && ready) {
81            return PidRunEnum::PID_RUN_PASS;
82        }
83        return PidRunEnum::PID_RUN_KEEP_GOING;
84    });
85}
86
87static pid_t RemoteFork()
88{
89    static volatile bool ready = false;
90
91    pid_t pid;
92    if ((pid = fork()) == 0) {
93        TestFunc1(nullptr, &ready);
94        _exit(0);
95    } else if (pid < 0) {
96        return -1;
97    }
98
99    if (!WaitForRemote(pid, &ready)) {
100        DFXLOGE("Failed to wait pid: %{public}d", pid);
101        TestScopedPidReaper::Kill(pid);
102        return -1;
103    }
104    return pid;
105}
106
107static size_t UnwindRemote(unwindstack::Unwinder unwinder, MAYBE_UNUSED UnwindData* dataPtr)
108{
109    if (dataPtr != nullptr) {
110        unwinder.SetResolveNames(dataPtr->isFillFrames);
111    }
112    unwinder.Unwind();
113    auto unwSize = unwinder.NumFrames();
114    DFXLOGU("%{public}s frames.size: %{public}zu", __func__, unwSize);
115    if (dataPtr != nullptr && dataPtr->isFillFrames) {
116        for (size_t i = 0; i < unwSize; ++i) {
117            auto str = unwinder.FormatFrame(i);
118            DFXLOGU("%{public}s frames: %{public}s", __func__, str.c_str());
119        }
120    }
121    return unwSize;
122}
123
124static void Run(benchmark::State& state, void* data)
125{
126    UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
127    UnwindData unwindData;
128    if (dataPtr != nullptr) {
129        unwindData.isCache = dataPtr->isCache;
130    }
131
132    pid_t pid = RemoteFork();
133    if (pid == -1) {
134        state.SkipWithError("Failed to fork remote process.");
135        return;
136    }
137    DFXLOGU("pid: %{public}d", pid);
138    TestScopedPidReaper reap(pid);
139
140    std::shared_ptr<unwindstack::Memory> processMemory;
141    if (unwindData.isCache) {
142        processMemory = unwindstack::Memory::CreateProcessMemoryCached(pid);
143    } else {
144        processMemory = unwindstack::Memory::CreateProcessMemory(pid);
145    }
146    unwindstack::RemoteMaps maps(pid);
147    if (!maps.Parse()) {
148        state.SkipWithError("Failed to parse maps.");
149    }
150
151    for (const auto& _ : state) {
152        std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid));
153        unwindstack::Unwinder unwinder(MAX_FRAMES, &maps, regs.get(), processMemory);
154        auto unwSize = UnwindRemote(unwinder, dataPtr);
155        if (unwSize < TEST_MIN_UNWIND_FRAMES) {
156            state.SkipWithError("Failed to unwind.");
157        }
158    }
159    DFXLOGU("Detach pid: %{public}d", pid);
160    ptrace(PTRACE_DETACH, pid, 0, 0);
161}
162
163/**
164* @tc.name: BenchmarkUnwindStackRemote
165* @tc.desc: UnwindStack remote
166* @tc.type: FUNC
167*/
168static void BenchmarkUnwindStackRemote(benchmark::State& state)
169{
170    UnwindData data;
171    data.isCache = false;
172    Run(state, &data);
173}
174BENCHMARK(BenchmarkUnwindStackRemote);
175
176/**
177* @tc.name: BenchmarkUnwindStackRemoteCache
178* @tc.desc: UnwindStack remote cache
179* @tc.type: FUNC
180*/
181static void BenchmarkUnwindStackRemoteCache(benchmark::State& state)
182{
183    UnwindData data;
184    data.isCache = true;
185    Run(state, &data);
186}
187BENCHMARK(BenchmarkUnwindStackRemoteCache);
188
189/**
190* @tc.name: BenchmarkUnwindStackRemoteFrames
191* @tc.desc: UnwindStack remote frames
192* @tc.type: FUNC
193*/
194static void BenchmarkUnwindStackRemoteFrames(benchmark::State& state)
195{
196    UnwindData data;
197    data.isCache = false;
198    data.isFillFrames = true;
199    Run(state, &data);
200}
201BENCHMARK(BenchmarkUnwindStackRemoteFrames);
202
203/**
204* @tc.name: BenchmarkUnwindStackRemoteFramesCache
205* @tc.desc: UnwindStack remote frames cache
206* @tc.type: FUNC
207*/
208static void BenchmarkUnwindStackRemoteFramesCache(benchmark::State& state)
209{
210    UnwindData data;
211    data.isCache = true;
212    data.isFillFrames = true;
213    Run(state, &data);
214}
215BENCHMARK(BenchmarkUnwindStackRemoteFramesCache);