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 <sys/ptrace.h> 21#include <unwindstack/Maps.h> 22#include <unwindstack/Memory.h> 23#include <unwindstack/Regs.h> 24#include <unwindstack/Unwinder.h> 25#include "dfx_define.h" 26#include "dfx_log.h" 27#include "dfx_test_util.h" 28#include "MemoryRemote.h" 29#include "pid_utils.h" 30 31using namespace OHOS::HiviewDFX; 32using namespace std; 33 34static constexpr size_t TEST_MIN_UNWIND_FRAMES = 5; 35static constexpr size_t MAX_FRAMES = 32; 36 37struct UnwindData { 38 bool isCache = false; 39 bool isFillFrames = false; 40}; 41 42NOINLINE static void TestFunc6(MAYBE_UNUSED void (*func)(void*), MAYBE_UNUSED volatile bool* ready) 43{ 44 *ready = true; 45 while (true) {}; 46 DFXLOGE("Not be run here!!!"); 47} 48 49NOINLINE static void TestFunc5(void (*func)(void*), volatile bool* ready) 50{ 51 return TestFunc6(func, ready); 52} 53 54NOINLINE static void TestFunc4(void (*func)(void*), volatile bool* ready) 55{ 56 return TestFunc5(func, ready); 57} 58 59NOINLINE static void TestFunc3(void (*func)(void*), volatile bool* ready) 60{ 61 return TestFunc4(func, ready); 62} 63 64NOINLINE static void TestFunc2(void (*func)(void*), volatile bool* ready) 65{ 66 return TestFunc3(func, ready); 67} 68 69NOINLINE static void TestFunc1(void (*func)(void*), volatile bool* ready) 70{ 71 return TestFunc2(func, ready); 72} 73 74static bool WaitForRemote(pid_t pid, volatile bool* readyPtr) 75{ 76 return PidUtils::WaitForPidState(pid, [pid, readyPtr]() { 77 unwindstack::MemoryRemote memory(pid); 78 bool ready; 79 uint64_t readyAddr = reinterpret_cast<uint64_t>(readyPtr); 80 if (memory.ReadFully(readyAddr, &ready, sizeof(ready)) && ready) { 81 return PidRunEnum::PID_RUN_PASS; 82 } 83 return PidRunEnum::PID_RUN_KEEP_GOING; 84 }); 85} 86 87static pid_t RemoteFork() 88{ 89 static volatile bool ready = false; 90 91 pid_t pid; 92 if ((pid = fork()) == 0) { 93 TestFunc1(nullptr, &ready); 94 _exit(0); 95 } else if (pid < 0) { 96 return -1; 97 } 98 99 if (!WaitForRemote(pid, &ready)) { 100 DFXLOGE("Failed to wait pid: %{public}d", pid); 101 TestScopedPidReaper::Kill(pid); 102 return -1; 103 } 104 return pid; 105} 106 107static size_t UnwindRemote(unwindstack::Unwinder unwinder, MAYBE_UNUSED UnwindData* dataPtr) 108{ 109 if (dataPtr != nullptr) { 110 unwinder.SetResolveNames(dataPtr->isFillFrames); 111 } 112 unwinder.Unwind(); 113 auto unwSize = unwinder.NumFrames(); 114 DFXLOGU("%{public}s frames.size: %{public}zu", __func__, unwSize); 115 if (dataPtr != nullptr && dataPtr->isFillFrames) { 116 for (size_t i = 0; i < unwSize; ++i) { 117 auto str = unwinder.FormatFrame(i); 118 DFXLOGU("%{public}s frames: %{public}s", __func__, str.c_str()); 119 } 120 } 121 return unwSize; 122} 123 124static void Run(benchmark::State& state, void* data) 125{ 126 UnwindData* dataPtr = reinterpret_cast<UnwindData*>(data); 127 UnwindData unwindData; 128 if (dataPtr != nullptr) { 129 unwindData.isCache = dataPtr->isCache; 130 } 131 132 pid_t pid = RemoteFork(); 133 if (pid == -1) { 134 state.SkipWithError("Failed to fork remote process."); 135 return; 136 } 137 DFXLOGU("pid: %{public}d", pid); 138 TestScopedPidReaper reap(pid); 139 140 std::shared_ptr<unwindstack::Memory> processMemory; 141 if (unwindData.isCache) { 142 processMemory = unwindstack::Memory::CreateProcessMemoryCached(pid); 143 } else { 144 processMemory = unwindstack::Memory::CreateProcessMemory(pid); 145 } 146 unwindstack::RemoteMaps maps(pid); 147 if (!maps.Parse()) { 148 state.SkipWithError("Failed to parse maps."); 149 } 150 151 for (const auto& _ : state) { 152 std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid)); 153 unwindstack::Unwinder unwinder(MAX_FRAMES, &maps, regs.get(), processMemory); 154 auto unwSize = UnwindRemote(unwinder, dataPtr); 155 if (unwSize < TEST_MIN_UNWIND_FRAMES) { 156 state.SkipWithError("Failed to unwind."); 157 } 158 } 159 DFXLOGU("Detach pid: %{public}d", pid); 160 ptrace(PTRACE_DETACH, pid, 0, 0); 161} 162 163/** 164* @tc.name: BenchmarkUnwindStackRemote 165* @tc.desc: UnwindStack remote 166* @tc.type: FUNC 167*/ 168static void BenchmarkUnwindStackRemote(benchmark::State& state) 169{ 170 UnwindData data; 171 data.isCache = false; 172 Run(state, &data); 173} 174BENCHMARK(BenchmarkUnwindStackRemote); 175 176/** 177* @tc.name: BenchmarkUnwindStackRemoteCache 178* @tc.desc: UnwindStack remote cache 179* @tc.type: FUNC 180*/ 181static void BenchmarkUnwindStackRemoteCache(benchmark::State& state) 182{ 183 UnwindData data; 184 data.isCache = true; 185 Run(state, &data); 186} 187BENCHMARK(BenchmarkUnwindStackRemoteCache); 188 189/** 190* @tc.name: BenchmarkUnwindStackRemoteFrames 191* @tc.desc: UnwindStack remote frames 192* @tc.type: FUNC 193*/ 194static void BenchmarkUnwindStackRemoteFrames(benchmark::State& state) 195{ 196 UnwindData data; 197 data.isCache = false; 198 data.isFillFrames = true; 199 Run(state, &data); 200} 201BENCHMARK(BenchmarkUnwindStackRemoteFrames); 202 203/** 204* @tc.name: BenchmarkUnwindStackRemoteFramesCache 205* @tc.desc: UnwindStack remote frames cache 206* @tc.type: FUNC 207*/ 208static void BenchmarkUnwindStackRemoteFramesCache(benchmark::State& state) 209{ 210 UnwindData data; 211 data.isCache = true; 212 data.isFillFrames = true; 213 Run(state, &data); 214} 215BENCHMARK(BenchmarkUnwindStackRemoteFramesCache);