blob: 4c248ff1a62a8e70573b5f94be6f0ffbb98062df [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
#include <inttypes.h>
#include <algorithm>
#include "base/time/default_tick_clock.h"
#include "third_party/blink/public/web/blink.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
// Wrapper function defined in WebKit.h
void LogRuntimeCallStats() {
LOG(INFO)
<< "\n"
<< RuntimeCallStats::From(MainThreadIsolate())->ToString().Utf8().data();
}
namespace {
RuntimeCallStats* g_runtime_call_stats_for_testing = nullptr;
}
void RuntimeCallCounter::Dump(TracedValue& value) const {
value.BeginArray(name_);
value.PushDouble(count_);
value.PushDouble(time_.InMicrosecondsF());
value.EndArray();
}
void RuntimeCallTimer::Start(RuntimeCallCounter* counter,
RuntimeCallTimer* parent) {
DCHECK(!IsRunning());
counter_ = counter;
parent_ = parent;
start_ticks_ = TimeTicks(clock_->NowTicks());
if (parent_)
parent_->Pause(start_ticks_);
}
RuntimeCallTimer* RuntimeCallTimer::Stop() {
DCHECK(IsRunning());
TimeTicks now = TimeTicks(clock_->NowTicks());
elapsed_time_ += (now - start_ticks_);
start_ticks_ = TimeTicks();
counter_->IncrementAndAddTime(elapsed_time_);
if (parent_)
parent_->Resume(now);
return parent_;
}
RuntimeCallStats::RuntimeCallStats(const base::TickClock* clock)
: clock_(clock) {
static const char* const names[] = {
#define BINDINGS_COUNTER_NAME(name) "Blink_Bindings_" #name,
BINDINGS_COUNTERS(BINDINGS_COUNTER_NAME) //
#undef BINDINGS_COUNTER_NAME
#define GC_COUNTER_NAME(name) "Blink_GC_" #name,
GC_COUNTERS(GC_COUNTER_NAME) //
#undef GC_COUNTER_NAME
#define PARSING_COUNTER_NAME(name) "Blink_Parsing_" #name,
PARSING_COUNTERS(PARSING_COUNTER_NAME) //
#undef PARSING_COUNTER_NAME
#define STYLE_COUNTER_NAME(name) "Blink_Style_" #name,
STYLE_COUNTERS(STYLE_COUNTER_NAME) //
#undef STYLE_COUNTER_NAME
#define LAYOUT_COUNTER_NAME(name) "Blink_Layout_" #name,
LAYOUT_COUNTERS(LAYOUT_COUNTER_NAME) //
#undef STYLE_COUNTER_NAME
#define COUNTER_NAME(name) "Blink_" #name,
CALLBACK_COUNTERS(COUNTER_NAME) //
EXTRA_COUNTERS(COUNTER_NAME)
#undef COUNTER_NAME
};
for (int i = 0; i < number_of_counters_; i++) {
counters_[i] = RuntimeCallCounter(names[i]);
}
}
// static
RuntimeCallStats* RuntimeCallStats::From(v8::Isolate* isolate) {
if (g_runtime_call_stats_for_testing)
return g_runtime_call_stats_for_testing;
return V8PerIsolateData::From(isolate)->GetRuntimeCallStats();
}
void RuntimeCallStats::Reset() {
for (int i = 0; i < number_of_counters_; i++) {
counters_[i].Reset();
}
#if BUILDFLAG(RCS_COUNT_EVERYTHING)
for (const auto& counter : counter_map_.Values()) {
counter->Reset();
}
#endif
}
void RuntimeCallStats::Dump(TracedValue& value) const {
for (int i = 0; i < number_of_counters_; i++) {
if (counters_[i].GetCount() > 0)
counters_[i].Dump(value);
}
#if BUILDFLAG(RCS_COUNT_EVERYTHING)
for (const auto& counter : counter_map_.Values()) {
if (counter->GetCount() > 0)
counter->Dump(value);
}
#endif
}
namespace {
const char row_format[] = "%-55s %8" PRIu64 " %9.3f\n";
}
String RuntimeCallStats::ToString() const {
StringBuilder builder;
builder.Append("Runtime Call Stats for Blink \n");
builder.Append(
"Name Count Time "
"(ms)\n\n");
for (int i = 0; i < number_of_counters_; i++) {
const RuntimeCallCounter* counter = &counters_[i];
builder.Append(String::Format(row_format, counter->GetName(),
counter->GetCount(),
counter->GetTime().InMillisecondsF()));
}
#if BUILDFLAG(RCS_COUNT_EVERYTHING)
AddCounterMapStatsToBuilder(builder);
#endif
return builder.ToString();
}
// static
void RuntimeCallStats::SetRuntimeCallStatsForTesting() {
DEFINE_STATIC_LOCAL(RuntimeCallStats, s_rcs_for_testing,
(base::DefaultTickClock::GetInstance()));
g_runtime_call_stats_for_testing =
static_cast<RuntimeCallStats*>(&s_rcs_for_testing);
}
// static
void RuntimeCallStats::ClearRuntimeCallStatsForTesting() {
g_runtime_call_stats_for_testing = nullptr;
}
#if BUILDFLAG(RCS_COUNT_EVERYTHING)
RuntimeCallCounter* RuntimeCallStats::GetCounter(const char* name) {
CounterMap::iterator it = counter_map_.find(name);
if (it != counter_map_.end())
return it->value.get();
return counter_map_.insert(name, std::make_unique<RuntimeCallCounter>(name))
.stored_value->value.get();
}
Vector<RuntimeCallCounter*> RuntimeCallStats::CounterMapToSortedArray() const {
Vector<RuntimeCallCounter*> counters;
for (const auto& counter : counter_map_.Values()) {
counters.push_back(counter.get());
}
auto comparator = [](RuntimeCallCounter* a, RuntimeCallCounter* b) {
return a->GetCount() == b->GetCount()
? strcmp(a->GetName(), b->GetName()) < 0
: a->GetCount() < b->GetCount();
};
std::sort(counters.begin(), counters.end(), comparator);
return counters;
}
void RuntimeCallStats::AddCounterMapStatsToBuilder(
StringBuilder& builder) const {
builder.Append(String::Format("\nNumber of counters in map: %u\n\n",
counter_map_.size()));
for (RuntimeCallCounter* counter : CounterMapToSortedArray()) {
builder.Append(String::Format(row_format, counter->GetName(),
counter->GetCount(),
counter->GetTime().InMillisecondsF()));
}
}
#endif
const char* const RuntimeCallStatsScopedTracer::s_category_group_ =
TRACE_DISABLED_BY_DEFAULT("v8.runtime_stats");
const char* const RuntimeCallStatsScopedTracer::s_name_ =
"BlinkRuntimeCallStats";
void RuntimeCallStatsScopedTracer::AddBeginTraceEventIfEnabled(
v8::Isolate* isolate) {
bool category_group_enabled;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(s_category_group_,
&category_group_enabled);
if (LIKELY(!category_group_enabled))
return;
RuntimeCallStats* stats = RuntimeCallStats::From(isolate);
if (stats->InUse())
return;
stats_ = stats;
stats_->Reset();
stats_->SetInUse(true);
TRACE_EVENT_BEGIN0(s_category_group_, s_name_);
}
void RuntimeCallStatsScopedTracer::AddEndTraceEvent() {
std::unique_ptr<TracedValue> value = TracedValue::Create();
stats_->Dump(*value);
stats_->SetInUse(false);
TRACE_EVENT_END1(s_category_group_, s_name_, "runtime-call-stats",
std::move(value));
}
} // namespace blink