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