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
28 using namespace OHOS::HiviewDFX;
29 using namespace std;
30
31 static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5;
32
TestFunc6(MAYBE_UNUSED void (* func)(void *))33 NOINLINE static void TestFunc6(MAYBE_UNUSED void (*func)(void*))
34 {
35 while (true) {};
36 LOGE("Not be run here!!!");
37 }
38
TestFunc5(void (* func)(void *))39 NOINLINE static void TestFunc5(void (*func)(void*))
40 {
41 return TestFunc6(func);
42 }
43
TestFunc4(void (* func)(void *))44 NOINLINE static void TestFunc4(void (*func)(void*))
45 {
46 return TestFunc5(func);
47 }
48
TestFunc3(void (* func)(void *))49 NOINLINE static void TestFunc3(void (*func)(void*))
50 {
51 return TestFunc4(func);
52 }
53
TestFunc2(void (* func)(void *))54 NOINLINE static void TestFunc2(void (*func)(void*))
55 {
56 return TestFunc3(func);
57 }
58
TestFunc1(void (* func)(void *))59 NOINLINE static void TestFunc1(void (*func)(void*))
60 {
61 return TestFunc2(func);
62 }
63
UnwindRemote(pid_t pid,unw_addr_space_t as)64 static size_t UnwindRemote(pid_t pid, unw_addr_space_t as)
65 {
66 if (as == nullptr) {
67 LOGE("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 LOGE("_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 LOGE("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 LOGU("%s pcs.size: %zu", __func__, pcs.size());
99 return pcs.size();
100 }
101
Run(benchmark::State & state)102 static 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 LOGE("Failed to attach pid: %d", pid);
113 TestScopedPidReaper::Kill(pid);
114 return;
115 }
116
117 LOGU("pid: %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 LOGU("Detach pid: %d", pid);
130 DfxPtrace::Detach(pid);
131 }
132
GetCacheUnwind(pid_t pid,unw_addr_space_t & as)133 static 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
RunCache(benchmark::State & state)145 static 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 LOGE("Failed to attach pid: %d", pid);
156 TestScopedPidReaper::Kill(pid);
157 return;
158 }
159 LOGU("pid: %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 LOGU("Detach pid: %d", pid);
174 DfxPtrace::Detach(pid);
175 }
176
177 /**
178 * @tc.name: BenchmarkUnwindRemote
179 * @tc.desc: Unwind remote
180 * @tc.type: FUNC
181 */
BenchmarkUnwindRemote(benchmark::State & state)182 static void BenchmarkUnwindRemote(benchmark::State& state)
183 {
184 Run(state);
185 }
186 BENCHMARK(BenchmarkUnwindRemote);
187
188 /**
189 * @tc.name: BenchmarkUnwindRemoteCache
190 * @tc.desc: Unwind remote cache
191 * @tc.type: FUNC
192 */
BenchmarkUnwindRemoteCache(benchmark::State & state)193 static void BenchmarkUnwindRemoteCache(benchmark::State& state)
194 {
195 RunCache(state);
196 }
197 BENCHMARK(BenchmarkUnwindRemoteCache);
198