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 "MemoryRemote.h"
26 #include "pid_utils.h"
27 #include "dfx_define.h"
28 #include "dfx_log.h"
29 #include "dfx_test_util.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 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 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 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 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 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 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 }
96 if (pid == -1) {
97 return -1;
98 }
99
100 if (!WaitForRemote(pid, &ready)) {
101 LOGE("Failed to wait pid: %d", pid);
102 TestScopedPidReaper::Kill(pid);
103 return -1;
104 }
105 return pid;
106 }
107
UnwindRemote(unwindstack::Unwinder unwinder,MAYBE_UNUSED UnwindData * dataPtr)108 static size_t UnwindRemote(unwindstack::Unwinder unwinder, MAYBE_UNUSED UnwindData* dataPtr)
109 {
110 if (dataPtr != nullptr) {
111 unwinder.SetResolveNames(dataPtr->isFillFrames);
112 }
113 unwinder.Unwind();
114 auto unwSize = unwinder.NumFrames();
115 LOGU("%s frames.size: %zu", __func__, unwSize);
116 if (dataPtr != nullptr && dataPtr->isFillFrames) {
117 for (size_t i = 0; i < unwSize; ++i) {
118 auto str = unwinder.FormatFrame(i);
119 LOGU("%s frames: %s", __func__, str.c_str());
120 }
121 }
122 return unwSize;
123 }
124
Run(benchmark::State & state,void * data)125 static void Run(benchmark::State& state, void* data)
126 {
127 UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
128 UnwindData unwindData;
129 if (dataPtr != nullptr) {
130 unwindData.isCache = dataPtr->isCache;
131 }
132
133 pid_t pid = RemoteFork();
134 if (pid == -1) {
135 state.SkipWithError("Failed to fork remote process.");
136 return;
137 }
138 LOGU("pid: %d", pid);
139 TestScopedPidReaper reap(pid);
140
141 std::shared_ptr<unwindstack::Memory> processMemory;
142 if (unwindData.isCache) {
143 processMemory = unwindstack::Memory::CreateProcessMemoryCached(pid);
144 } else {
145 processMemory = unwindstack::Memory::CreateProcessMemory(pid);
146 }
147 unwindstack::RemoteMaps maps(pid);
148 if (!maps.Parse()) {
149 state.SkipWithError("Failed to parse maps.");
150 }
151
152 for (const auto& _ : state) {
153 std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid));
154 unwindstack::Unwinder unwinder(MAX_FRAMES, &maps, regs.get(), processMemory);
155 auto unwSize = UnwindRemote(unwinder, dataPtr);
156 if (unwSize < TEST_MIN_UNWIND_FRAMES) {
157 state.SkipWithError("Failed to unwind.");
158 }
159 }
160 LOGU("Detach pid: %d", pid);
161 ptrace(PTRACE_DETACH, pid, 0, 0);
162 }
163
164 /**
165 * @tc.name: BenchmarkUnwindStackRemote
166 * @tc.desc: UnwindStack remote
167 * @tc.type: FUNC
168 */
BenchmarkUnwindStackRemote(benchmark::State & state)169 static void BenchmarkUnwindStackRemote(benchmark::State& state)
170 {
171 UnwindData data;
172 data.isCache = false;
173 Run(state, &data);
174 }
175 BENCHMARK(BenchmarkUnwindStackRemote);
176
177 /**
178 * @tc.name: BenchmarkUnwindStackRemoteCache
179 * @tc.desc: UnwindStack remote cache
180 * @tc.type: FUNC
181 */
BenchmarkUnwindStackRemoteCache(benchmark::State & state)182 static void BenchmarkUnwindStackRemoteCache(benchmark::State& state)
183 {
184 UnwindData data;
185 data.isCache = true;
186 Run(state, &data);
187 }
188 BENCHMARK(BenchmarkUnwindStackRemoteCache);
189
190 /**
191 * @tc.name: BenchmarkUnwindStackRemoteFrames
192 * @tc.desc: UnwindStack remote frames
193 * @tc.type: FUNC
194 */
BenchmarkUnwindStackRemoteFrames(benchmark::State & state)195 static void BenchmarkUnwindStackRemoteFrames(benchmark::State& state)
196 {
197 UnwindData data;
198 data.isCache = false;
199 data.isFillFrames = true;
200 Run(state, &data);
201 }
202 BENCHMARK(BenchmarkUnwindStackRemoteFrames);
203
204 /**
205 * @tc.name: BenchmarkUnwindStackRemoteFramesCache
206 * @tc.desc: UnwindStack remote frames cache
207 * @tc.type: FUNC
208 */
BenchmarkUnwindStackRemoteFramesCache(benchmark::State & state)209 static void BenchmarkUnwindStackRemoteFramesCache(benchmark::State& state)
210 {
211 UnwindData data;
212 data.isCache = true;
213 data.isFillFrames = true;
214 Run(state, &data);
215 }
216 BENCHMARK(BenchmarkUnwindStackRemoteFramesCache);