• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <vector>
20 #include "dfx_define.h"
21 #include "dfx_log.h"
22 #include <libunwind.h>
23 #include <libunwind_i-ohos.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 
33 struct UnwindData {
34     bool isFillFrames = false;
35 };
36 
TestFunc6(MAYBE_UNUSED void (* func)(void *),MAYBE_UNUSED void * data)37 static void TestFunc6(MAYBE_UNUSED void (*func)(void*), MAYBE_UNUSED void* data)
38 {
39     while (true) {};
40     LOGE("Not be run here!!!");
41 }
42 
TestFunc5(void (* func)(void *),void * data)43 static void TestFunc5(void (*func)(void*), void* data)
44 {
45     return TestFunc6(func, data);
46 }
47 
TestFunc4(void (* func)(void *),void * data)48 static void TestFunc4(void (*func)(void*), void* data)
49 {
50     return TestFunc5(func, data);
51 }
52 
TestFunc3(void (* func)(void *),void * data)53 static void TestFunc3(void (*func)(void*), void* data)
54 {
55     return TestFunc4(func, data);
56 }
57 
TestFunc2(void (* func)(void *),void * data)58 static void TestFunc2(void (*func)(void*), void* data)
59 {
60     return TestFunc3(func, data);
61 }
62 
TestFunc1(void (* func)(void *),void * data)63 static void TestFunc1(void (*func)(void*), void* data)
64 {
65     return TestFunc2(func, data);
66 }
67 
RemoteFork()68 static pid_t RemoteFork()
69 {
70     pid_t pid;
71     if ((pid = fork()) == 0) {
72         TestFunc1(nullptr, nullptr);
73         _exit(0);
74     }
75     if (pid == -1) {
76         return -1;
77     }
78     if (!DfxPtrace::Attach(pid)) {
79         LOGE("Failed to attach pid: %d", pid);
80         TestScopedPidReaper::Kill(pid);
81         return -1;
82     }
83     return pid;
84 }
85 
UnwindRemote(unw_addr_space_t as,UnwindData * dataPtr)86 static size_t UnwindRemote(unw_addr_space_t as, UnwindData* dataPtr)
87 {
88     if (as == nullptr) {
89         LOGE("as is nullptr");
90         return 0;
91     }
92     unw_set_caching_policy(as, UNW_CACHE_GLOBAL);
93     void *context = _UPT_create(as->pid);
94     if (context == nullptr) {
95         LOGE("_UPT_create");
96         return 0;
97     }
98 
99     unw_cursor_t cursor;
100     int unwRet = unw_init_remote(&cursor, as, context);
101     if (unwRet != 0) {
102         LOGE("unw_init_remote");
103         _UPT_destroy(context);
104         return 0;
105     }
106 
107     std::vector<unw_word_t> pcs;
108     size_t index = 0;
109     unw_word_t sp = 0, pc = 0, prevPc, relPc;
110     do {
111         if ((unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t*)(&(pc)))) || (prevPc == pc)) {
112             break;
113         }
114         pcs.emplace_back(pc);
115         prevPc = pc;
116         relPc = unw_get_rel_pc(&cursor);
117         unw_word_t sz = unw_get_previous_instr_sz(&cursor);
118         if ((index > 0) && (relPc > sz)) {
119             relPc -= sz;
120             pc -= sz;
121         }
122 
123         struct map_info* map = unw_get_map(&cursor);
124         if (map == nullptr) {
125             break;
126         }
127 
128         if (unw_step(&cursor) <= 0) {
129             break;
130         }
131         index++;
132     } while (true);
133     _UPT_destroy(context);
134     LOGU("%s pcs.size: %zu", __func__, pcs.size());
135     return pcs.size();
136 }
137 
Run(benchmark::State & state,void * data)138 static void Run(benchmark::State& state, void* data)
139 {
140     UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
141 
142     pid_t pid = RemoteFork();
143     if (pid == -1) {
144         state.SkipWithError("Failed to fork remote process.");
145         return;
146     }
147     LOGU("pid: %d", pid);
148     TestScopedPidReaper reap(pid);
149 
150     for (const auto& _ : state) {
151         unw_addr_space_t as = unw_create_addr_space(&_UPT_accessors, 0);
152         unw_set_target_pid(as, pid);
153         auto unwSize = UnwindRemote(as, dataPtr);
154         if (unwSize < TEST_MIN_UNWIND_FRAMES) {
155             state.SkipWithError("Failed to unwind.");
156         }
157         unw_destroy_addr_space(as);
158         as = nullptr;
159     }
160     LOGU("Detach pid: %d", pid);
161     DfxPtrace::Detach(pid);
162 }
163 
GetCacheUnwind(pid_t pid,unw_addr_space_t & as)164 static void GetCacheUnwind(pid_t pid, unw_addr_space_t& as)
165 {
166     static std::unordered_map<pid_t, unw_addr_space_t> ass_;
167     auto iter = ass_.find(pid);
168     if (iter != ass_.end()) {
169         as = iter->second;
170     } else {
171         as = unw_create_addr_space(&_UPT_accessors, 0);
172         ass_[pid] = as;
173     }
174 }
175 
RunCache(benchmark::State & state,void * data)176 static void RunCache(benchmark::State& state, void* data)
177 {
178     UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
179 
180     pid_t pid = RemoteFork();
181     if (pid == -1) {
182         state.SkipWithError("Failed to fork remote process.");
183         return;
184     }
185     LOGU("pid: %d", pid);
186     TestScopedPidReaper reap(pid);
187 
188     unw_addr_space_t as;
189     GetCacheUnwind(pid, as);
190 
191     for (const auto& _ : state) {
192         unw_set_target_pid(as, pid);
193         auto unwSize = UnwindRemote(as, dataPtr);
194         if (unwSize < TEST_MIN_UNWIND_FRAMES) {
195             state.SkipWithError("Failed to unwind.");
196         }
197     }
198     unw_destroy_addr_space(as);
199     as = nullptr;
200     LOGU("Detach pid: %d", pid);
201     DfxPtrace::Detach(pid);
202 }
203 
204 /**
205 * @tc.name: BenchmarkUnwindRemote
206 * @tc.desc: Unwind remote
207 * @tc.type: FUNC
208 */
BenchmarkUnwindRemote(benchmark::State & state)209 static void BenchmarkUnwindRemote(benchmark::State& state)
210 {
211     Run(state, nullptr);
212 }
213 BENCHMARK(BenchmarkUnwindRemote);
214 
215 /**
216 * @tc.name: BenchmarkUnwindRemoteCache
217 * @tc.desc: Unwind remote cache
218 * @tc.type: FUNC
219 */
BenchmarkUnwindRemoteCache(benchmark::State & state)220 static void BenchmarkUnwindRemoteCache(benchmark::State& state)
221 {
222     RunCache(state, nullptr);
223 }
224 BENCHMARK(BenchmarkUnwindRemoteCache);
225