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 <memory>
19#include <securec.h>
20#include <string>
21#include <vector>
22#include <unistd.h>
23#include <unwindstack/Elf.h>
24#include <unwindstack/Maps.h>
25#include <unwindstack/Memory.h>
26#include <unwindstack/Regs.h>
27#include <unwindstack/RegsGetLocal.h>
28#include <unwindstack/Unwinder.h>
29#include "dfx_define.h"
30#include "dfx_log.h"
31
32using namespace OHOS::HiviewDFX;
33using namespace std;
34
35static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5;
36static constexpr size_t MAX_FRAMES = 32;
37
38struct UnwindData {
39    std::shared_ptr<unwindstack::Memory>& processMemory;
40    unwindstack::Maps* maps;
41    bool isFillFrames = false;
42};
43
44NOINLINE static size_t TestFunc5(size_t (*func)(void*), void* data)
45{
46    return func(data);
47}
48
49NOINLINE static size_t TestFunc4(size_t (*func)(void*), void* data)
50{
51    return TestFunc5(func, data);
52}
53
54NOINLINE static size_t TestFunc3(size_t (*func)(void*), void* data)
55{
56    return TestFunc4(func, data);
57}
58
59NOINLINE static size_t TestFunc2(size_t (*func)(void*), void* data)
60{
61    return TestFunc3(func, data);
62}
63
64NOINLINE static size_t TestFunc1(size_t (*func)(void*), void* data)
65{
66    return TestFunc2(func, data);
67}
68
69static size_t UnwindLocal(MAYBE_UNUSED void* data)
70{
71    UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
72    if (dataPtr == nullptr) {
73        return 0;
74    }
75    std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
76    unwindstack::RegsGetLocal(regs.get());
77    unwindstack::Unwinder unwinder(MAX_FRAMES, dataPtr->maps, regs.get(), dataPtr->processMemory);
78    unwinder.SetResolveNames(dataPtr->isFillFrames);
79    unwinder.Unwind();
80    auto unwSize = unwinder.NumFrames();
81    DFXLOGU("%{public}s frames.size: %{public}zu", __func__, unwSize);
82    if (dataPtr->isFillFrames) {
83        for (size_t i = 0; i < unwSize; ++i) {
84            auto str = unwinder.FormatFrame(i);
85            DFXLOGU("%{public}s frames: %{public}s", __func__, str.c_str());
86        }
87    }
88    return unwSize;
89}
90
91static void Run(benchmark::State& state, size_t (*func)(void*), void* data)
92{
93    DFXLOGU("++++++pid: %{public}d", getpid());
94    for (const auto& _ : state) {
95        if (TestFunc1(func, data) < TEST_MIN_UNWIND_FRAMES) {
96            state.SkipWithError("Failed to unwind.");
97        }
98    }
99    DFXLOGU("------pid: %{public}d", getpid());
100}
101
102/**
103* @tc.name: BenchmarkUnwindStackLocal
104* @tc.desc: UnwindStack local
105* @tc.type: FUNC
106*/
107static void BenchmarkUnwindStackLocal(benchmark::State& state)
108{
109    auto processMemory = unwindstack::Memory::CreateProcessMemory(getpid());
110    unwindstack::LocalMaps maps;
111    if (!maps.Parse()) {
112        state.SkipWithError("Failed to parse local maps.");
113    }
114
115    UnwindData data = {.processMemory = processMemory, .maps = &maps, .isFillFrames = false};
116    Run(state, UnwindLocal, &data);
117}
118BENCHMARK(BenchmarkUnwindStackLocal);
119
120/**
121* @tc.name: BenchmarkUnwindStackLocalCache
122* @tc.desc: UnwindStack local cache
123* @tc.type: FUNC
124*/
125static void BenchmarkUnwindStackLocalCache(benchmark::State& state)
126{
127    auto processMemory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
128    unwindstack::LocalMaps maps;
129    if (!maps.Parse()) {
130        state.SkipWithError("Failed to parse local maps.");
131    }
132
133    UnwindData data = {.processMemory = processMemory, .maps = &maps, .isFillFrames = false};
134    Run(state, UnwindLocal, &data);
135}
136BENCHMARK(BenchmarkUnwindStackLocalCache);
137
138/**
139* @tc.name: BenchmarkUnwindStackLocalFrames
140* @tc.desc: UnwindStack local frames
141* @tc.type: FUNC
142*/
143static void BenchmarkUnwindStackLocalFrames(benchmark::State& state)
144{
145    auto processMemory = unwindstack::Memory::CreateProcessMemory(getpid());
146    unwindstack::LocalMaps maps;
147    if (!maps.Parse()) {
148        state.SkipWithError("Failed to parse local maps.");
149    }
150
151    UnwindData data = {.processMemory = processMemory, .maps = &maps, .isFillFrames = true};
152    Run(state, UnwindLocal, &data);
153}
154BENCHMARK(BenchmarkUnwindStackLocalFrames);
155
156/**
157* @tc.name: BenchmarkUnwindStackLocalFramesCache
158* @tc.desc: UnwindStack local frames cache
159* @tc.type: FUNC
160*/
161static void BenchmarkUnwindStackLocalFramesCache(benchmark::State& state)
162{
163    auto processMemory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
164    unwindstack::LocalMaps maps;
165    if (!maps.Parse()) {
166        state.SkipWithError("Failed to parse local maps.");
167    }
168
169    UnwindData data = {.processMemory = processMemory, .maps = &maps, .isFillFrames = true};
170    Run(state, UnwindLocal, &data);
171}
172BENCHMARK(BenchmarkUnwindStackLocalFramesCache);