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