Home | History | Annotate | Download | only in src
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/counters.h"
      6 
      7 #include <iomanip>
      8 
      9 #include "src/base/platform/platform.h"
     10 #include "src/isolate.h"
     11 #include "src/log-inl.h"
     12 #include "src/log.h"
     13 
     14 namespace v8 {
     15 namespace internal {
     16 
     17 StatsTable::StatsTable()
     18     : lookup_function_(NULL),
     19       create_histogram_function_(NULL),
     20       add_histogram_sample_function_(NULL) {}
     21 
     22 
     23 int* StatsCounter::FindLocationInStatsTable() const {
     24   return isolate_->stats_table()->FindLocation(name_);
     25 }
     26 
     27 
     28 void Histogram::AddSample(int sample) {
     29   if (Enabled()) {
     30     isolate()->stats_table()->AddHistogramSample(histogram_, sample);
     31   }
     32 }
     33 
     34 void* Histogram::CreateHistogram() const {
     35   return isolate()->stats_table()->
     36       CreateHistogram(name_, min_, max_, num_buckets_);
     37 }
     38 
     39 
     40 // Start the timer.
     41 void HistogramTimer::Start() {
     42   if (Enabled()) {
     43     timer_.Start();
     44   }
     45   Logger::CallEventLogger(isolate(), name(), Logger::START, true);
     46 }
     47 
     48 
     49 // Stop the timer and record the results.
     50 void HistogramTimer::Stop() {
     51   if (Enabled()) {
     52     int64_t sample = resolution_ == MICROSECOND
     53                          ? timer_.Elapsed().InMicroseconds()
     54                          : timer_.Elapsed().InMilliseconds();
     55     // Compute the delta between start and stop, in microseconds.
     56     AddSample(static_cast<int>(sample));
     57     timer_.Stop();
     58   }
     59   Logger::CallEventLogger(isolate(), name(), Logger::END, true);
     60 }
     61 
     62 
     63 Counters::Counters(Isolate* isolate) {
     64 #define HR(name, caption, min, max, num_buckets) \
     65   name##_ = Histogram(#caption, min, max, num_buckets, isolate);
     66   HISTOGRAM_RANGE_LIST(HR)
     67 #undef HR
     68 
     69 #define HT(name, caption, max, res) \
     70   name##_ = HistogramTimer(#caption, 0, max, HistogramTimer::res, 50, isolate);
     71     HISTOGRAM_TIMER_LIST(HT)
     72 #undef HT
     73 
     74 #define AHT(name, caption) \
     75   name##_ = AggregatableHistogramTimer(#caption, 0, 10000000, 50, isolate);
     76     AGGREGATABLE_HISTOGRAM_TIMER_LIST(AHT)
     77 #undef AHT
     78 
     79 #define HP(name, caption) \
     80     name##_ = Histogram(#caption, 0, 101, 100, isolate);
     81     HISTOGRAM_PERCENTAGE_LIST(HP)
     82 #undef HP
     83 
     84 
     85 // Exponential histogram assigns bucket limits to points
     86 // p[1], p[2], ... p[n] such that p[i+1] / p[i] = constant.
     87 // The constant factor is equal to the n-th root of (high / low),
     88 // where the n is the number of buckets, the low is the lower limit,
     89 // the high is the upper limit.
     90 // For n = 50, low = 1000, high = 500000: the factor = 1.13.
     91 #define HM(name, caption) \
     92     name##_ = Histogram(#caption, 1000, 500000, 50, isolate);
     93   HISTOGRAM_LEGACY_MEMORY_LIST(HM)
     94 #undef HM
     95 // For n = 100, low = 4000, high = 2000000: the factor = 1.06.
     96 #define HM(name, caption) \
     97   name##_ = Histogram(#caption, 4000, 2000000, 100, isolate);
     98   HISTOGRAM_MEMORY_LIST(HM)
     99 #undef HM
    100 
    101 #define HM(name, caption) \
    102   aggregated_##name##_ = AggregatedMemoryHistogram<Histogram>(&name##_);
    103     HISTOGRAM_MEMORY_LIST(HM)
    104 #undef HM
    105 
    106 #define SC(name, caption) \
    107     name##_ = StatsCounter(isolate, "c:" #caption);
    108 
    109     STATS_COUNTER_LIST_1(SC)
    110     STATS_COUNTER_LIST_2(SC)
    111 #undef SC
    112 
    113 #define SC(name) \
    114     count_of_##name##_ = StatsCounter(isolate, "c:" "V8.CountOf_" #name); \
    115     size_of_##name##_ = StatsCounter(isolate, "c:" "V8.SizeOf_" #name);
    116     INSTANCE_TYPE_LIST(SC)
    117 #undef SC
    118 
    119 #define SC(name) \
    120     count_of_CODE_TYPE_##name##_ = \
    121         StatsCounter(isolate, "c:" "V8.CountOf_CODE_TYPE-" #name); \
    122     size_of_CODE_TYPE_##name##_ = \
    123         StatsCounter(isolate, "c:" "V8.SizeOf_CODE_TYPE-" #name);
    124     CODE_KIND_LIST(SC)
    125 #undef SC
    126 
    127 #define SC(name) \
    128     count_of_FIXED_ARRAY_##name##_ = \
    129         StatsCounter(isolate, "c:" "V8.CountOf_FIXED_ARRAY-" #name); \
    130     size_of_FIXED_ARRAY_##name##_ = \
    131         StatsCounter(isolate, "c:" "V8.SizeOf_FIXED_ARRAY-" #name);
    132     FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC)
    133 #undef SC
    134 
    135 #define SC(name) \
    136     count_of_CODE_AGE_##name##_ = \
    137         StatsCounter(isolate, "c:" "V8.CountOf_CODE_AGE-" #name); \
    138     size_of_CODE_AGE_##name##_ = \
    139         StatsCounter(isolate, "c:" "V8.SizeOf_CODE_AGE-" #name);
    140     CODE_AGE_LIST_COMPLETE(SC)
    141 #undef SC
    142 }
    143 
    144 
    145 void Counters::ResetCounters() {
    146 #define SC(name, caption) name##_.Reset();
    147   STATS_COUNTER_LIST_1(SC)
    148   STATS_COUNTER_LIST_2(SC)
    149 #undef SC
    150 
    151 #define SC(name)              \
    152   count_of_##name##_.Reset(); \
    153   size_of_##name##_.Reset();
    154   INSTANCE_TYPE_LIST(SC)
    155 #undef SC
    156 
    157 #define SC(name)                        \
    158   count_of_CODE_TYPE_##name##_.Reset(); \
    159   size_of_CODE_TYPE_##name##_.Reset();
    160   CODE_KIND_LIST(SC)
    161 #undef SC
    162 
    163 #define SC(name)                          \
    164   count_of_FIXED_ARRAY_##name##_.Reset(); \
    165   size_of_FIXED_ARRAY_##name##_.Reset();
    166   FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC)
    167 #undef SC
    168 
    169 #define SC(name)                       \
    170   count_of_CODE_AGE_##name##_.Reset(); \
    171   size_of_CODE_AGE_##name##_.Reset();
    172   CODE_AGE_LIST_COMPLETE(SC)
    173 #undef SC
    174 }
    175 
    176 
    177 void Counters::ResetHistograms() {
    178 #define HR(name, caption, min, max, num_buckets) name##_.Reset();
    179   HISTOGRAM_RANGE_LIST(HR)
    180 #undef HR
    181 
    182 #define HT(name, caption, max, res) name##_.Reset();
    183     HISTOGRAM_TIMER_LIST(HT)
    184 #undef HT
    185 
    186 #define AHT(name, caption) name##_.Reset();
    187     AGGREGATABLE_HISTOGRAM_TIMER_LIST(AHT)
    188 #undef AHT
    189 
    190 #define HP(name, caption) name##_.Reset();
    191     HISTOGRAM_PERCENTAGE_LIST(HP)
    192 #undef HP
    193 
    194 #define HM(name, caption) name##_.Reset();
    195     HISTOGRAM_LEGACY_MEMORY_LIST(HM)
    196 #undef HM
    197 }
    198 
    199 class RuntimeCallStatEntries {
    200  public:
    201   void Print(std::ostream& os) {
    202     if (total_call_count == 0) return;
    203     std::sort(entries.rbegin(), entries.rend());
    204     os << std::setw(50) << "Runtime Function/C++ Builtin" << std::setw(12)
    205        << "Time" << std::setw(18) << "Count" << std::endl
    206        << std::string(88, '=') << std::endl;
    207     for (Entry& entry : entries) {
    208       entry.SetTotal(total_time, total_call_count);
    209       entry.Print(os);
    210     }
    211     os << std::string(88, '-') << std::endl;
    212     Entry("Total", total_time, total_call_count).Print(os);
    213   }
    214 
    215   // By default, the compiler will usually inline this, which results in a large
    216   // binary size increase: std::vector::push_back expands to a large amount of
    217   // instructions, and this function is invoked repeatedly by macros.
    218   V8_NOINLINE void Add(RuntimeCallCounter* counter) {
    219     if (counter->count() == 0) return;
    220     entries.push_back(
    221         Entry(counter->name(), counter->time(), counter->count()));
    222     total_time += counter->time();
    223     total_call_count += counter->count();
    224   }
    225 
    226  private:
    227   class Entry {
    228    public:
    229     Entry(const char* name, base::TimeDelta time, uint64_t count)
    230         : name_(name),
    231           time_(time.InMicroseconds()),
    232           count_(count),
    233           time_percent_(100),
    234           count_percent_(100) {}
    235 
    236     bool operator<(const Entry& other) const {
    237       if (time_ < other.time_) return true;
    238       if (time_ > other.time_) return false;
    239       return count_ < other.count_;
    240     }
    241 
    242     V8_NOINLINE void Print(std::ostream& os) {
    243       os.precision(2);
    244       os << std::fixed << std::setprecision(2);
    245       os << std::setw(50) << name_;
    246       os << std::setw(10) << static_cast<double>(time_) / 1000 << "ms ";
    247       os << std::setw(6) << time_percent_ << "%";
    248       os << std::setw(10) << count_ << " ";
    249       os << std::setw(6) << count_percent_ << "%";
    250       os << std::endl;
    251     }
    252 
    253     V8_NOINLINE void SetTotal(base::TimeDelta total_time,
    254                               uint64_t total_count) {
    255       if (total_time.InMicroseconds() == 0) {
    256         time_percent_ = 0;
    257       } else {
    258         time_percent_ = 100.0 * time_ / total_time.InMicroseconds();
    259       }
    260       count_percent_ = 100.0 * count_ / total_count;
    261     }
    262 
    263    private:
    264     const char* name_;
    265     int64_t time_;
    266     uint64_t count_;
    267     double time_percent_;
    268     double count_percent_;
    269   };
    270 
    271   uint64_t total_call_count = 0;
    272   base::TimeDelta total_time;
    273   std::vector<Entry> entries;
    274 };
    275 
    276 void RuntimeCallCounter::Reset() {
    277   count_ = 0;
    278   time_ = base::TimeDelta();
    279 }
    280 
    281 void RuntimeCallCounter::Dump(v8::tracing::TracedValue* value) {
    282   value->BeginArray(name_);
    283   value->AppendDouble(count_);
    284   value->AppendDouble(time_.InMicroseconds());
    285   value->EndArray();
    286 }
    287 
    288 void RuntimeCallCounter::Add(RuntimeCallCounter* other) {
    289   count_ += other->count();
    290   time_ += other->time();
    291 }
    292 
    293 void RuntimeCallTimer::Snapshot() {
    294   base::TimeTicks now = Now();
    295   // Pause only / topmost timer in the timer stack.
    296   Pause(now);
    297   // Commit all the timer's elapsed time to the counters.
    298   RuntimeCallTimer* timer = this;
    299   while (timer != nullptr) {
    300     timer->CommitTimeToCounter();
    301     timer = timer->parent();
    302   }
    303   Resume(now);
    304 }
    305 
    306 // static
    307 const RuntimeCallStats::CounterId RuntimeCallStats::counters[] = {
    308 #define CALL_RUNTIME_COUNTER(name) &RuntimeCallStats::name,
    309     FOR_EACH_MANUAL_COUNTER(CALL_RUNTIME_COUNTER)  //
    310 #undef CALL_RUNTIME_COUNTER
    311 #define CALL_RUNTIME_COUNTER(name, nargs, ressize) \
    312   &RuntimeCallStats::Runtime_##name,          //
    313     FOR_EACH_INTRINSIC(CALL_RUNTIME_COUNTER)  //
    314 #undef CALL_RUNTIME_COUNTER
    315 #define CALL_BUILTIN_COUNTER(name) &RuntimeCallStats::Builtin_##name,
    316     BUILTIN_LIST_C(CALL_BUILTIN_COUNTER)  //
    317 #undef CALL_BUILTIN_COUNTER
    318 #define CALL_BUILTIN_COUNTER(name) &RuntimeCallStats::API_##name,
    319     FOR_EACH_API_COUNTER(CALL_BUILTIN_COUNTER)  //
    320 #undef CALL_BUILTIN_COUNTER
    321 #define CALL_BUILTIN_COUNTER(name) &RuntimeCallStats::Handler_##name,
    322     FOR_EACH_HANDLER_COUNTER(CALL_BUILTIN_COUNTER)
    323 #undef CALL_BUILTIN_COUNTER
    324 };
    325 
    326 // static
    327 const int RuntimeCallStats::counters_count =
    328     arraysize(RuntimeCallStats::counters);
    329 
    330 // static
    331 void RuntimeCallStats::Enter(RuntimeCallStats* stats, RuntimeCallTimer* timer,
    332                              CounterId counter_id) {
    333   RuntimeCallCounter* counter = &(stats->*counter_id);
    334   DCHECK(counter->name() != nullptr);
    335   timer->Start(counter, stats->current_timer_.Value());
    336   stats->current_timer_.SetValue(timer);
    337 }
    338 
    339 // static
    340 void RuntimeCallStats::Leave(RuntimeCallStats* stats, RuntimeCallTimer* timer) {
    341   if (stats->current_timer_.Value() == timer) {
    342     stats->current_timer_.SetValue(timer->Stop());
    343   } else {
    344     // Must be a Threading cctest. Walk the chain of Timers to find the
    345     // buried one that's leaving. We don't care about keeping nested timings
    346     // accurate, just avoid crashing by keeping the chain intact.
    347     RuntimeCallTimer* next = stats->current_timer_.Value();
    348     while (next && next->parent() != timer) next = next->parent();
    349     if (next == nullptr) return;
    350     next->set_parent(timer->Stop());
    351   }
    352 }
    353 
    354 void RuntimeCallStats::Add(RuntimeCallStats* other) {
    355   for (const RuntimeCallStats::CounterId counter_id :
    356        RuntimeCallStats::counters) {
    357     RuntimeCallCounter* counter = &(this->*counter_id);
    358     RuntimeCallCounter* other_counter = &(other->*counter_id);
    359     counter->Add(other_counter);
    360   }
    361 }
    362 
    363 // static
    364 void RuntimeCallStats::CorrectCurrentCounterId(RuntimeCallStats* stats,
    365                                                CounterId counter_id) {
    366   RuntimeCallTimer* timer = stats->current_timer_.Value();
    367   // When RCS are enabled dynamically there might be no current timer set up.
    368   if (timer == nullptr) return;
    369   timer->set_counter(&(stats->*counter_id));
    370 }
    371 
    372 void RuntimeCallStats::Print(std::ostream& os) {
    373   RuntimeCallStatEntries entries;
    374   if (current_timer_.Value() != nullptr) {
    375     current_timer_.Value()->Snapshot();
    376   }
    377   for (const RuntimeCallStats::CounterId counter_id :
    378        RuntimeCallStats::counters) {
    379     RuntimeCallCounter* counter = &(this->*counter_id);
    380     entries.Add(counter);
    381   }
    382   entries.Print(os);
    383 }
    384 
    385 void RuntimeCallStats::Reset() {
    386   if (V8_LIKELY(FLAG_runtime_stats == 0)) return;
    387 
    388   // In tracing, we only what to trace the time spent on top level trace events,
    389   // if runtime counter stack is not empty, we should clear the whole runtime
    390   // counter stack, and then reset counters so that we can dump counters into
    391   // top level trace events accurately.
    392   while (current_timer_.Value()) {
    393     current_timer_.SetValue(current_timer_.Value()->Stop());
    394   }
    395 
    396   for (const RuntimeCallStats::CounterId counter_id :
    397        RuntimeCallStats::counters) {
    398     RuntimeCallCounter* counter = &(this->*counter_id);
    399     counter->Reset();
    400   }
    401 
    402   in_use_ = true;
    403 }
    404 
    405 void RuntimeCallStats::Dump(v8::tracing::TracedValue* value) {
    406   for (const RuntimeCallStats::CounterId counter_id :
    407        RuntimeCallStats::counters) {
    408     RuntimeCallCounter* counter = &(this->*counter_id);
    409     if (counter->count() > 0) counter->Dump(value);
    410   }
    411 
    412   in_use_ = false;
    413 }
    414 
    415 }  // namespace internal
    416 }  // namespace v8
    417