diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h')
-rw-r--r-- | chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h new file mode 100644 index 00000000000..ecc3c01c90d --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h @@ -0,0 +1,390 @@ +// 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. + +// This file contains the Blink version of RuntimeCallStats which is implemented +// by V8 in //v8/src/counters.h + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_RUNTIME_CALL_STATS_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_RUNTIME_CALL_STATS_H_ + +#include "third_party/blink/renderer/platform/bindings/runtime_call_stats_count_everything_buildflags.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/platform_export.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" +#include "third_party/blink/renderer/platform/wtf/allocator.h" +#include "third_party/blink/renderer/platform/wtf/optional.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/platform/wtf/time.h" +#include "v8/include/v8.h" + +namespace base { +class TickClock; +} + +namespace blink { + +// A simple counter used to track total execution count & time for a particular +// function/scope. +class PLATFORM_EXPORT RuntimeCallCounter { + public: + explicit RuntimeCallCounter(const char* name) : count_(0), name_(name) {} + + void IncrementAndAddTime(TimeDelta time) { + count_++; + time_ += time; + } + + uint64_t GetCount() const { return count_; } + TimeDelta GetTime() const { return time_; } + const char* GetName() const { return name_; } + + void Reset() { + time_ = TimeDelta(); + count_ = 0; + } + + void Dump(TracedValue&) const; + + private: + RuntimeCallCounter() = default; + + uint64_t count_; + TimeDelta time_; + const char* name_; + + friend class RuntimeCallStats; +}; + +// Used to track elapsed time for a counter. +// NOTE: Do not use this class directly to track execution times, instead use it +// with the macros below. +class PLATFORM_EXPORT RuntimeCallTimer { + public: + explicit RuntimeCallTimer(const base::TickClock* clock) : clock_(clock) {} + ~RuntimeCallTimer() { DCHECK(!IsRunning()); }; + + // Starts recording time for <counter>, and pauses <parent> (if non-null). + void Start(RuntimeCallCounter*, RuntimeCallTimer* parent); + + // Stops recording time for the counter passed in Start(), and also updates + // elapsed time and increments the count stored by the counter. It also + // resumes the parent timer passed in Start() (if any). + RuntimeCallTimer* Stop(); + + // Resets the timer. Call this before reusing a timer. + void Reset() { + start_ticks_ = TimeTicks(); + elapsed_time_ = TimeDelta(); + } + + private: + void Pause(TimeTicks now) { + DCHECK(IsRunning()); + elapsed_time_ += (now - start_ticks_); + start_ticks_ = TimeTicks(); + } + + void Resume(TimeTicks now) { + DCHECK(!IsRunning()); + start_ticks_ = now; + } + + bool IsRunning() { return start_ticks_ != TimeTicks(); } + + RuntimeCallCounter* counter_; + RuntimeCallTimer* parent_; + TimeTicks start_ticks_; + TimeDelta elapsed_time_; + const base::TickClock* clock_ = nullptr; +}; + +// Macros that take RuntimeCallStats as a parameter; used only in +// RuntimeCallStatsTest. +#define RUNTIME_CALL_STATS_ENTER_WITH_RCS(runtime_call_stats, timer, \ + counterId) \ + if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \ + (runtime_call_stats)->Enter(timer, counterId); \ + } + +#define RUNTIME_CALL_STATS_LEAVE_WITH_RCS(runtime_call_stats, timer) \ + if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \ + (runtime_call_stats)->Leave(timer); \ + } + +#define RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(runtime_call_stats, counterId) \ + Optional<RuntimeCallTimerScope> rcs_scope; \ + if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \ + rcs_scope.emplace(runtime_call_stats, counterId); \ + } + +#define RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS( \ + optional_scope_name, runtime_call_stats, counterId) \ + if (UNLIKELY(RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) { \ + optional_scope_name.emplace(runtime_call_stats, counterId); \ + } + +// Use the macros below instead of directly using RuntimeCallStats::Enter, +// RuntimeCallStats::Leave and RuntimeCallTimerScope. They force an early +// exit if Runtime Call Stats is disabled. +#define RUNTIME_CALL_STATS_ENTER(isolate, timer, counterId) \ + RUNTIME_CALL_STATS_ENTER_WITH_RCS(RuntimeCallStats::From(isolate), timer, \ + counterId) + +#define RUNTIME_CALL_STATS_LEAVE(isolate, timer) \ + RUNTIME_CALL_STATS_LEAVE_WITH_RCS(RuntimeCallStats::From(isolate), timer) + +#define RUNTIME_CALL_TIMER_SCOPE(isolate, counterId) \ + RUNTIME_CALL_TIMER_SCOPE_WITH_RCS(RuntimeCallStats::From(isolate), counterId) + +#define RUNTIME_CALL_TIMER_SCOPE_IF_ISOLATE_EXISTS(isolate, counterId) \ + Optional<RuntimeCallTimerScope> rcs_scope; \ + if (isolate) { \ + RUNTIME_CALL_TIMER_SCOPE_WITH_OPTIONAL_RCS( \ + rcs_scope, RuntimeCallStats::From(isolate), counterId) \ + } + +// Used in places which do not have a counter explicitly defined in +// FOR_EACH_COUNTER. This is a no-op by default (when RCS_COUNT_EVERYTHING is +// not set). +#if BUILDFLAG(RCS_COUNT_EVERYTHING) +#define RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(isolate, counterName) \ + RUNTIME_CALL_TIMER_SCOPE(isolate, counterName) +#else +#define RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(isolate, counterName) \ + do { \ + } while (false) +#endif + +// Maintains a stack of timers and provides functions to manage recording scopes +// by pausing and resuming timers in the chain when entering and leaving a +// scope. +class PLATFORM_EXPORT RuntimeCallStats { + public: + explicit RuntimeCallStats(const base::TickClock*); + // Get RuntimeCallStats object associated with the given isolate. + static RuntimeCallStats* From(v8::Isolate*); + +// The following 3 macros are used to define counters that are used in the +// bindings layer to measure call stats for IDL interface methods and +// attributes. Also see documentation for [RuntimeCallStatsCounter] in +// bindings/IDLExtendedAttributes.md. + +// Use this to define a counter for IDL interface methods. +// [RuntimeCallStatsCounter=MethodCounter] void method() => +// BINDINGS_METHOD(V, MethodCounter) +#define BINDINGS_METHOD(V, counter) V(counter) + +// Use this to define a counter for IDL readonly attributes. +// [RuntimeCallStatsCounter=AttributeCounter] readonly attribute boolean attr => +// BINDINGS_READ_ONLY_ATTRIBUTE(V, AttributeCounter) +#define BINDINGS_READ_ONLY_ATTRIBUTE(V, counter) V(counter##_Getter) + +// Use this to define counters for IDL attributes (defines a counter each for +// getter and setter). +// [RuntimeCallStats=AttributeCounter] attribute long attr +// => BINDINGS_ATTRIBUTE(V, AttributeCounter) +#define BINDINGS_ATTRIBUTE(V, counter) \ + V(counter##_Getter) \ + V(counter##_Setter) + +// Counters + +#define BINDINGS_COUNTERS(V) \ + V(AssociateObjectWithWrapper) \ + V(CreateWrapper) \ + V(GetEventListener) \ + V(HasInstance) \ + V(ToExecutionContext) \ + V(ToV8DOMWindow) \ + V(ToV8SequenceInternal) \ + V(SetReturnValueFromStringSlow) \ + V(V8ExternalStringSlow) + +#define GC_COUNTERS(V) \ + V(CollectGarbage) \ + V(GcEpilogue) \ + V(GcPrologue) \ + V(PerformIdleLazySweep) + +#define PARSING_COUNTERS(V) \ + V(DocumentFragmentParseHTML) \ + V(ParserAppendChild) \ + V(ReplaceChildrenWithFragment) \ + V(HTMLTokenizerNextToken) \ + V(ConstructTree) + +#define STYLE_COUNTERS(V) \ + V(ProcessStyleSheet) \ + V(UpdateStyle) + +#define LAYOUT_COUNTERS(V) \ + V(UpdateLayout) \ + V(UpdateLayerPositionsAfterLayout) + +#define CALLBACK_COUNTERS(V) \ + BINDINGS_METHOD(V, ElementGetBoundingClientRect) \ + BINDINGS_METHOD(V, EventTargetDispatchEvent) \ + BINDINGS_METHOD(V, HTMLElementClick) \ + BINDINGS_METHOD(V, NodeAppendChild) \ + BINDINGS_METHOD(V, NodeRemoveChild) \ + BINDINGS_METHOD(V, WindowSetTimeout) \ + BINDINGS_ATTRIBUTE(V, DocumentCookie) \ + BINDINGS_ATTRIBUTE(V, ElementInnerHTML) \ + BINDINGS_READ_ONLY_ATTRIBUTE(V, NodeName) + +#define EXTRA_COUNTERS(V) \ + V(V8) \ + V(TestCounter1) \ + V(TestCounter2) \ + BINDINGS_METHOD(V, BindingsMethodTestCounter) \ + BINDINGS_READ_ONLY_ATTRIBUTE(V, BindingsReadOnlyAttributeTestCounter) \ + BINDINGS_ATTRIBUTE(V, BindingsAttributeTestCounter) + +#define FOR_EACH_COUNTER(V) \ + BINDINGS_COUNTERS(V) \ + GC_COUNTERS(V) \ + PARSING_COUNTERS(V) \ + STYLE_COUNTERS(V) \ + LAYOUT_COUNTERS(V) \ + CALLBACK_COUNTERS(V) \ + EXTRA_COUNTERS(V) + + enum class CounterId : uint16_t { +#define ADD_ENUM_VALUE(counter) k##counter, + FOR_EACH_COUNTER(ADD_ENUM_VALUE) +#undef ADD_ENUM_VALUE + kNumberOfCounters + }; + + // Enters a new recording scope by pausing the currently running timer that + // was started by the current instance, and starting <timer>. + // NOTE: Do not use this function directly, use RUNTIME_CALL_STATS_ENTER. + void Enter(RuntimeCallTimer* timer, CounterId id) { + timer->Start(GetCounter(id), current_timer_); + current_timer_ = timer; + } + +#if BUILDFLAG(RCS_COUNT_EVERYTHING) + void Enter(RuntimeCallTimer* timer, const char* id) { + timer->Start(GetCounter(id), current_timer_); + current_timer_ = timer; + } +#endif + + // Exits the current recording scope, by stopping <timer> (and updating the + // counter associated with <timer>) and resuming the timer that was paused + // before entering the current scope. + // NOTE: Do not use this function directly, use RUNTIME_CALL_STATS_LEAVE. + void Leave(RuntimeCallTimer* timer) { + DCHECK_EQ(timer, current_timer_); + current_timer_ = timer->Stop(); + } + + // Reset all the counters. + void Reset(); + + void Dump(TracedValue&) const; + + bool InUse() const { return in_use_; } + void SetInUse(bool in_use) { in_use_ = in_use; } + + RuntimeCallCounter* GetCounter(CounterId id) { + return &(counters_[static_cast<uint16_t>(id)]); + } + + String ToString() const; + + static void SetRuntimeCallStatsForTesting(); + static void ClearRuntimeCallStatsForTesting(); + +#if BUILDFLAG(RCS_COUNT_EVERYTHING) + // Look up counter in counter map. If counter doesn't exist, a new counter is + // created and inserted into the map. + RuntimeCallCounter* GetCounter(const char* name); +#endif + + const base::TickClock* clock() const { return clock_; } + + private: + RuntimeCallTimer* current_timer_ = nullptr; + bool in_use_ = false; + RuntimeCallCounter counters_[static_cast<int>(CounterId::kNumberOfCounters)]; + static const int number_of_counters_ = + static_cast<int>(CounterId::kNumberOfCounters); + const base::TickClock* clock_ = nullptr; + +#if BUILDFLAG(RCS_COUNT_EVERYTHING) + typedef HashMap<const char*, std::unique_ptr<RuntimeCallCounter>> CounterMap; + CounterMap counter_map_; + + Vector<RuntimeCallCounter*> CounterMapToSortedArray() const; + void AddCounterMapStatsToBuilder(StringBuilder&) const; +#endif +}; + +// A utility class that creates a RuntimeCallTimer and uses it with +// RuntimeCallStats to measure execution time of a C++ scope. +// Do not use this class directly, use RUNTIME_CALL_TIMER_SCOPE instead. +class PLATFORM_EXPORT RuntimeCallTimerScope { + public: + RuntimeCallTimerScope(RuntimeCallStats* stats, + RuntimeCallStats::CounterId counter) + : call_stats_(stats), timer_(stats->clock()) { + call_stats_->Enter(&timer_, counter); + } +#if BUILDFLAG(RCS_COUNT_EVERYTHING) + RuntimeCallTimerScope(RuntimeCallStats* stats, const char* counterName) + : call_stats_(stats), timer_(stats->clock()) { + call_stats_->Enter(&timer_, counterName); + } +#endif + ~RuntimeCallTimerScope() { call_stats_->Leave(&timer_); } + + private: + RuntimeCallStats* call_stats_; + RuntimeCallTimer timer_; +}; + +// Creates scoped begin and end trace events. The end trace event also contains +// a dump of RuntimeCallStats collected to that point (and the stats are reset +// before sending a begin event). Use this to define regions where +// RuntimeCallStats data is collected and dumped through tracing. +// NOTE: Nested scoped tracers will not send events of their own, the stats +// collected in their scopes will be dumped by the root tracer scope. +class PLATFORM_EXPORT RuntimeCallStatsScopedTracer { + public: + explicit RuntimeCallStatsScopedTracer(v8::Isolate* isolate) { + bool category_group_enabled; + TRACE_EVENT_CATEGORY_GROUP_ENABLED(s_category_group_, + &category_group_enabled); + if (LIKELY(!category_group_enabled || + !RuntimeEnabledFeatures::BlinkRuntimeCallStatsEnabled())) + return; + + RuntimeCallStats* stats = RuntimeCallStats::From(isolate); + if (!stats->InUse()) { + stats_ = stats; + AddBeginTraceEvent(); + } + } + + ~RuntimeCallStatsScopedTracer() { + if (stats_) + AddEndTraceEvent(); + } + + private: + void AddBeginTraceEvent(); + void AddEndTraceEvent(); + + static const char* const s_category_group_; + static const char* const s_name_; + + RuntimeCallStats* stats_ = nullptr; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_RUNTIME_CALL_STATS_H_ |