/* * 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 #include #include #include #include #include #include #include #include #include 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 GetMaximumPause(std::atomic& state) { std::chrono::duration 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 static void BM_maximum_pause_impl(benchmark::State& state, const Fn& function) { SetScheduler(); for (auto _ : state) { std::chrono::duration max_pause; std::atomic 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();