summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/bindings/runtime_call_stats.h
diff options
context:
space:
mode:
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.h390
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_