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 <unistd.h>
20#include <vector>
21#include "dfx_define.h"
22#include "dfx_log.h"
23#include <libunwind.h>
24#include <libunwind-ptrace.h>
25#include "dfx_ptrace.h"
26#include "dfx_test_util.h"
27
28using namespace OHOS::HiviewDFX;
29using namespace std;
30
31static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5;
32
33NOINLINE static void TestFunc6(MAYBE_UNUSED void (*func)(void*))
34{
35    while (true) {};
36    DFXLOGE("Not be run here!!!");
37}
38
39NOINLINE static void TestFunc5(void (*func)(void*))
40{
41    return TestFunc6(func);
42}
43
44NOINLINE static void TestFunc4(void (*func)(void*))
45{
46    return TestFunc5(func);
47}
48
49NOINLINE static void TestFunc3(void (*func)(void*))
50{
51    return TestFunc4(func);
52}
53
54NOINLINE static void TestFunc2(void (*func)(void*))
55{
56    return TestFunc3(func);
57}
58
59NOINLINE static void TestFunc1(void (*func)(void*))
60{
61    return TestFunc2(func);
62}
63
64static size_t UnwindRemote(pid_t pid, unw_addr_space_t as)
65{
66    if (as == nullptr) {
67        DFXLOGE("as is nullptr");
68        return 0;
69    }
70    unw_set_caching_policy(as, UNW_CACHE_GLOBAL);
71    void *context = _UPT_create(pid);
72    if (context == nullptr) {
73        DFXLOGE("_UPT_create");
74        return 0;
75    }
76
77    unw_cursor_t cursor;
78    int unwRet = unw_init_remote(&cursor, as, context);
79    if (unwRet != 0) {
80        DFXLOGE("unw_init_remote");
81        _UPT_destroy(context);
82        return 0;
83    }
84
85    std::vector<unw_word_t> pcs;
86    size_t index = 0;
87    unw_word_t pc = 0;
88    unw_word_t prevPc;
89    do {
90        if ((unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t*)(&(pc)))) || (prevPc == pc)) {
91            break;
92        }
93        pcs.emplace_back(pc);
94        prevPc = pc;
95        index++;
96    } while (unw_step(&cursor) > 0);
97    _UPT_destroy(context);
98    DFXLOGU("%{public}s pcs.size: %{public}zu", __func__, pcs.size());
99    return pcs.size();
100}
101
102static void Run(benchmark::State& state)
103{
104    pid_t pid = fork();
105    if (pid == 0) {
106        TestFunc1(nullptr);
107        _exit(0);
108    } else if (pid < 0) {
109        return;
110    }
111    if (!DfxPtrace::Attach(pid)) {
112        DFXLOGE("Failed to attach pid: %{public}d", pid);
113        TestScopedPidReaper::Kill(pid);
114        return;
115    }
116
117    DFXLOGU("pid: %{public}d", pid);
118    TestScopedPidReaper reap(pid);
119
120    for (const auto& _ : state) {
121        unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, 0);
122        auto unwSize = UnwindRemote(pid, as);
123        if (unwSize < TEST_MIN_UNWIND_FRAMES) {
124            state.SkipWithError("Failed to unwind.");
125        }
126        unw_destroy_addr_space(as);
127        as = nullptr;
128    }
129    DFXLOGU("Detach pid: %{public}d", pid);
130    DfxPtrace::Detach(pid);
131}
132
133static void GetCacheUnwind(pid_t pid, unw_addr_space_t& as)
134{
135    static std::unordered_map<pid_t, unw_addr_space_t> ass_;
136    auto iter = ass_.find(pid);
137    if (iter != ass_.end()) {
138        as = iter->second;
139    } else {
140        as = unw_create_addr_space(&_UPT_accessors, 0);
141        ass_[pid] = as;
142    }
143}
144
145static void RunCache(benchmark::State& state)
146{
147    pid_t pid = fork();
148    if (pid == 0) {
149        TestFunc1(nullptr);
150        _exit(0);
151    } else if (pid < 0) {
152        return;
153    }
154    if (!DfxPtrace::Attach(pid)) {
155        DFXLOGE("Failed to attach pid: %{public}d", pid);
156        TestScopedPidReaper::Kill(pid);
157        return;
158    }
159    DFXLOGU("pid: %{public}d", pid);
160    TestScopedPidReaper reap(pid);
161
162    unw_addr_space_t as;
163    GetCacheUnwind(pid, as);
164
165    for (const auto& _ : state) {
166        auto unwSize = UnwindRemote(pid, as);
167        if (unwSize < TEST_MIN_UNWIND_FRAMES) {
168            state.SkipWithError("Failed to unwind.");
169        }
170    }
171    unw_destroy_addr_space(as);
172    as = nullptr;
173    DFXLOGU("Detach pid: %{public}d", pid);
174    DfxPtrace::Detach(pid);
175}
176
177/**
178* @tc.name: BenchmarkUnwindRemote
179* @tc.desc: Unwind remote
180* @tc.type: FUNC
181*/
182static void BenchmarkUnwindRemote(benchmark::State& state)
183{
184    Run(state);
185}
186BENCHMARK(BenchmarkUnwindRemote);
187
188/**
189* @tc.name: BenchmarkUnwindRemoteCache
190* @tc.desc: Unwind remote cache
191* @tc.type: FUNC
192*/
193static void BenchmarkUnwindRemoteCache(benchmark::State& state)
194{
195    RunCache(state);
196}
197BENCHMARK(BenchmarkUnwindRemoteCache);
198