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