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_log.h"
21 #include "dfx_ptrace.h"
22 #include "dfx_regs_qut.h"
23 #include "dfx_test_util.h"
24 #include "unwinder.h"
25 #include "unwinder_config.h"
26 
27 using namespace OHOS::HiviewDFX;
28 using namespace std;
29 
30 #undef LOG_DOMAIN
31 #undef LOG_TAG
32 #define LOG_DOMAIN 0xD002D11
33 #define LOG_TAG "DfxUnwinderRemote"
34 
35 static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5;
36 
37 struct UnwindData {
38     bool isCache = false;
39     bool isFillFrames = false;
40     bool isFp = false;
41 };
42 
TestFunc6(MAYBE_UNUSED void (* func)(void *),MAYBE_UNUSED void * data)43 NOINLINE static void TestFunc6(MAYBE_UNUSED void (*func)(void*), MAYBE_UNUSED void* data)
44 {
45     while (true) {};
46     LOGE("Not be run here!!!");
47 }
48 
TestFunc5(void (* func)(void *),void * data)49 NOINLINE static void TestFunc5(void (*func)(void*), void* data)
50 {
51     return TestFunc6(func, data);
52 }
53 
TestFunc4(void (* func)(void *),void * data)54 NOINLINE static void TestFunc4(void (*func)(void*), void* data)
55 {
56     return TestFunc5(func, data);
57 }
58 
TestFunc3(void (* func)(void *),void * data)59 NOINLINE static void TestFunc3(void (*func)(void*), void* data)
60 {
61     return TestFunc4(func, data);
62 }
63 
TestFunc2(void (* func)(void *),void * data)64 NOINLINE static void TestFunc2(void (*func)(void*), void* data)
65 {
66     return TestFunc3(func, data);
67 }
68 
TestFunc1(void (* func)(void *),void * data)69 NOINLINE static void TestFunc1(void (*func)(void*), void* data)
70 {
71     return TestFunc2(func, data);
72 }
73 
UnwinderRemote(std::shared_ptr<Unwinder> unwinder,const pid_t tid)74 static size_t UnwinderRemote(std::shared_ptr<Unwinder> unwinder, const pid_t tid)
75 {
76     if (unwinder == nullptr) {
77         return 0;
78     }
79     MAYBE_UNUSED bool unwRet = unwinder->UnwindRemote(tid);
80     auto frames = unwinder->GetFrames();
81     LOGU("%s frames.size: %zu", __func__, frames.size());
82     return frames.size();
83 }
84 
UnwinderRemoteFp(std::shared_ptr<Unwinder> unwinder,const pid_t tid)85 static size_t UnwinderRemoteFp(std::shared_ptr<Unwinder> unwinder, const pid_t tid)
86 {
87     if (unwinder == nullptr) {
88         return 0;
89     }
90     auto regs = DfxRegs::CreateRemoteRegs(tid);
91     unwinder->SetRegs(regs);
92     UnwindContext context;
93     context.pid = tid;
94     unwinder->EnableFpCheckMapExec(false);
95     unwinder->UnwindByFp(&context);
96     auto frames = unwinder->GetPcs();
97     LOGU("%s frames.size: %zu", __func__, frames.size());
98     return frames.size();
99 }
100 
GetUnwinder(pid_t pid,void * data,std::shared_ptr<Unwinder> & unwinder,bool & isFp)101 static bool GetUnwinder(pid_t pid, void* data, std::shared_ptr<Unwinder>& unwinder, bool& isFp)
102 {
103     static std::unordered_map<pid_t, std::shared_ptr<Unwinder>> unwinders_;
104     auto iter = unwinders_.find(pid);
105     if (iter != unwinders_.end()) {
106         unwinder = iter->second;
107     } else {
108         unwinder = std::make_shared<Unwinder>(pid);
109         unwinders_[pid] = unwinder;
110     }
111 
112     UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data);
113     if ((dataPtr != nullptr) && (unwinder != nullptr)) {
114         unwinder->EnableFillFrames(dataPtr->isFillFrames);
115         unwinder->EnableUnwindCache(dataPtr->isCache);
116         isFp = dataPtr->isFp;
117     }
118     return true;
119 }
120 
Run(benchmark::State & state,void * data)121 static void Run(benchmark::State& state, void* data)
122 {
123     pid_t pid = fork();
124     if (pid == 0) {
125         TestFunc1(nullptr, nullptr);
126         _exit(0);
127     } else if (pid < 0) {
128         return;
129     }
130     if (!DfxPtrace::Attach(pid)) {
131         LOGE("Failed to attach pid: %d", pid);
132         TestScopedPidReaper::Kill(pid);
133         return;
134     }
135     LOGU("pid: %d", pid);
136     TestScopedPidReaper reap(pid);
137 
138     std::shared_ptr<Unwinder> unwinder = nullptr;
139     bool isFp = false;
140     if (!GetUnwinder(pid, data, unwinder, isFp) || (unwinder == nullptr)) {
141         state.SkipWithError("Failed to get unwinder.");
142         return;
143     }
144 
145     for (const auto& _ : state) {
146         size_t unwSize = 0;
147         if (isFp) {
148             unwSize = UnwinderRemoteFp(unwinder, pid);
149         } else {
150             unwSize = UnwinderRemote(unwinder, pid);
151         }
152 
153         if (unwSize < TEST_MIN_UNWIND_FRAMES) {
154             state.SkipWithError("Failed to unwind.");
155         }
156     }
157     LOGU("Detach pid: %d", pid);
158     DfxPtrace::Detach(pid);
159 }
160 
161 /**
162 * @tc.name: BenchmarkUnwinderRemoteFull
163 * @tc.desc: Unwind remote full
164 * @tc.type: FUNC
165 */
BenchmarkUnwinderRemoteFull(benchmark::State & state)166 static void BenchmarkUnwinderRemoteFull(benchmark::State& state)
167 {
168     std::vector<uint16_t> qutRegs;
169     for (uint16_t i = REG_EH; i < REG_LAST; ++i) {
170         qutRegs.emplace_back(i);
171     }
172     DfxRegsQut::SetQutRegs(qutRegs);
173     UnwindData data;
174     data.isCache = false;
175     data.isFillFrames = false;
176     Run(state, &data);
177 }
178 BENCHMARK(BenchmarkUnwinderRemoteFull);
179 
180 /**
181 * @tc.name: BenchmarkUnwinderRemoteQut
182 * @tc.desc: Unwind remote qut
183 * @tc.type: FUNC
184 */
BenchmarkUnwinderRemoteQut(benchmark::State & state)185 static void BenchmarkUnwinderRemoteQut(benchmark::State& state)
186 {
187     DfxRegsQut::SetQutRegs(QUT_REGS);
188     UnwindData data;
189     data.isCache = false;
190     data.isFillFrames = false;
191     Run(state, &data);
192 }
193 BENCHMARK(BenchmarkUnwinderRemoteQut);
194 
195 /**
196 * @tc.name: BenchmarkUnwinderRemoteQutCache
197 * @tc.desc: Unwind remote qut cache
198 * @tc.type: FUNC
199 */
BenchmarkUnwinderRemoteQutCache(benchmark::State & state)200 static void BenchmarkUnwinderRemoteQutCache(benchmark::State& state)
201 {
202     DfxRegsQut::SetQutRegs(QUT_REGS);
203     UnwindData data;
204     data.isCache = true;
205     data.isFillFrames = false;
206     Run(state, &data);
207 }
208 BENCHMARK(BenchmarkUnwinderRemoteQutCache);
209 
210 /**
211 * @tc.name: BenchmarkUnwinderRemoteQutFrames
212 * @tc.desc: Unwind remote qut frames
213 * @tc.type: FUNC
214 */
BenchmarkUnwinderRemoteQutFrames(benchmark::State & state)215 static void BenchmarkUnwinderRemoteQutFrames(benchmark::State& state)
216 {
217     DfxRegsQut::SetQutRegs(QUT_REGS);
218     UnwindData data;
219     data.isCache = false;
220     data.isFillFrames = true;
221     Run(state, &data);
222 }
223 BENCHMARK(BenchmarkUnwinderRemoteQutFrames);
224 
225 /**
226 * @tc.name: BenchmarkUnwinderRemoteQutFramesCache
227 * @tc.desc: Unwind remote qut frames cache
228 * @tc.type: FUNC
229 */
BenchmarkUnwinderRemoteQutFramesCache(benchmark::State & state)230 static void BenchmarkUnwinderRemoteQutFramesCache(benchmark::State& state)
231 {
232     DfxRegsQut::SetQutRegs(QUT_REGS);
233     UnwindData data;
234     data.isCache = true;
235     data.isFillFrames = true;
236     Run(state, &data);
237 }
238 BENCHMARK(BenchmarkUnwinderRemoteQutFramesCache);
239 
240 /**
241 * @tc.name: BenchmarkUnwinderRemoteQutMiniDebugInfos
242 * @tc.desc: Unwind remote qut minidebuginfo
243 * @tc.type: FUNC
244 */
BenchmarkUnwinderRemoteQutMiniDebugInfos(benchmark::State & state)245 static void BenchmarkUnwinderRemoteQutMiniDebugInfos(benchmark::State& state)
246 {
247     DfxRegsQut::SetQutRegs(QUT_REGS);
248     UnwinderConfig::SetEnableMiniDebugInfo(true);
249     UnwindData data;
250     data.isCache = false;
251     data.isFillFrames = true;
252     Run(state, &data);
253     UnwinderConfig::SetEnableMiniDebugInfo(false);
254 }
255 BENCHMARK(BenchmarkUnwinderRemoteQutMiniDebugInfos);
256 
257 /**
258 * @tc.name: BenchmarkUnwinderRemoteQutMiniDebugInfosLazily
259 * @tc.desc: Unwind remote qut minidebuginfo lazily
260 * @tc.type: FUNC
261 */
BenchmarkUnwinderRemoteQutMiniDebugInfosLazily(benchmark::State & state)262 static void BenchmarkUnwinderRemoteQutMiniDebugInfosLazily(benchmark::State& state)
263 {
264     DfxRegsQut::SetQutRegs(QUT_REGS);
265     UnwinderConfig::SetEnableMiniDebugInfo(true);
266     UnwinderConfig::SetEnableLoadSymbolLazily(true);
267     UnwindData data;
268     data.isCache = false;
269     data.isFillFrames = true;
270     Run(state, &data);
271     UnwinderConfig::SetEnableMiniDebugInfo(false);
272     UnwinderConfig::SetEnableLoadSymbolLazily(false);
273 }
274 BENCHMARK(BenchmarkUnwinderRemoteQutMiniDebugInfosLazily);
275 
276 #if defined(__aarch64__)
277 /**
278 * @tc.name: BenchmarkUnwinderRemoteFp
279 * @tc.desc: Unwind remote fp
280 * @tc.type: FUNC
281 */
BenchmarkUnwinderRemoteFp(benchmark::State & state)282 static void BenchmarkUnwinderRemoteFp(benchmark::State& state)
283 {
284     UnwindData data;
285     data.isFp = true;
286     Run(state, &data);
287 }
288 BENCHMARK(BenchmarkUnwinderRemoteFp);
289 #endif