129 lines
3.4 KiB
C++
129 lines
3.4 KiB
C++
/*
|
|
* 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();
|