| Josh Gao | a42314e | 2017-12-19 15:08:19 -0800 | [diff] [blame] | 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 |  | 
|  | 36 | static void SetScheduler() { | 
|  | 37 | struct sched_param param { | 
|  | 38 | .sched_priority = 1, | 
|  | 39 | }; | 
|  | 40 |  | 
|  | 41 | if (sched_setscheduler(getpid(), SCHED_FIFO, ¶m) != 0) { | 
|  | 42 | fprintf(stderr, "failed to set scheduler to SCHED_FIFO: %s", strerror(errno)); | 
|  | 43 | } | 
|  | 44 | } | 
|  | 45 |  | 
|  | 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 |  | 
|  | 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> | 
|  | 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 |  | 
|  | 117 | static void BM_maximum_pause_noop(benchmark::State& state) { | 
|  | 118 | BM_maximum_pause_impl(state, []() {}); | 
|  | 119 | } | 
|  | 120 |  | 
|  | 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(); |