Home | History | Annotate | Download | only in debuggerd
      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, &param) != 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();
    129