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, &param) != 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