| /* |
| * 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(); |