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/logging/counters.h"
6 
7 #include <iomanip>
8 
9 #include "src/base/platform/platform.h"
10 #include "src/builtins/builtins-definitions.h"
11 #include "src/execution/isolate.h"
12 #include "src/logging/counters-inl.h"
13 #include "src/logging/log-inl.h"
14 #include "src/logging/log.h"
15 #include "src/utils/ostreams.h"
16 
17 namespace v8 {
18 namespace internal {
19 
20 std::atomic_uint TracingFlags::runtime_stats{0};
21 std::atomic_uint TracingFlags::gc{0};
22 std::atomic_uint TracingFlags::gc_stats{0};
23 std::atomic_uint TracingFlags::ic_stats{0};
24 
StatsTable(Counters * counters)25 StatsTable::StatsTable(Counters* counters)
26     : lookup_function_(nullptr),
27       create_histogram_function_(nullptr),
28       add_histogram_sample_function_(nullptr) {}
29 
SetCounterFunction(CounterLookupCallback f)30 void StatsTable::SetCounterFunction(CounterLookupCallback f) {
31   lookup_function_ = f;
32 }
33 
FindLocationInStatsTable() const34 int* StatsCounterBase::FindLocationInStatsTable() const {
35   return counters_->FindLocation(name_);
36 }
37 
StatsCounterThreadSafe(Counters * counters,const char * name)38 StatsCounterThreadSafe::StatsCounterThreadSafe(Counters* counters,
39                                                const char* name)
40     : StatsCounterBase(counters, name) {}
41 
Set(int Value)42 void StatsCounterThreadSafe::Set(int Value) {
43   if (ptr_) {
44     base::MutexGuard Guard(&mutex_);
45     SetLoc(ptr_, Value);
46   }
47 }
48 
Increment()49 void StatsCounterThreadSafe::Increment() {
50   if (ptr_) {
51     base::MutexGuard Guard(&mutex_);
52     IncrementLoc(ptr_);
53   }
54 }
55 
Increment(int value)56 void StatsCounterThreadSafe::Increment(int value) {
57   if (ptr_) {
58     base::MutexGuard Guard(&mutex_);
59     IncrementLoc(ptr_, value);
60   }
61 }
62 
Decrement()63 void StatsCounterThreadSafe::Decrement() {
64   if (ptr_) {
65     base::MutexGuard Guard(&mutex_);
66     DecrementLoc(ptr_);
67   }
68 }
69 
Decrement(int value)70 void StatsCounterThreadSafe::Decrement(int value) {
71   if (ptr_) {
72     base::MutexGuard Guard(&mutex_);
73     DecrementLoc(ptr_, value);
74   }
75 }
76 
AddSample(int sample)77 void Histogram::AddSample(int sample) {
78   if (Enabled()) {
79     counters_->AddHistogramSample(histogram_, sample);
80   }
81 }
82 
CreateHistogram() const83 void* Histogram::CreateHistogram() const {
84   return counters_->CreateHistogram(name_, min_, max_, num_buckets_);
85 }
86 
Start(base::ElapsedTimer * timer,Isolate * isolate)87 void TimedHistogram::Start(base::ElapsedTimer* timer, Isolate* isolate) {
88   if (Enabled()) timer->Start();
89   if (isolate) Logger::CallEventLogger(isolate, name(), Logger::START, true);
90 }
91 
Stop(base::ElapsedTimer * timer,Isolate * isolate)92 void TimedHistogram::Stop(base::ElapsedTimer* timer, Isolate* isolate) {
93   if (Enabled()) {
94     int64_t sample = resolution_ == HistogramTimerResolution::MICROSECOND
95                          ? timer->Elapsed().InMicroseconds()
96                          : timer->Elapsed().InMilliseconds();
97     timer->Stop();
98     AddSample(static_cast<int>(sample));
99   }
100   if (isolate != nullptr) {
101     Logger::CallEventLogger(isolate, name(), Logger::END, true);
102   }
103 }
104 
RecordAbandon(base::ElapsedTimer * timer,Isolate * isolate)105 void TimedHistogram::RecordAbandon(base::ElapsedTimer* timer,
106                                    Isolate* isolate) {
107   if (Enabled()) {
108     DCHECK(timer->IsStarted());
109     timer->Stop();
110     int64_t sample = resolution_ == HistogramTimerResolution::MICROSECOND
111                          ? base::TimeDelta::Max().InMicroseconds()
112                          : base::TimeDelta::Max().InMilliseconds();
113     AddSample(static_cast<int>(sample));
114   }
115   if (isolate != nullptr) {
116     Logger::CallEventLogger(isolate, name(), Logger::END, true);
117   }
118 }
119 
Counters(Isolate * isolate)120 Counters::Counters(Isolate* isolate)
121     : isolate_(isolate),
122       stats_table_(this),
123 // clang format off
124 #define SC(name, caption) name##_(this, "c:" #caption),
125       STATS_COUNTER_TS_LIST(SC)
126 #undef SC
127       // clang format on
128       runtime_call_stats_(RuntimeCallStats::kMainIsolateThread),
129       worker_thread_runtime_call_stats_() {
130   static const struct {
131     Histogram Counters::*member;
132     const char* caption;
133     int min;
134     int max;
135     int num_buckets;
136   } kHistograms[] = {
137 #define HR(name, caption, min, max, num_buckets) \
138   {&Counters::name##_, #caption, min, max, num_buckets},
139       HISTOGRAM_RANGE_LIST(HR)
140 #undef HR
141   };
142   for (const auto& histogram : kHistograms) {
143     this->*histogram.member =
144         Histogram(histogram.caption, histogram.min, histogram.max,
145                   histogram.num_buckets, this);
146   }
147 
148   const int DefaultTimedHistogramNumBuckets = 50;
149 
150   static const struct {
151     HistogramTimer Counters::*member;
152     const char* caption;
153     int max;
154     HistogramTimerResolution res;
155   } kHistogramTimers[] = {
156 #define HT(name, caption, max, res) \
157   {&Counters::name##_, #caption, max, HistogramTimerResolution::res},
158       HISTOGRAM_TIMER_LIST(HT)
159 #undef HT
160   };
161   for (const auto& timer : kHistogramTimers) {
162     this->*timer.member = HistogramTimer(timer.caption, 0, timer.max, timer.res,
163                                          DefaultTimedHistogramNumBuckets, this);
164   }
165 
166   static const struct {
167     TimedHistogram Counters::*member;
168     const char* caption;
169     int max;
170     HistogramTimerResolution res;
171   } kTimedHistograms[] = {
172 #define HT(name, caption, max, res) \
173   {&Counters::name##_, #caption, max, HistogramTimerResolution::res},
174       TIMED_HISTOGRAM_LIST(HT)
175 #undef HT
176   };
177   for (const auto& timer : kTimedHistograms) {
178     this->*timer.member = TimedHistogram(timer.caption, 0, timer.max, timer.res,
179                                          DefaultTimedHistogramNumBuckets, this);
180   }
181 
182   static const struct {
183     AggregatableHistogramTimer Counters::*member;
184     const char* caption;
185   } kAggregatableHistogramTimers[] = {
186 #define AHT(name, caption) {&Counters::name##_, #caption},
187       AGGREGATABLE_HISTOGRAM_TIMER_LIST(AHT)
188 #undef AHT
189   };
190   for (const auto& aht : kAggregatableHistogramTimers) {
191     this->*aht.member = AggregatableHistogramTimer(
192         aht.caption, 0, 10000000, DefaultTimedHistogramNumBuckets, this);
193   }
194 
195   static const struct {
196     Histogram Counters::*member;
197     const char* caption;
198   } kHistogramPercentages[] = {
199 #define HP(name, caption) {&Counters::name##_, #caption},
200       HISTOGRAM_PERCENTAGE_LIST(HP)
201 #undef HP
202   };
203   for (const auto& percentage : kHistogramPercentages) {
204     this->*percentage.member = Histogram(percentage.caption, 0, 101, 100, this);
205   }
206 
207   // Exponential histogram assigns bucket limits to points
208   // p[1], p[2], ... p[n] such that p[i+1] / p[i] = constant.
209   // The constant factor is equal to the n-th root of (high / low),
210   // where the n is the number of buckets, the low is the lower limit,
211   // the high is the upper limit.
212   // For n = 50, low = 1000, high = 500000: the factor = 1.13.
213   static const struct {
214     Histogram Counters::*member;
215     const char* caption;
216   } kLegacyMemoryHistograms[] = {
217 #define HM(name, caption) {&Counters::name##_, #caption},
218       HISTOGRAM_LEGACY_MEMORY_LIST(HM)
219 #undef HM
220   };
221   for (const auto& histogram : kLegacyMemoryHistograms) {
222     this->*histogram.member =
223         Histogram(histogram.caption, 1000, 500000, 50, this);
224   }
225 
226   // clang-format off
227   static const struct {
228     StatsCounter Counters::*member;
229     const char* caption;
230   } kStatsCounters[] = {
231 #define SC(name, caption) {&Counters::name##_, "c:" #caption},
232   STATS_COUNTER_LIST_1(SC)
233   STATS_COUNTER_LIST_2(SC)
234   STATS_COUNTER_NATIVE_CODE_LIST(SC)
235 #undef SC
236 #define SC(name)                                             \
237   {&Counters::count_of_##name##_, "c:" "V8.CountOf_" #name}, \
238   {&Counters::size_of_##name##_, "c:" "V8.SizeOf_" #name},
239       INSTANCE_TYPE_LIST(SC)
240 #undef SC
241 #define SC(name)                            \
242   {&Counters::count_of_CODE_TYPE_##name##_, \
243     "c:" "V8.CountOf_CODE_TYPE-" #name},     \
244   {&Counters::size_of_CODE_TYPE_##name##_,  \
245     "c:" "V8.SizeOf_CODE_TYPE-" #name},
246       CODE_KIND_LIST(SC)
247 #undef SC
248 #define SC(name)                              \
249   {&Counters::count_of_FIXED_ARRAY_##name##_, \
250     "c:" "V8.CountOf_FIXED_ARRAY-" #name},     \
251   {&Counters::size_of_FIXED_ARRAY_##name##_,  \
252     "c:" "V8.SizeOf_FIXED_ARRAY-" #name},
253       FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC)
254 #undef SC
255   };
256   // clang-format on
257   for (const auto& counter : kStatsCounters) {
258     this->*counter.member = StatsCounter(this, counter.caption);
259   }
260 }
261 
ResetCounterFunction(CounterLookupCallback f)262 void Counters::ResetCounterFunction(CounterLookupCallback f) {
263   stats_table_.SetCounterFunction(f);
264 
265 #define SC(name, caption) name##_.Reset();
266   STATS_COUNTER_LIST_1(SC)
267   STATS_COUNTER_LIST_2(SC)
268   STATS_COUNTER_TS_LIST(SC)
269   STATS_COUNTER_NATIVE_CODE_LIST(SC)
270 #undef SC
271 
272 #define SC(name)              \
273   count_of_##name##_.Reset(); \
274   size_of_##name##_.Reset();
275   INSTANCE_TYPE_LIST(SC)
276 #undef SC
277 
278 #define SC(name)                        \
279   count_of_CODE_TYPE_##name##_.Reset(); \
280   size_of_CODE_TYPE_##name##_.Reset();
281   CODE_KIND_LIST(SC)
282 #undef SC
283 
284 #define SC(name)                          \
285   count_of_FIXED_ARRAY_##name##_.Reset(); \
286   size_of_FIXED_ARRAY_##name##_.Reset();
287   FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC)
288 #undef SC
289 }
290 
ResetCreateHistogramFunction(CreateHistogramCallback f)291 void Counters::ResetCreateHistogramFunction(CreateHistogramCallback f) {
292   stats_table_.SetCreateHistogramFunction(f);
293 
294 #define HR(name, caption, min, max, num_buckets) name##_.Reset();
295   HISTOGRAM_RANGE_LIST(HR)
296 #undef HR
297 
298 #define HT(name, caption, max, res) name##_.Reset();
299   HISTOGRAM_TIMER_LIST(HT)
300 #undef HT
301 
302 #define HT(name, caption, max, res) name##_.Reset();
303   TIMED_HISTOGRAM_LIST(HT)
304 #undef HT
305 
306 #define AHT(name, caption) name##_.Reset();
307   AGGREGATABLE_HISTOGRAM_TIMER_LIST(AHT)
308 #undef AHT
309 
310 #define HP(name, caption) name##_.Reset();
311   HISTOGRAM_PERCENTAGE_LIST(HP)
312 #undef HP
313 
314 #define HM(name, caption) name##_.Reset();
315   HISTOGRAM_LEGACY_MEMORY_LIST(HM)
316 #undef HM
317 }
318 
319 base::TimeTicks (*RuntimeCallTimer::Now)() =
320     &base::TimeTicks::HighResolutionNow;
321 
NowCPUTime()322 base::TimeTicks RuntimeCallTimer::NowCPUTime() {
323   base::ThreadTicks ticks = base::ThreadTicks::Now();
324   return base::TimeTicks::FromInternalValue(ticks.ToInternalValue());
325 }
326 
327 class RuntimeCallStatEntries {
328  public:
Print(std::ostream & os)329   void Print(std::ostream& os) {
330     if (total_call_count == 0) return;
331     std::sort(entries.rbegin(), entries.rend());
332     os << std::setw(50) << "Runtime Function/C++ Builtin" << std::setw(12)
333        << "Time" << std::setw(18) << "Count" << std::endl
334        << std::string(88, '=') << std::endl;
335     for (Entry& entry : entries) {
336       entry.SetTotal(total_time, total_call_count);
337       entry.Print(os);
338     }
339     os << std::string(88, '-') << std::endl;
340     Entry("Total", total_time, total_call_count).Print(os);
341   }
342 
343   // By default, the compiler will usually inline this, which results in a large
344   // binary size increase: std::vector::push_back expands to a large amount of
345   // instructions, and this function is invoked repeatedly by macros.
Add(RuntimeCallCounter * counter)346   V8_NOINLINE void Add(RuntimeCallCounter* counter) {
347     if (counter->count() == 0) return;
348     entries.push_back(
349         Entry(counter->name(), counter->time(), counter->count()));
350     total_time += counter->time();
351     total_call_count += counter->count();
352   }
353 
354  private:
355   class Entry {
356    public:
Entry(const char * name,base::TimeDelta time,uint64_t count)357     Entry(const char* name, base::TimeDelta time, uint64_t count)
358         : name_(name),
359           time_(time.InMicroseconds()),
360           count_(count),
361           time_percent_(100),
362           count_percent_(100) {}
363 
operator <(const Entry & other) const364     bool operator<(const Entry& other) const {
365       if (time_ < other.time_) return true;
366       if (time_ > other.time_) return false;
367       return count_ < other.count_;
368     }
369 
Print(std::ostream & os)370     V8_NOINLINE void Print(std::ostream& os) {
371       os.precision(2);
372       os << std::fixed << std::setprecision(2);
373       os << std::setw(50) << name_;
374       os << std::setw(10) << static_cast<double>(time_) / 1000 << "ms ";
375       os << std::setw(6) << time_percent_ << "%";
376       os << std::setw(10) << count_ << " ";
377       os << std::setw(6) << count_percent_ << "%";
378       os << std::endl;
379     }
380 
SetTotal(base::TimeDelta total_time,uint64_t total_count)381     V8_NOINLINE void SetTotal(base::TimeDelta total_time,
382                               uint64_t total_count) {
383       if (total_time.InMicroseconds() == 0) {
384         time_percent_ = 0;
385       } else {
386         time_percent_ = 100.0 * time_ / total_time.InMicroseconds();
387       }
388       count_percent_ = 100.0 * count_ / total_count;
389     }
390 
391    private:
392     const char* name_;
393     int64_t time_;
394     uint64_t count_;
395     double time_percent_;
396     double count_percent_;
397   };
398 
399   uint64_t total_call_count = 0;
400   base::TimeDelta total_time;
401   std::vector<Entry> entries;
402 };
403 
Reset()404 void RuntimeCallCounter::Reset() {
405   count_ = 0;
406   time_ = 0;
407 }
408 
Dump(v8::tracing::TracedValue * value)409 void RuntimeCallCounter::Dump(v8::tracing::TracedValue* value) {
410   value->BeginArray(name_);
411   value->AppendDouble(count_);
412   value->AppendDouble(time_);
413   value->EndArray();
414 }
415 
Add(RuntimeCallCounter * other)416 void RuntimeCallCounter::Add(RuntimeCallCounter* other) {
417   count_ += other->count();
418   time_ += other->time().InMicroseconds();
419 }
420 
Snapshot()421 void RuntimeCallTimer::Snapshot() {
422   base::TimeTicks now = Now();
423   // Pause only / topmost timer in the timer stack.
424   Pause(now);
425   // Commit all the timer's elapsed time to the counters.
426   RuntimeCallTimer* timer = this;
427   while (timer != nullptr) {
428     timer->CommitTimeToCounter();
429     timer = timer->parent();
430   }
431   Resume(now);
432 }
433 
RuntimeCallStats(ThreadType thread_type)434 RuntimeCallStats::RuntimeCallStats(ThreadType thread_type)
435     : in_use_(false), thread_type_(thread_type) {
436   static const char* const kNames[] = {
437 #define CALL_BUILTIN_COUNTER(name) "GC_" #name,
438       FOR_EACH_GC_COUNTER(CALL_BUILTIN_COUNTER)  //
439 #undef CALL_BUILTIN_COUNTER
440 #define CALL_RUNTIME_COUNTER(name) #name,
441       FOR_EACH_MANUAL_COUNTER(CALL_RUNTIME_COUNTER)  //
442 #undef CALL_RUNTIME_COUNTER
443 #define CALL_RUNTIME_COUNTER(name, nargs, ressize) #name,
444       FOR_EACH_INTRINSIC(CALL_RUNTIME_COUNTER)  //
445 #undef CALL_RUNTIME_COUNTER
446 #define CALL_BUILTIN_COUNTER(name) #name,
447       BUILTIN_LIST_C(CALL_BUILTIN_COUNTER)  //
448 #undef CALL_BUILTIN_COUNTER
449 #define CALL_BUILTIN_COUNTER(name) "API_" #name,
450       FOR_EACH_API_COUNTER(CALL_BUILTIN_COUNTER)  //
451 #undef CALL_BUILTIN_COUNTER
452 #define CALL_BUILTIN_COUNTER(name) #name,
453       FOR_EACH_HANDLER_COUNTER(CALL_BUILTIN_COUNTER)  //
454 #undef CALL_BUILTIN_COUNTER
455 #define THREAD_SPECIFIC_COUNTER(name) #name,
456       FOR_EACH_THREAD_SPECIFIC_COUNTER(THREAD_SPECIFIC_COUNTER)  //
457 #undef THREAD_SPECIFIC_COUNTER
458   };
459   for (int i = 0; i < kNumberOfCounters; i++) {
460     this->counters_[i] = RuntimeCallCounter(kNames[i]);
461   }
462   if (FLAG_rcs_cpu_time) {
463     CHECK(base::ThreadTicks::IsSupported());
464     base::ThreadTicks::WaitUntilInitialized();
465     RuntimeCallTimer::Now = &RuntimeCallTimer::NowCPUTime;
466   }
467 }
468 
469 namespace {
FirstCounter(RuntimeCallCounterId first,...)470 constexpr RuntimeCallCounterId FirstCounter(RuntimeCallCounterId first, ...) {
471   return first;
472 }
473 
474 #define THREAD_SPECIFIC_COUNTER(name) k##name,
475 constexpr RuntimeCallCounterId kFirstThreadVariantCounter =
476     FirstCounter(FOR_EACH_THREAD_SPECIFIC_COUNTER(THREAD_SPECIFIC_COUNTER) 0);
477 #undef THREAD_SPECIFIC_COUNTER
478 
479 #define THREAD_SPECIFIC_COUNTER(name) +1
480 constexpr int kThreadVariantCounterCount =
481     0 FOR_EACH_THREAD_SPECIFIC_COUNTER(THREAD_SPECIFIC_COUNTER);
482 #undef THREAD_SPECIFIC_COUNTER
483 
484 constexpr auto kLastThreadVariantCounter = static_cast<RuntimeCallCounterId>(
485     kFirstThreadVariantCounter + kThreadVariantCounterCount - 1);
486 }  // namespace
487 
HasThreadSpecificCounterVariants(RuntimeCallCounterId id)488 bool RuntimeCallStats::HasThreadSpecificCounterVariants(
489     RuntimeCallCounterId id) {
490   // Check that it's in the range of the thread-specific variant counters and
491   // also that it's one of the background counters.
492   return id >= kFirstThreadVariantCounter && id <= kLastThreadVariantCounter;
493 }
494 
IsBackgroundThreadSpecificVariant(RuntimeCallCounterId id)495 bool RuntimeCallStats::IsBackgroundThreadSpecificVariant(
496     RuntimeCallCounterId id) {
497   return HasThreadSpecificCounterVariants(id) &&
498          (id - kFirstThreadVariantCounter) % 2 == 1;
499 }
500 
Enter(RuntimeCallTimer * timer,RuntimeCallCounterId counter_id)501 void RuntimeCallStats::Enter(RuntimeCallTimer* timer,
502                              RuntimeCallCounterId counter_id) {
503   DCHECK(IsCalledOnTheSameThread());
504   RuntimeCallCounter* counter = GetCounter(counter_id);
505   DCHECK_NOT_NULL(counter->name());
506   timer->Start(counter, current_timer());
507   current_timer_.SetValue(timer);
508   current_counter_.SetValue(counter);
509 }
510 
Leave(RuntimeCallTimer * timer)511 void RuntimeCallStats::Leave(RuntimeCallTimer* timer) {
512   DCHECK(IsCalledOnTheSameThread());
513   RuntimeCallTimer* stack_top = current_timer();
514   if (stack_top == nullptr) return;  // Missing timer is a result of Reset().
515   CHECK(stack_top == timer);
516   current_timer_.SetValue(timer->Stop());
517   RuntimeCallTimer* cur_timer = current_timer();
518   current_counter_.SetValue(cur_timer ? cur_timer->counter() : nullptr);
519 }
520 
Add(RuntimeCallStats * other)521 void RuntimeCallStats::Add(RuntimeCallStats* other) {
522   for (int i = 0; i < kNumberOfCounters; i++) {
523     GetCounter(i)->Add(other->GetCounter(i));
524   }
525 }
526 
527 // static
CorrectCurrentCounterId(RuntimeCallCounterId counter_id,CounterMode mode)528 void RuntimeCallStats::CorrectCurrentCounterId(RuntimeCallCounterId counter_id,
529                                                CounterMode mode) {
530   DCHECK(IsCalledOnTheSameThread());
531   if (mode == RuntimeCallStats::CounterMode::kThreadSpecific) {
532     counter_id = CounterIdForThread(counter_id);
533   }
534   DCHECK(IsCounterAppropriateForThread(counter_id));
535 
536   RuntimeCallTimer* timer = current_timer();
537   if (timer == nullptr) return;
538   RuntimeCallCounter* counter = GetCounter(counter_id);
539   timer->set_counter(counter);
540   current_counter_.SetValue(counter);
541 }
542 
IsCalledOnTheSameThread()543 bool RuntimeCallStats::IsCalledOnTheSameThread() {
544   if (thread_id_.IsValid()) return thread_id_ == ThreadId::Current();
545   thread_id_ = ThreadId::Current();
546   return true;
547 }
548 
Print()549 void RuntimeCallStats::Print() {
550   StdoutStream os;
551   Print(os);
552 }
553 
Print(std::ostream & os)554 void RuntimeCallStats::Print(std::ostream& os) {
555   RuntimeCallStatEntries entries;
556   if (current_timer_.Value() != nullptr) {
557     current_timer_.Value()->Snapshot();
558   }
559   for (int i = 0; i < kNumberOfCounters; i++) {
560     entries.Add(GetCounter(i));
561   }
562   entries.Print(os);
563 }
564 
Reset()565 void RuntimeCallStats::Reset() {
566   if (V8_LIKELY(!TracingFlags::is_runtime_stats_enabled())) return;
567 
568   // In tracing, we only what to trace the time spent on top level trace events,
569   // if runtime counter stack is not empty, we should clear the whole runtime
570   // counter stack, and then reset counters so that we can dump counters into
571   // top level trace events accurately.
572   while (current_timer_.Value()) {
573     current_timer_.SetValue(current_timer_.Value()->Stop());
574   }
575 
576   for (int i = 0; i < kNumberOfCounters; i++) {
577     GetCounter(i)->Reset();
578   }
579 
580   in_use_ = true;
581 }
582 
Dump(v8::tracing::TracedValue * value)583 void RuntimeCallStats::Dump(v8::tracing::TracedValue* value) {
584   for (int i = 0; i < kNumberOfCounters; i++) {
585     if (GetCounter(i)->count() > 0) GetCounter(i)->Dump(value);
586   }
587   in_use_ = false;
588 }
589 
WorkerThreadRuntimeCallStats()590 WorkerThreadRuntimeCallStats::WorkerThreadRuntimeCallStats()
591     : isolate_thread_id_(ThreadId::Current()) {}
592 
~WorkerThreadRuntimeCallStats()593 WorkerThreadRuntimeCallStats::~WorkerThreadRuntimeCallStats() {
594   if (tls_key_) base::Thread::DeleteThreadLocalKey(*tls_key_);
595 }
596 
GetKey()597 base::Thread::LocalStorageKey WorkerThreadRuntimeCallStats::GetKey() {
598   DCHECK(TracingFlags::is_runtime_stats_enabled());
599   if (!tls_key_) tls_key_ = base::Thread::CreateThreadLocalKey();
600   return *tls_key_;
601 }
602 
NewTable()603 RuntimeCallStats* WorkerThreadRuntimeCallStats::NewTable() {
604   DCHECK(TracingFlags::is_runtime_stats_enabled());
605   // Never create a new worker table on the isolate's main thread.
606   DCHECK_NE(ThreadId::Current(), isolate_thread_id_);
607   std::unique_ptr<RuntimeCallStats> new_table =
608       std::make_unique<RuntimeCallStats>(RuntimeCallStats::kWorkerThread);
609   RuntimeCallStats* result = new_table.get();
610 
611   base::MutexGuard lock(&mutex_);
612   tables_.push_back(std::move(new_table));
613   return result;
614 }
615 
AddToMainTable(RuntimeCallStats * main_call_stats)616 void WorkerThreadRuntimeCallStats::AddToMainTable(
617     RuntimeCallStats* main_call_stats) {
618   base::MutexGuard lock(&mutex_);
619   for (auto& worker_stats : tables_) {
620     DCHECK_NE(main_call_stats, worker_stats.get());
621     main_call_stats->Add(worker_stats.get());
622     worker_stats->Reset();
623   }
624 }
625 
WorkerThreadRuntimeCallStatsScope(WorkerThreadRuntimeCallStats * worker_stats)626 WorkerThreadRuntimeCallStatsScope::WorkerThreadRuntimeCallStatsScope(
627     WorkerThreadRuntimeCallStats* worker_stats)
628     : table_(nullptr) {
629   if (V8_LIKELY(!TracingFlags::is_runtime_stats_enabled())) return;
630 
631   table_ = reinterpret_cast<RuntimeCallStats*>(
632       base::Thread::GetThreadLocal(worker_stats->GetKey()));
633   if (table_ == nullptr) {
634     table_ = worker_stats->NewTable();
635     base::Thread::SetThreadLocal(worker_stats->GetKey(), table_);
636   }
637 
638   if ((TracingFlags::runtime_stats.load(std::memory_order_relaxed) &
639        v8::tracing::TracingCategoryObserver::ENABLED_BY_TRACING)) {
640     table_->Reset();
641   }
642 }
643 
~WorkerThreadRuntimeCallStatsScope()644 WorkerThreadRuntimeCallStatsScope::~WorkerThreadRuntimeCallStatsScope() {
645   if (V8_LIKELY(table_ == nullptr)) return;
646 
647   if ((TracingFlags::runtime_stats.load(std::memory_order_relaxed) &
648        v8::tracing::TracingCategoryObserver::ENABLED_BY_TRACING)) {
649     auto value = v8::tracing::TracedValue::Create();
650     table_->Dump(value.get());
651     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats"),
652                          "V8.RuntimeStats", TRACE_EVENT_SCOPE_THREAD,
653                          "runtime-call-stats", std::move(value));
654   }
655 }
656 
657 }  // namespace internal
658 }  // namespace v8
659