1 /* 2 * Copyright 2017, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <err.h> 18 #include <errno.h> 19 #include <sched.h> 20 #include <string.h> 21 #include <sys/wait.h> 22 #include <unistd.h> 23 24 #include <chrono> 25 #include <thread> 26 27 #include <benchmark/benchmark.h> 28 #include <debuggerd/client.h> 29 30 using namespace std::chrono_literals; 31 32 static_assert(std::chrono::high_resolution_clock::is_steady); 33 34 enum class ThreadState { Starting, Started, Stopping }; 35 36 static void SetScheduler() { 37 struct sched_param param { 38 .sched_priority = 1, 39 }; 40 41 if (sched_setscheduler(getpid(), SCHED_FIFO, ¶m) != 0) { 42 fprintf(stderr, "failed to set scheduler to SCHED_FIFO: %s", strerror(errno)); 43 } 44 } 45 46 static std::chrono::duration<double> GetMaximumPause(std::atomic<ThreadState>& state) { 47 std::chrono::duration<double> max_diff(0); 48 49 const auto begin = std::chrono::high_resolution_clock::now(); 50 auto last = begin; 51 state.store(ThreadState::Started); 52 while (state.load() != ThreadState::Stopping) { 53 auto now = std::chrono::high_resolution_clock::now(); 54 55 auto diff = now - last; 56 if (diff > max_diff) { 57 max_diff = diff; 58 } 59 60 last = now; 61 } 62 63 return max_diff; 64 } 65 66 static void PerformDump() { 67 pid_t target = getpid(); 68 pid_t forkpid = fork(); 69 if (forkpid == -1) { 70 err(1, "fork failed"); 71 } else if (forkpid != 0) { 72 int status; 73 pid_t pid = waitpid(forkpid, &status, 0); 74 if (pid == -1) { 75 err(1, "waitpid failed"); 76 } else if (!WIFEXITED(status)) { 77 err(1, "child didn't exit"); 78 } else if (WEXITSTATUS(status) != 0) { 79 errx(1, "child exited with non-zero status %d", WEXITSTATUS(status)); 80 } 81 } else { 82 android::base::unique_fd output_fd(open("/dev/null", O_WRONLY | O_CLOEXEC)); 83 if (output_fd == -1) { 84 err(1, "failed to open /dev/null"); 85 } 86 87 if (!debuggerd_trigger_dump(target, kDebuggerdNativeBacktrace, 1000, std::move(output_fd))) { 88 errx(1, "failed to trigger dump"); 89 } 90 91 _exit(0); 92 } 93 } 94 95 template <typename Fn> 96 static void BM_maximum_pause_impl(benchmark::State& state, const Fn& function) { 97 SetScheduler(); 98 99 for (auto _ : state) { 100 std::chrono::duration<double> max_pause; 101 std::atomic<ThreadState> thread_state(ThreadState::Starting); 102 auto thread = std::thread([&]() { max_pause = GetMaximumPause(thread_state); }); 103 104 while (thread_state != ThreadState::Started) { 105 std::this_thread::sleep_for(1ms); 106 } 107 108 function(); 109 110 thread_state = ThreadState::Stopping; 111 thread.join(); 112 113 state.SetIterationTime(max_pause.count()); 114 } 115 } 116 117 static void BM_maximum_pause_noop(benchmark::State& state) { 118 BM_maximum_pause_impl(state, []() {}); 119 } 120 121 static void BM_maximum_pause_debuggerd(benchmark::State& state) { 122 BM_maximum_pause_impl(state, []() { PerformDump(); }); 123 } 124 125 BENCHMARK(BM_maximum_pause_noop)->Iterations(128)->UseManualTime(); 126 BENCHMARK(BM_maximum_pause_debuggerd)->Iterations(128)->UseManualTime(); 127 128 BENCHMARK_MAIN(); 129