Home | History | Annotate | Download | only in base
      1 /*
      2  * Copyright (C) 2011 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 
     18 #include <stdio.h>
     19 
     20 #include "timing_logger.h"
     21 
     22 #include "base/logging.h"
     23 #include "base/stl_util.h"
     24 #include "base/histogram-inl.h"
     25 #include "base/systrace.h"
     26 #include "base/time_utils.h"
     27 #include "gc/heap.h"
     28 #include "runtime.h"
     29 #include "thread-current-inl.h"
     30 
     31 #include <cmath>
     32 #include <iomanip>
     33 
     34 namespace art {
     35 
     36 constexpr size_t CumulativeLogger::kLowMemoryBucketCount;
     37 constexpr size_t CumulativeLogger::kDefaultBucketCount;
     38 constexpr size_t TimingLogger::kIndexNotFound;
     39 
     40 CumulativeLogger::CumulativeLogger(const std::string& name)
     41     : name_(name),
     42       lock_name_("CumulativeLoggerLock" + name),
     43       lock_(lock_name_.c_str(), kDefaultMutexLevel, true) {
     44   Reset();
     45 }
     46 
     47 CumulativeLogger::~CumulativeLogger() {
     48   STLDeleteElements(&histograms_);
     49 }
     50 
     51 void CumulativeLogger::SetName(const std::string& name) {
     52   MutexLock mu(Thread::Current(), lock_);
     53   name_.assign(name);
     54 }
     55 
     56 void CumulativeLogger::Start() {
     57 }
     58 
     59 void CumulativeLogger::End() {
     60   MutexLock mu(Thread::Current(), lock_);
     61   ++iterations_;
     62 }
     63 
     64 void CumulativeLogger::Reset() {
     65   MutexLock mu(Thread::Current(), lock_);
     66   iterations_ = 0;
     67   total_time_ = 0;
     68   STLDeleteElements(&histograms_);
     69 }
     70 
     71 void CumulativeLogger::AddLogger(const TimingLogger &logger) {
     72   MutexLock mu(Thread::Current(), lock_);
     73   TimingLogger::TimingData timing_data(logger.CalculateTimingData());
     74   const std::vector<TimingLogger::Timing>& timings = logger.GetTimings();
     75   for (size_t i = 0; i < timings.size(); ++i) {
     76     if (timings[i].IsStartTiming()) {
     77       AddPair(timings[i].GetName(), timing_data.GetExclusiveTime(i));
     78     }
     79   }
     80   ++iterations_;
     81 }
     82 
     83 size_t CumulativeLogger::GetIterations() const {
     84   MutexLock mu(Thread::Current(), lock_);
     85   return iterations_;
     86 }
     87 
     88 void CumulativeLogger::Dump(std::ostream &os) const {
     89   MutexLock mu(Thread::Current(), lock_);
     90   DumpHistogram(os);
     91 }
     92 
     93 void CumulativeLogger::AddPair(const std::string& label, uint64_t delta_time) {
     94   // Convert delta time to microseconds so that we don't overflow our counters.
     95   delta_time /= kAdjust;
     96   total_time_ += delta_time;
     97   Histogram<uint64_t>* histogram;
     98   Histogram<uint64_t> dummy(label.c_str());
     99   auto it = histograms_.find(&dummy);
    100   if (it == histograms_.end()) {
    101     const size_t max_buckets = Runtime::Current()->GetHeap()->IsLowMemoryMode() ?
    102         kLowMemoryBucketCount : kDefaultBucketCount;
    103     histogram = new Histogram<uint64_t>(label.c_str(), kInitialBucketSize, max_buckets);
    104     histograms_.insert(histogram);
    105   } else {
    106     histogram = *it;
    107   }
    108   histogram->AddValue(delta_time);
    109 }
    110 
    111 class CompareHistorgramByTimeSpentDeclining {
    112  public:
    113   bool operator()(const Histogram<uint64_t>* a, const Histogram<uint64_t>* b) const {
    114     return a->Sum() > b->Sum();
    115   }
    116 };
    117 
    118 void CumulativeLogger::DumpHistogram(std::ostream &os) const {
    119   os << "Start Dumping histograms for " << iterations_ << " iterations"
    120      << " for " << name_ << "\n";
    121   std::set<Histogram<uint64_t>*, CompareHistorgramByTimeSpentDeclining>
    122       sorted_histograms(histograms_.begin(), histograms_.end());
    123   for (Histogram<uint64_t>* histogram : sorted_histograms) {
    124     Histogram<uint64_t>::CumulativeData cumulative_data;
    125     // We don't expect DumpHistogram to be called often, so it is not performance critical.
    126     histogram->CreateHistogram(&cumulative_data);
    127     histogram->PrintConfidenceIntervals(os, 0.99, cumulative_data);
    128   }
    129   os << "Done Dumping histograms\n";
    130 }
    131 
    132 TimingLogger::TimingLogger(const char* name, bool precise, bool verbose)
    133     : name_(name), precise_(precise), verbose_(verbose) {
    134 }
    135 
    136 void TimingLogger::Reset() {
    137   timings_.clear();
    138 }
    139 
    140 void TimingLogger::StartTiming(const char* label) {
    141   DCHECK(label != nullptr);
    142   timings_.push_back(Timing(NanoTime(), label));
    143   ATRACE_BEGIN(label);
    144 }
    145 
    146 void TimingLogger::EndTiming() {
    147   timings_.push_back(Timing(NanoTime(), nullptr));
    148   ATRACE_END();
    149 }
    150 
    151 uint64_t TimingLogger::GetTotalNs() const {
    152   if (timings_.size() < 2) {
    153     return 0;
    154   }
    155   return timings_.back().GetTime() - timings_.front().GetTime();
    156 }
    157 
    158 size_t TimingLogger::FindTimingIndex(const char* name, size_t start_idx) const {
    159   DCHECK_LT(start_idx, timings_.size());
    160   for (size_t i = start_idx; i < timings_.size(); ++i) {
    161     if (timings_[i].IsStartTiming() && strcmp(timings_[i].GetName(), name) == 0) {
    162       return i;
    163     }
    164   }
    165   return kIndexNotFound;
    166 }
    167 
    168 TimingLogger::TimingData TimingLogger::CalculateTimingData() const {
    169   TimingLogger::TimingData ret;
    170   ret.data_.resize(timings_.size());
    171   std::vector<size_t> open_stack;
    172   for (size_t i = 0; i < timings_.size(); ++i) {
    173     if (timings_[i].IsEndTiming()) {
    174       CHECK(!open_stack.empty()) << "No starting split for ending split at index " << i;
    175       size_t open_idx = open_stack.back();
    176       uint64_t time = timings_[i].GetTime() - timings_[open_idx].GetTime();
    177       ret.data_[open_idx].exclusive_time += time;
    178       DCHECK_EQ(ret.data_[open_idx].total_time, 0U);
    179       ret.data_[open_idx].total_time += time;
    180       // Each open split has exactly one end.
    181       open_stack.pop_back();
    182       // If there is a parent node, subtract from the exclusive time.
    183       if (!open_stack.empty()) {
    184         // Note this may go negative, but will work due to 2s complement when we add the value
    185         // total time value later.
    186         ret.data_[open_stack.back()].exclusive_time -= time;
    187       }
    188     } else {
    189       open_stack.push_back(i);
    190     }
    191   }
    192   CHECK(open_stack.empty()) << "Missing ending for timing "
    193       << timings_[open_stack.back()].GetName() << " at index " << open_stack.back();
    194   return ret;  // No need to fear, C++11 move semantics are here.
    195 }
    196 
    197 void TimingLogger::Dump(std::ostream &os, const char* indent_string) const {
    198   static constexpr size_t kFractionalDigits = 3;
    199   TimingLogger::TimingData timing_data(CalculateTimingData());
    200   uint64_t longest_split = 0;
    201   for (size_t i = 0; i < timings_.size(); ++i) {
    202     longest_split = std::max(longest_split, timing_data.GetTotalTime(i));
    203   }
    204   // Compute which type of unit we will use for printing the timings.
    205   TimeUnit tu = GetAppropriateTimeUnit(longest_split);
    206   uint64_t divisor = GetNsToTimeUnitDivisor(tu);
    207   uint64_t mod_fraction = divisor >= 1000 ? divisor / 1000 : 1;
    208   // Print formatted splits.
    209   size_t tab_count = 1;
    210   os << name_ << " [Exclusive time] [Total time]\n";
    211   for (size_t i = 0; i < timings_.size(); ++i) {
    212     if (timings_[i].IsStartTiming()) {
    213       uint64_t exclusive_time = timing_data.GetExclusiveTime(i);
    214       uint64_t total_time = timing_data.GetTotalTime(i);
    215       if (!precise_) {
    216         // Make the fractional part 0.
    217         exclusive_time -= exclusive_time % mod_fraction;
    218         total_time -= total_time % mod_fraction;
    219       }
    220       for (size_t j = 0; j < tab_count; ++j) {
    221         os << indent_string;
    222       }
    223       os << FormatDuration(exclusive_time, tu, kFractionalDigits);
    224       // If they are the same, just print one value to prevent spam.
    225       if (exclusive_time != total_time) {
    226         os << "/" << FormatDuration(total_time, tu, kFractionalDigits);
    227       }
    228       os << " " << timings_[i].GetName() << "\n";
    229       ++tab_count;
    230     } else {
    231       --tab_count;
    232     }
    233   }
    234   os << name_ << ": end, " << PrettyDuration(GetTotalNs()) << "\n";
    235 }
    236 
    237 void TimingLogger::Verify() {
    238   size_t counts[2] = { 0 };
    239   for (size_t i = 0; i < timings_.size(); ++i) {
    240     if (i > 0) {
    241       CHECK_LE(timings_[i - 1].GetTime(), timings_[i].GetTime());
    242     }
    243     ++counts[timings_[i].IsStartTiming() ? 0 : 1];
    244   }
    245   CHECK_EQ(counts[0], counts[1]) << "Number of StartTiming and EndTiming doesn't match";
    246 }
    247 
    248 TimingLogger::~TimingLogger() {
    249   if (kIsDebugBuild) {
    250     Verify();
    251   }
    252 }
    253 
    254 }  // namespace art
    255