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