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
SetScheduler()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
GetMaximumPause(std::atomic<ThreadState> & state)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
PerformDump()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>
BM_maximum_pause_impl(benchmark::State & state,const Fn & function)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
BM_maximum_pause_noop(benchmark::State & state)117 static void BM_maximum_pause_noop(benchmark::State& state) {
118 BM_maximum_pause_impl(state, []() {});
119 }
120
BM_maximum_pause_debuggerd(benchmark::State & state)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