1 // Copyright 2017 The Chromium 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 "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
6 
7 #include <inttypes.h>
8 #include <algorithm>
9 #include "base/time/default_tick_clock.h"
10 #include "third_party/blink/public/web/blink.h"
11 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
12 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
13 #include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
14 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
15 
16 namespace blink {
17 
18 // Wrapper function defined in WebKit.h
LogRuntimeCallStats()19 void LogRuntimeCallStats() {
20   LOG(INFO) << "\n"
21             << RuntimeCallStats::From(MainThreadIsolate())->ToString().Utf8();
22 }
23 
24 namespace {
25 RuntimeCallStats* g_runtime_call_stats_for_testing = nullptr;
26 }
27 
Dump(TracedValue & value) const28 void RuntimeCallCounter::Dump(TracedValue& value) const {
29   value.BeginArray(name_);
30   value.PushDouble(count_);
31   value.PushDouble(time_.InMicrosecondsF());
32   value.EndArray();
33 }
34 
Start(RuntimeCallCounter * counter,RuntimeCallTimer * parent)35 void RuntimeCallTimer::Start(RuntimeCallCounter* counter,
36                              RuntimeCallTimer* parent) {
37   DCHECK(!IsRunning());
38   counter_ = counter;
39   parent_ = parent;
40   start_ticks_ = base::TimeTicks(clock_->NowTicks());
41   if (parent_)
42     parent_->Pause(start_ticks_);
43 }
44 
Stop()45 RuntimeCallTimer* RuntimeCallTimer::Stop() {
46   DCHECK(IsRunning());
47   base::TimeTicks now = base::TimeTicks(clock_->NowTicks());
48   elapsed_time_ += (now - start_ticks_);
49   start_ticks_ = base::TimeTicks();
50   counter_->IncrementAndAddTime(elapsed_time_);
51   if (parent_)
52     parent_->Resume(now);
53   return parent_;
54 }
55 
RuntimeCallStats(const base::TickClock * clock)56 RuntimeCallStats::RuntimeCallStats(const base::TickClock* clock)
57     : clock_(clock) {
58   static const char* const names[] = {
59 #define BINDINGS_COUNTER_NAME(name) "Blink_Bindings_" #name,
60       BINDINGS_COUNTERS(BINDINGS_COUNTER_NAME)  //
61 #undef BINDINGS_COUNTER_NAME
62 #define GC_COUNTER_NAME(name) "Blink_GC_" #name,
63       GC_COUNTERS(GC_COUNTER_NAME)  //
64 #undef GC_COUNTER_NAME
65 #define PARSING_COUNTER_NAME(name) "Blink_Parsing_" #name,
66       PARSING_COUNTERS(PARSING_COUNTER_NAME)  //
67 #undef PARSING_COUNTER_NAME
68 #define STYLE_COUNTER_NAME(name) "Blink_Style_" #name,
69       STYLE_COUNTERS(STYLE_COUNTER_NAME)  //
70 #undef STYLE_COUNTER_NAME
71 #define LAYOUT_COUNTER_NAME(name) "Blink_Layout_" #name,
72       LAYOUT_COUNTERS(LAYOUT_COUNTER_NAME)  //
73 #undef STYLE_COUNTER_NAME
74 #define COUNTER_NAME(name) "Blink_" #name,
75       CALLBACK_COUNTERS(COUNTER_NAME)  //
76       EXTRA_COUNTERS(COUNTER_NAME)
77 #undef COUNTER_NAME
78   };
79 
80   for (int i = 0; i < number_of_counters_; i++) {
81     counters_[i] = RuntimeCallCounter(names[i]);
82   }
83 }
84 
85 // static
From(v8::Isolate * isolate)86 RuntimeCallStats* RuntimeCallStats::From(v8::Isolate* isolate) {
87   if (g_runtime_call_stats_for_testing)
88     return g_runtime_call_stats_for_testing;
89   return V8PerIsolateData::From(isolate)->GetRuntimeCallStats();
90 }
91 
Reset()92 void RuntimeCallStats::Reset() {
93   for (int i = 0; i < number_of_counters_; i++) {
94     counters_[i].Reset();
95   }
96 
97 #if BUILDFLAG(RCS_COUNT_EVERYTHING)
98   for (const auto& counter : counter_map_.Values()) {
99     counter->Reset();
100   }
101 #endif
102 }
103 
Dump(TracedValue & value) const104 void RuntimeCallStats::Dump(TracedValue& value) const {
105   for (int i = 0; i < number_of_counters_; i++) {
106     if (counters_[i].GetCount() > 0)
107       counters_[i].Dump(value);
108   }
109 
110 #if BUILDFLAG(RCS_COUNT_EVERYTHING)
111   for (const auto& counter : counter_map_.Values()) {
112     if (counter->GetCount() > 0)
113       counter->Dump(value);
114   }
115 #endif
116 }
117 
118 namespace {
119 const char row_format[] = "%-55s  %8" PRIu64 "  %9.3f\n";
120 }
121 
ToString() const122 String RuntimeCallStats::ToString() const {
123   StringBuilder builder;
124   builder.Append("Runtime Call Stats for Blink \n");
125   builder.Append(
126       "Name                                                    Count     Time "
127       "(ms)\n\n");
128   for (int i = 0; i < number_of_counters_; i++) {
129     const RuntimeCallCounter* counter = &counters_[i];
130     builder.AppendFormat(row_format, counter->GetName(), counter->GetCount(),
131                          counter->GetTime().InMillisecondsF());
132   }
133 
134 #if BUILDFLAG(RCS_COUNT_EVERYTHING)
135   AddCounterMapStatsToBuilder(builder);
136 #endif
137 
138   return builder.ToString();
139 }
140 
141 // static
SetRuntimeCallStatsForTesting()142 void RuntimeCallStats::SetRuntimeCallStatsForTesting() {
143   DEFINE_STATIC_LOCAL(RuntimeCallStats, s_rcs_for_testing,
144                       (base::DefaultTickClock::GetInstance()));
145   g_runtime_call_stats_for_testing =
146       static_cast<RuntimeCallStats*>(&s_rcs_for_testing);
147 }
148 
149 // static
ClearRuntimeCallStatsForTesting()150 void RuntimeCallStats::ClearRuntimeCallStatsForTesting() {
151   g_runtime_call_stats_for_testing = nullptr;
152 }
153 
154 #if BUILDFLAG(RCS_COUNT_EVERYTHING)
GetCounter(const char * name)155 RuntimeCallCounter* RuntimeCallStats::GetCounter(const char* name) {
156   CounterMap::iterator it = counter_map_.find(name);
157   if (it != counter_map_.end())
158     return it->value.get();
159   return counter_map_.insert(name, std::make_unique<RuntimeCallCounter>(name))
160       .stored_value->value.get();
161 }
162 
CounterMapToSortedArray() const163 Vector<RuntimeCallCounter*> RuntimeCallStats::CounterMapToSortedArray() const {
164   Vector<RuntimeCallCounter*> counters;
165   for (const auto& counter : counter_map_.Values()) {
166     counters.push_back(counter.get());
167   }
168   auto comparator = [](RuntimeCallCounter* a, RuntimeCallCounter* b) {
169     return a->GetCount() == b->GetCount()
170                ? strcmp(a->GetName(), b->GetName()) < 0
171                : a->GetCount() < b->GetCount();
172   };
173   std::sort(counters.begin(), counters.end(), comparator);
174   return counters;
175 }
176 
AddCounterMapStatsToBuilder(StringBuilder & builder) const177 void RuntimeCallStats::AddCounterMapStatsToBuilder(
178     StringBuilder& builder) const {
179   builder.AppendFormat("\nNumber of counters in map: %u\n\n",
180                        counter_map_.size());
181   for (RuntimeCallCounter* counter : CounterMapToSortedArray()) {
182     builder.AppendFormat(row_format, counter->GetName(), counter->GetCount(),
183                          counter->GetTime().InMillisecondsF());
184   }
185 }
186 #endif
187 
188 constexpr const char* RuntimeCallStatsScopedTracer::s_category_group_ =
189     TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats");
190 constexpr const char* RuntimeCallStatsScopedTracer::s_name_ =
191     "BlinkRuntimeCallStats";
192 
AddBeginTraceEventIfEnabled(v8::Isolate * isolate)193 void RuntimeCallStatsScopedTracer::AddBeginTraceEventIfEnabled(
194     v8::Isolate* isolate) {
195   bool category_group_enabled;
196   TRACE_EVENT_CATEGORY_GROUP_ENABLED(s_category_group_,
197                                      &category_group_enabled);
198   if (LIKELY(!category_group_enabled))
199     return;
200 
201   RuntimeCallStats* stats = RuntimeCallStats::From(isolate);
202   if (stats->InUse())
203     return;
204   stats_ = stats;
205   stats_->Reset();
206   stats_->SetInUse(true);
207   TRACE_EVENT_BEGIN0(s_category_group_, s_name_);
208 }
209 
AddEndTraceEvent()210 void RuntimeCallStatsScopedTracer::AddEndTraceEvent() {
211   auto value = std::make_unique<TracedValue>();
212   stats_->Dump(*value);
213   stats_->SetInUse(false);
214   TRACE_EVENT_END1(s_category_group_, s_name_, "runtime-call-stats",
215                    std::move(value));
216 }
217 
218 }  // namespace blink
219