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