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);