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 #include <string>
18 #include <vector>
19 #include <unistd.h>
20 #include "dfx_log.h"
21 #include "dfx_regs_get.h"
22 #include "dfx_regs_qut.h"
23 #include "dfx_test_util.h"
24 #include "fp_unwinder.h"
25 #include "unwinder.h"
26 #include "unwinder_config.h"
27
28 using namespace OHOS::HiviewDFX;
29 using namespace std;
30
31 #undef LOG_DOMAIN
32 #undef LOG_TAG
33 #define LOG_DOMAIN 0xD002D11
34 #define LOG_TAG "DfxUnwinderLocal"
35
36 static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5;
37
38 struct UnwindData {
39 std::shared_ptr<Unwinder> unwinder = nullptr;
40 bool isCache = false;
41 bool isFillFrames = false;
42 bool isFp = false;
43 };
44
TestFunc5(size_t (* func)(void *),void * data)45 NOINLINE static size_t TestFunc5(size_t (*func)(void*), void* data)
46 {
47 return func(data);
48 }
49
TestFunc4(size_t (* func)(void *),void * data)50 NOINLINE static size_t TestFunc4(size_t (*func)(void*), void* data)
51 {
52 return TestFunc5(func, data);
53 }
54
TestFunc3(size_t (* func)(void *),void * data)55 NOINLINE static size_t TestFunc3(size_t (*func)(void*), void* data)
56 {
57 return TestFunc4(func, data);
58 }
59
TestFunc2(size_t (* func)(void *),void * data)60 NOINLINE static size_t TestFunc2(size_t (*func)(void*), void* data)
61 {
62 return TestFunc3(func, data);
63 }
64
TestFunc1(size_t (* func)(void *),void * data)65 NOINLINE static size_t TestFunc1(size_t (*func)(void*), void* data)
66 {
67 return TestFunc2(func, data);
68 }
69
GetUnwinder(void * data,std::shared_ptr<Unwinder> & unwinder,bool & isFp)70 static bool GetUnwinder(void* data, std::shared_ptr<Unwinder>& unwinder, bool& isFp)
71 {
72 UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
73 if ((dataPtr != nullptr) && (dataPtr->unwinder != nullptr)) {
74 unwinder = dataPtr->unwinder;
75 unwinder->EnableFillFrames(dataPtr->isFillFrames);
76 unwinder->EnableUnwindCache(dataPtr->isCache);
77 isFp = dataPtr->isFp;
78 return true;
79 }
80 LOGE("Failed to get unwinder");
81 return false;
82 }
83
UnwinderLocal(MAYBE_UNUSED void * data)84 static size_t UnwinderLocal(MAYBE_UNUSED void* data)
85 {
86 std::shared_ptr<Unwinder> unwinder = nullptr;
87 bool isFp = false;
88 if (!GetUnwinder(data, unwinder, isFp)) {
89 return 0;
90 }
91 MAYBE_UNUSED bool unwRet = unwinder->UnwindLocal(false, isFp);
92 size_t unwSize = 0;
93 if (isFp) {
94 unwSize = unwinder->GetPcs().size();
95 } else {
96 unwSize = unwinder->GetFrames().size();
97 }
98 LOGU("%s frames.size: %zu", __func__, unwSize);
99 return unwSize;
100 }
101
102 #if defined(__aarch64__)
UnwinderLocalFp(MAYBE_UNUSED void * data)103 static size_t UnwinderLocalFp(MAYBE_UNUSED void* data) {
104 UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
105 if ((dataPtr == nullptr) || (dataPtr->unwinder == nullptr)) {
106 return 0;
107 }
108
109 uintptr_t stackBottom = 1, stackTop = static_cast<uintptr_t>(-1);
110 if (!dataPtr->unwinder->GetStackRange(stackBottom, stackTop)) {
111 return 0;
112 }
113 uintptr_t miniRegs[FP_MINI_REGS_SIZE] = {0};
114 GetFramePointerMiniRegs(miniRegs, sizeof(miniRegs) / sizeof(miniRegs[0]));
115 auto regs = DfxRegs::CreateFromRegs(UnwindMode::FRAMEPOINTER_UNWIND, miniRegs,
116 sizeof(miniRegs) / sizeof(miniRegs[0]));
117 dataPtr->unwinder->SetRegs(regs);
118 UnwindContext context;
119 context.pid = UNWIND_TYPE_LOCAL;
120 context.regs = regs;
121 context.stackCheck = false;
122 context.stackBottom = stackBottom;
123 context.stackTop = stackTop;
124 dataPtr->unwinder->EnableFpCheckMapExec(false);
125 dataPtr->unwinder->UnwindByFp(&context);
126 auto unwSize = dataPtr->unwinder->GetPcs().size();
127 LOGU("%s frames.size: %zu", __func__, unwSize);
128
129 if (dataPtr->isFillFrames) {
130 auto& pcs = dataPtr->unwinder->GetPcs();
131 std::vector<DfxFrame> frames;
132 for (size_t i = 0; i < unwSize; ++i) {
133 DfxFrame frame;
134 frame.index = i;
135 frame.pc = static_cast<uint64_t>(pcs[i]);
136 frames.emplace_back(frame);
137 }
138 Unwinder::FillLocalFrames(frames);
139 LOGU("%s", Unwinder::GetFramesStr(frames).c_str());
140 }
141 return unwSize;
142 }
143
FpUnwinderLocal(MAYBE_UNUSED void * data)144 static size_t FpUnwinderLocal(MAYBE_UNUSED void* data) {
145 uintptr_t regs[2]; // 2: pc and fp reg
146 FpUnwinder::GetPcFpRegs(regs);
147 const size_t maxSize = 32;
148 uintptr_t pcs[maxSize] = {0};
149 auto unwSize = FpUnwinder::GetPtr()->Unwind(regs[0], regs[1], pcs, maxSize);
150 LOGU("%s frames.size: %zu", __func__, unwSize);
151
152 UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
153 if (dataPtr != nullptr && dataPtr->isFillFrames) {
154 std::vector<DfxFrame> frames;
155 for (auto i = 0; i < unwSize; ++i) {
156 DfxFrame frame;
157 frame.index = i;
158 frame.pc = static_cast<uint64_t>(pcs[i]);
159 frames.emplace_back(frame);
160 }
161 Unwinder::FillLocalFrames(frames);
162 LOGU("%s", Unwinder::GetFramesStr(frames).c_str());
163 }
164 return unwSize;
165 }
166
FpUnwinderLocalSafe(MAYBE_UNUSED void * data)167 static size_t FpUnwinderLocalSafe(MAYBE_UNUSED void* data) {
168 uintptr_t regs[2]; // 2: pc and fp reg
169 FpUnwinder::GetPcFpRegs(regs);
170 const size_t maxSize = 32;
171 uintptr_t pcs[maxSize] = {0};
172 auto unwSize = FpUnwinder::GetPtr()->UnwindSafe(regs[0], regs[1], pcs, maxSize);
173 LOGU("%s frames.size: %zu", __func__, unwSize);
174
175 UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
176 if (dataPtr != nullptr && dataPtr->isFillFrames) {
177 std::vector<DfxFrame> frames;
178 for (auto i = 0; i < unwSize; ++i) {
179 DfxFrame frame;
180 frame.index = i;
181 frame.pc = static_cast<uint64_t>(pcs[i]);
182 frames.emplace_back(frame);
183 }
184 Unwinder::FillLocalFrames(frames);
185 LOGU("%s", Unwinder::GetFramesStr(frames).c_str());
186 }
187 return unwSize;
188 }
189 #endif
190
Run(benchmark::State & state,size_t (* func)(void *),void * data)191 static void Run(benchmark::State& state, size_t (*func)(void*), void* data)
192 {
193 LOGU("++++++pid: %d", getpid());
194 for (const auto& _ : state) {
195 if (TestFunc1(func, data) < TEST_MIN_UNWIND_FRAMES) {
196 state.SkipWithError("Failed to unwind.");
197 }
198 }
199 LOGU("------pid: %d", getpid());
200 }
201
202 /**
203 * @tc.name: BenchmarkUnwinderLocalFull
204 * @tc.desc: Unwind local full
205 * @tc.type: FUNC
206 */
BenchmarkUnwinderLocalFull(benchmark::State & state)207 static void BenchmarkUnwinderLocalFull(benchmark::State& state)
208 {
209 std::vector<uint16_t> qutRegs;
210 for (uint16_t i = REG_EH; i < REG_LAST; ++i) {
211 qutRegs.emplace_back(i);
212 }
213 DfxRegsQut::SetQutRegs(qutRegs);
214 UnwindData data;
215 data.unwinder = std::make_shared<Unwinder>();
216 data.isCache = false;
217 data.isFillFrames = false;
218 Run(state, UnwinderLocal, &data);
219 }
220 BENCHMARK(BenchmarkUnwinderLocalFull);
221
222 /**
223 * @tc.name: BenchmarkUnwinderLocalQut
224 * @tc.desc: Unwind local qut
225 * @tc.type: FUNC
226 */
BenchmarkUnwinderLocalQut(benchmark::State & state)227 static void BenchmarkUnwinderLocalQut(benchmark::State& state)
228 {
229 DfxRegsQut::SetQutRegs(QUT_REGS);
230 UnwindData data;
231 data.unwinder = std::make_shared<Unwinder>();
232 data.isCache = false;
233 data.isFillFrames = false;
234 Run(state, UnwinderLocal, &data);
235 }
236 BENCHMARK(BenchmarkUnwinderLocalQut);
237
238 /**
239 * @tc.name: BenchmarkUnwinderLocalQutCache
240 * @tc.desc: Unwind local qut cache
241 * @tc.type: FUNC
242 */
BenchmarkUnwinderLocalQutCache(benchmark::State & state)243 static void BenchmarkUnwinderLocalQutCache(benchmark::State& state)
244 {
245 DfxRegsQut::SetQutRegs(QUT_REGS);
246 UnwindData data;
247 data.unwinder = std::make_shared<Unwinder>();
248 data.isCache = true;
249 data.isFillFrames = false;
250 Run(state, UnwinderLocal, &data);
251 }
252 BENCHMARK(BenchmarkUnwinderLocalQutCache);
253
254 /**
255 * @tc.name: BenchmarkUnwinderLocalQutFrames
256 * @tc.desc: Unwind local qut frames
257 * @tc.type: FUNC
258 */
BenchmarkUnwinderLocalQutFrames(benchmark::State & state)259 static void BenchmarkUnwinderLocalQutFrames(benchmark::State& state)
260 {
261 DfxRegsQut::SetQutRegs(QUT_REGS);
262 UnwindData data;
263 data.unwinder = std::make_shared<Unwinder>();
264 data.isCache = false;
265 data.isFillFrames = true;
266 Run(state, UnwinderLocal, &data);
267 }
268 BENCHMARK(BenchmarkUnwinderLocalQutFrames);
269
270 /**
271 * @tc.name: BenchmarkUnwinderLocalQutFramesCache
272 * @tc.desc: Unwind local qut Frames cache
273 * @tc.type: FUNC
274 */
BenchmarkUnwinderLocalQutFramesCache(benchmark::State & state)275 static void BenchmarkUnwinderLocalQutFramesCache(benchmark::State& state)
276 {
277 DfxRegsQut::SetQutRegs(QUT_REGS);
278 UnwindData data;
279 data.unwinder = std::make_shared<Unwinder>();
280 data.isCache = true;
281 data.isFillFrames = true;
282 Run(state, UnwinderLocal, &data);
283 }
284 BENCHMARK(BenchmarkUnwinderLocalQutFramesCache);
285
286 /**
287 * @tc.name: BenchmarkUnwinderLocalQutMiniDebugInfos
288 * @tc.desc: Unwind local qut minidebuginfo
289 * @tc.type: FUNC
290 */
BenchmarkUnwinderLocalQutMiniDebugInfos(benchmark::State & state)291 static void BenchmarkUnwinderLocalQutMiniDebugInfos(benchmark::State& state)
292 {
293 DfxRegsQut::SetQutRegs(QUT_REGS);
294 UnwinderConfig::SetEnableMiniDebugInfo(true);
295 UnwindData data;
296 data.unwinder = std::make_shared<Unwinder>();
297 data.isCache = true;
298 data.isFillFrames = true;
299 Run(state, UnwinderLocal, &data);
300 UnwinderConfig::SetEnableMiniDebugInfo(false);
301 }
302 BENCHMARK(BenchmarkUnwinderLocalQutMiniDebugInfos);
303
304 /**
305 * @tc.name: BenchmarkUnwinderLocalQutMiniDebugInfosLazily
306 * @tc.desc: Unwind local qut minidebuginfo lazily
307 * @tc.type: FUNC
308 */
BenchmarkUnwinderLocalQutMiniDebugInfosLazily(benchmark::State & state)309 static void BenchmarkUnwinderLocalQutMiniDebugInfosLazily(benchmark::State& state)
310 {
311 DfxRegsQut::SetQutRegs(QUT_REGS);
312 UnwinderConfig::SetEnableMiniDebugInfo(true);
313 UnwinderConfig::SetEnableLoadSymbolLazily(true);
314 UnwindData data;
315 data.unwinder = std::make_shared<Unwinder>();
316 data.isCache = true;
317 data.isFillFrames = true;
318 Run(state, UnwinderLocal, &data);
319 UnwinderConfig::SetEnableMiniDebugInfo(false);
320 UnwinderConfig::SetEnableLoadSymbolLazily(false);
321 }
322 BENCHMARK(BenchmarkUnwinderLocalQutMiniDebugInfosLazily);
323
324 #if defined(__aarch64__)
325 /**
326 * @tc.name: BenchmarkUnwinderLocalByFp
327 * @tc.desc: Unwind local by fp
328 * @tc.type: FUNC
329 */
BenchmarkUnwinderLocalByFp(benchmark::State & state)330 static void BenchmarkUnwinderLocalByFp(benchmark::State& state)
331 {
332 DfxRegsQut::SetQutRegs(QUT_REGS);
333 UnwindData data;
334 data.unwinder = std::make_shared<Unwinder>(false);
335 data.isCache = true;
336 data.isFillFrames = false;
337 data.isFp = true;
338 Run(state, UnwinderLocal, &data);
339 }
340 BENCHMARK(BenchmarkUnwinderLocalByFp);
341
342 /**
343 * @tc.name: BenchmarkUnwinderLocalFp
344 * @tc.desc: Unwind local fp
345 * @tc.type: FUNC
346 */
BenchmarkUnwinderLocalFp(benchmark::State & state)347 static void BenchmarkUnwinderLocalFp(benchmark::State& state)
348 {
349 UnwindData data;
350 data.unwinder = std::make_shared<Unwinder>(false);
351 data.isFp = true;
352 data.isFillFrames = false;
353 Run(state, UnwinderLocalFp, &data);
354 }
355 BENCHMARK(BenchmarkUnwinderLocalFp);
356
357 /**
358 * @tc.name: BenchmarkUnwinderLocalFpFrames
359 * @tc.desc: Unwind local fp Frames
360 * @tc.type: FUNC
361 */
BenchmarkUnwinderLocalFpFrames(benchmark::State & state)362 static void BenchmarkUnwinderLocalFpFrames(benchmark::State& state)
363 {
364 UnwindData data;
365 data.unwinder = std::make_shared<Unwinder>(false);
366 data.isFp = true;
367 data.isFillFrames = true;
368 Run(state, UnwinderLocalFp, &data);
369 }
370 BENCHMARK(BenchmarkUnwinderLocalFpFrames);
371
372 /**
373 * @tc.name: BenchmarkFpUnwinderLocal
374 * @tc.desc: FpUnwinder Unwind
375 * @tc.type: FUNC
376 */
BenchmarkFpUnwinderLocal(benchmark::State & state)377 static void BenchmarkFpUnwinderLocal(benchmark::State& state)
378 {
379 UnwindData data;
380 data.isFillFrames = false;
381 Run(state, FpUnwinderLocal, &data);
382 }
383 BENCHMARK(BenchmarkFpUnwinderLocal);
384
385 /**
386 * @tc.name: BenchmarkFpUnwinderLocalFrames
387 * @tc.desc: FpUnwinder Unwind Frames
388 * @tc.type: FUNC
389 */
BenchmarkFpUnwinderLocalFrames(benchmark::State & state)390 static void BenchmarkFpUnwinderLocalFrames(benchmark::State& state)
391 {
392 UnwindData data;
393 data.isFillFrames = true;
394 Run(state, FpUnwinderLocal, &data);
395 }
396 BENCHMARK(BenchmarkFpUnwinderLocalFrames);
397
398 /**
399 * @tc.name: BenchmarkFpUnwinderLocalSafe
400 * @tc.desc: FpUnwinder UnwindSafe
401 * @tc.type: FUNC
402 */
BenchmarkFpUnwinderLocalSafe(benchmark::State & state)403 static void BenchmarkFpUnwinderLocalSafe(benchmark::State& state)
404 {
405 UnwindData data;
406 data.isFillFrames = false;
407 Run(state, FpUnwinderLocalSafe, &data);
408 }
409 BENCHMARK(BenchmarkFpUnwinderLocalSafe);
410
411 /**
412 * @tc.name: BenchmarkFpUnwinderLocalSafeFrames
413 * @tc.desc: FpUnwinder UnwindSafe Frames
414 * @tc.type: FUNC
415 */
BenchmarkFpUnwinderLocalSafeFrames(benchmark::State & state)416 static void BenchmarkFpUnwinderLocalSafeFrames(benchmark::State& state)
417 {
418 UnwindData data;
419 data.isFillFrames = true;
420 Run(state, FpUnwinderLocalSafe, &data);
421 }
422 BENCHMARK(BenchmarkFpUnwinderLocalSafeFrames);
423 #endif