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 
31 using namespace OHOS::HiviewDFX;
32 using namespace std;
33 
34 static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5;
35 static constexpr size_t MAX_FRAMES = 32;
36 
37 struct UnwindData {
38     bool isCache = false;
39     bool isFillFrames = false;
40 };
41 
TestFunc6(MAYBE_UNUSED void (* func)(void *),MAYBE_UNUSED volatile bool * ready)42 NOINLINE static void TestFunc6(MAYBE_UNUSED void (*func)(void*), MAYBE_UNUSED volatile bool* ready)
43 {
44     *ready = true;
45     while (true) {};
46     LOGE("Not be run here!!!");
47 }
48 
TestFunc5(void (* func)(void *),volatile bool * ready)49 NOINLINE static void TestFunc5(void (*func)(void*), volatile bool* ready)
50 {
51     return TestFunc6(func, ready);
52 }
53 
TestFunc4(void (* func)(void *),volatile bool * ready)54 NOINLINE static void TestFunc4(void (*func)(void*), volatile bool* ready)
55 {
56     return TestFunc5(func, ready);
57 }
58 
TestFunc3(void (* func)(void *),volatile bool * ready)59 NOINLINE static void TestFunc3(void (*func)(void*), volatile bool* ready)
60 {
61     return TestFunc4(func, ready);
62 }
63 
TestFunc2(void (* func)(void *),volatile bool * ready)64 NOINLINE static void TestFunc2(void (*func)(void*), volatile bool* ready)
65 {
66     return TestFunc3(func, ready);
67 }
68 
TestFunc1(void (* func)(void *),volatile bool * ready)69 NOINLINE static void TestFunc1(void (*func)(void*), volatile bool* ready)
70 {
71     return TestFunc2(func, ready);
72 }
73 
WaitForRemote(pid_t pid,volatile bool * readyPtr)74 static 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 
RemoteFork()87 static 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         LOGE("Failed to wait pid: %d", pid);
101         TestScopedPidReaper::Kill(pid);
102         return -1;
103     }
104     return pid;
105 }
106 
UnwindRemote(unwindstack::Unwinder unwinder,MAYBE_UNUSED UnwindData * dataPtr)107 static 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     LOGU("%s frames.size: %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             LOGU("%s frames: %s", __func__, str.c_str());
119         }
120     }
121     return unwSize;
122 }
123 
Run(benchmark::State & state,void * data)124 static 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     LOGU("pid: %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     LOGU("Detach pid: %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 */
BenchmarkUnwindStackRemote(benchmark::State & state)168 static void BenchmarkUnwindStackRemote(benchmark::State& state)
169 {
170     UnwindData data;
171     data.isCache = false;
172     Run(state, &data);
173 }
174 BENCHMARK(BenchmarkUnwindStackRemote);
175 
176 /**
177 * @tc.name: BenchmarkUnwindStackRemoteCache
178 * @tc.desc: UnwindStack remote cache
179 * @tc.type: FUNC
180 */
BenchmarkUnwindStackRemoteCache(benchmark::State & state)181 static void BenchmarkUnwindStackRemoteCache(benchmark::State& state)
182 {
183     UnwindData data;
184     data.isCache = true;
185     Run(state, &data);
186 }
187 BENCHMARK(BenchmarkUnwindStackRemoteCache);
188 
189 /**
190 * @tc.name: BenchmarkUnwindStackRemoteFrames
191 * @tc.desc: UnwindStack remote frames
192 * @tc.type: FUNC
193 */
BenchmarkUnwindStackRemoteFrames(benchmark::State & state)194 static void BenchmarkUnwindStackRemoteFrames(benchmark::State& state)
195 {
196     UnwindData data;
197     data.isCache = false;
198     data.isFillFrames = true;
199     Run(state, &data);
200 }
201 BENCHMARK(BenchmarkUnwindStackRemoteFrames);
202 
203 /**
204 * @tc.name: BenchmarkUnwindStackRemoteFramesCache
205 * @tc.desc: UnwindStack remote frames cache
206 * @tc.type: FUNC
207 */
BenchmarkUnwindStackRemoteFramesCache(benchmark::State & state)208 static void BenchmarkUnwindStackRemoteFramesCache(benchmark::State& state)
209 {
210     UnwindData data;
211     data.isCache = true;
212     data.isFillFrames = true;
213     Run(state, &data);
214 }
215 BENCHMARK(BenchmarkUnwindStackRemoteFramesCache);