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