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 27using namespace OHOS::HiviewDFX; 28using namespace std; 29 30#undef LOG_DOMAIN 31#undef LOG_TAG 32#define LOG_DOMAIN 0xD002D11 33#define LOG_TAG "DfxUnwinderRemote" 34 35static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5; 36 37struct UnwindData { 38 bool isCache = false; 39 bool isFillFrames = false; 40 bool isFp = false; 41}; 42 43NOINLINE static void TestFunc6(MAYBE_UNUSED void (*func)(void*), MAYBE_UNUSED void* data) 44{ 45 while (true) {}; 46 DFXLOGE("Not be run here!!!"); 47} 48 49NOINLINE static void TestFunc5(void (*func)(void*), void* data) 50{ 51 return TestFunc6(func, data); 52} 53 54NOINLINE static void TestFunc4(void (*func)(void*), void* data) 55{ 56 return TestFunc5(func, data); 57} 58 59NOINLINE static void TestFunc3(void (*func)(void*), void* data) 60{ 61 return TestFunc4(func, data); 62} 63 64NOINLINE static void TestFunc2(void (*func)(void*), void* data) 65{ 66 return TestFunc3(func, data); 67} 68 69NOINLINE static void TestFunc1(void (*func)(void*), void* data) 70{ 71 return TestFunc2(func, data); 72} 73 74static 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 DFXLOGU("%{public}s frames.size: %{public}zu", __func__, frames.size()); 82 return frames.size(); 83} 84 85static 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 DFXLOGU("%{public}s frames.size: %{public}zu", __func__, frames.size()); 98 return frames.size(); 99} 100 101static 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 121static 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 DFXLOGE("Failed to attach pid: %{public}d", pid); 132 TestScopedPidReaper::Kill(pid); 133 return; 134 } 135 DFXLOGU("pid: %{public}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 DFXLOGU("Detach pid: %{public}d", pid); 158 DfxPtrace::Detach(pid); 159} 160 161/** 162* @tc.name: BenchmarkUnwinderRemoteFull 163* @tc.desc: Unwind remote full 164* @tc.type: FUNC 165*/ 166static 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} 178BENCHMARK(BenchmarkUnwinderRemoteFull); 179 180/** 181* @tc.name: BenchmarkUnwinderRemoteQut 182* @tc.desc: Unwind remote qut 183* @tc.type: FUNC 184*/ 185static 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} 193BENCHMARK(BenchmarkUnwinderRemoteQut); 194 195/** 196* @tc.name: BenchmarkUnwinderRemoteQutCache 197* @tc.desc: Unwind remote qut cache 198* @tc.type: FUNC 199*/ 200static 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} 208BENCHMARK(BenchmarkUnwinderRemoteQutCache); 209 210/** 211* @tc.name: BenchmarkUnwinderRemoteQutFrames 212* @tc.desc: Unwind remote qut frames 213* @tc.type: FUNC 214*/ 215static 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} 223BENCHMARK(BenchmarkUnwinderRemoteQutFrames); 224 225/** 226* @tc.name: BenchmarkUnwinderRemoteQutFramesCache 227* @tc.desc: Unwind remote qut frames cache 228* @tc.type: FUNC 229*/ 230static 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} 238BENCHMARK(BenchmarkUnwinderRemoteQutFramesCache); 239 240/** 241* @tc.name: BenchmarkUnwinderRemoteQutMiniDebugInfos 242* @tc.desc: Unwind remote qut minidebuginfo 243* @tc.type: FUNC 244*/ 245static 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} 255BENCHMARK(BenchmarkUnwinderRemoteQutMiniDebugInfos); 256 257/** 258* @tc.name: BenchmarkUnwinderRemoteQutMiniDebugInfosLazily 259* @tc.desc: Unwind remote qut minidebuginfo lazily 260* @tc.type: FUNC 261*/ 262static 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} 274BENCHMARK(BenchmarkUnwinderRemoteQutMiniDebugInfosLazily); 275 276#if defined(__aarch64__) 277/** 278* @tc.name: BenchmarkUnwinderRemoteFp 279* @tc.desc: Unwind remote fp 280* @tc.type: FUNC 281*/ 282static void BenchmarkUnwinderRemoteFp(benchmark::State& state) 283{ 284 UnwindData data; 285 data.isFp = true; 286 Run(state, &data); 287} 288BENCHMARK(BenchmarkUnwinderRemoteFp); 289#endif