blob: ba5136874efed1e0fa0f9862305c71e012512dbd [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.
#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_TRACING_HELPER_H_
#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_TRACING_HELPER_H_
#include <string>
#include <unordered_set>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/typed_macros.h"
#include "third_party/blink/renderer/platform/platform_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/perfetto/include/perfetto/tracing/event_context.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/track_event.pbzero.h"
namespace blink {
namespace scheduler {
// DISCLAIMER
// Using these constants in TRACE_EVENTs is discouraged nor should you pass any
// non-literal string as a category, unless familiar with tracing internals.
// The constants are implemented as static members of a class to have an unique
// address and not violate ODR.
struct PLATFORM_EXPORT TracingCategoryName {
static constexpr const char kTopLevel[] = "toplevel";
static constexpr const char kDefault[] = "renderer.scheduler";
static constexpr const char kInfo[] =
TRACE_DISABLED_BY_DEFAULT("renderer.scheduler");
static constexpr const char kDebug[] =
TRACE_DISABLED_BY_DEFAULT("renderer.scheduler.debug");
};
namespace internal {
PLATFORM_EXPORT void ValidateTracingCategory(const char* category);
} // namespace internal
PLATFORM_EXPORT double TimeDeltaToMilliseconds(const base::TimeDelta& value);
PLATFORM_EXPORT const char* YesNoStateToString(bool is_yes);
class TraceableVariable;
// Unfortunately, using |base::trace_event::TraceLog::EnabledStateObserver|
// wouldn't be helpful in our case because removing one takes linear time
// and tracers may be created and disposed frequently.
class PLATFORM_EXPORT TraceableVariableController {
DISALLOW_NEW();
public:
TraceableVariableController();
~TraceableVariableController();
// Not thread safe.
void RegisterTraceableVariable(TraceableVariable* traceable_variable);
void DeregisterTraceableVariable(TraceableVariable* traceable_variable);
void OnTraceLogEnabled();
private:
std::unordered_set<TraceableVariable*> traceable_variables_;
};
class TraceableVariable {
public:
TraceableVariable(TraceableVariableController* controller)
: controller_(controller) {
controller_->RegisterTraceableVariable(this);
}
virtual ~TraceableVariable() {
controller_->DeregisterTraceableVariable(this);
}
virtual void OnTraceLogEnabled() = 0;
private:
TraceableVariableController* const controller_; // Not owned.
};
// TRACE_EVENT macros define static variable to cache a pointer to the state
// of category. Hence, we need distinct version for each category in order to
// prevent unintended leak of state.
template <const char* category>
class StateTracer {
DISALLOW_NEW();
public:
explicit StateTracer(const char* name) : name_(name), slice_is_open_(false) {
internal::ValidateTracingCategory(category);
}
~StateTracer() {
if (slice_is_open_)
TRACE_EVENT_NESTABLE_ASYNC_END0(category, name_, TRACE_ID_LOCAL(this));
}
// String will be copied before leaving this function.
void TraceString(const String& state) {
TraceImpl(state.Utf8().c_str(), true);
}
// Trace compile-time defined const string, so no copy needed.
// Null may be passed to indicate the absence of state.
void TraceCompileTimeString(const char* state) { TraceImpl(state, false); }
protected:
bool is_enabled() const {
bool result = false;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(category, &result); // Cached.
return result;
}
private:
void TraceImpl(const char* state, bool need_copy) {
if (slice_is_open_) {
TRACE_EVENT_NESTABLE_ASYNC_END0(category, name_, TRACE_ID_LOCAL(this));
slice_is_open_ = false;
}
if (!state || !is_enabled())
return;
if (need_copy) {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(category, name_, TRACE_ID_LOCAL(this),
"state", TRACE_STR_COPY(state));
} else {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(category, name_, TRACE_ID_LOCAL(this),
"state", state);
}
slice_is_open_ = true;
}
const char* const name_; // Not owned.
// We have to track whether slice is open to avoid confusion since assignment,
// "absent" state and OnTraceLogEnabled can happen anytime.
bool slice_is_open_;
DISALLOW_COPY_AND_ASSIGN(StateTracer);
};
// TODO(kraynov): Rename to something less generic and reflecting
// the enum nature of such variables.
template <typename T, const char* category>
class TraceableState : public TraceableVariable, private StateTracer<category> {
public:
// Converter must return compile-time defined const strings because tracing
// will not make a copy of them.
using ConverterFuncPtr = const char* (*)(T);
TraceableState(T initial_state,
const char* name,
TraceableVariableController* controller,
ConverterFuncPtr converter)
: TraceableVariable(controller),
StateTracer<category>(name),
converter_(converter),
state_(initial_state) {
Trace();
}
~TraceableState() override = default;
TraceableState& operator=(const T& value) {
Assign(value);
return *this;
}
TraceableState& operator=(const TraceableState& another) {
Assign(another.state_);
return *this;
}
operator T() const { return state_; }
const T& get() const { return state_; }
void OnTraceLogEnabled() final { Trace(); }
// TraceableState<T> is serialisable into trace iff T is serialisable.
template <typename V = T>
typename perfetto::check_traced_value_support<V>::type WriteIntoTracedValue(
perfetto::TracedValue context) const {
perfetto::WriteIntoTracedValue(std::move(context), get());
}
protected:
void Assign(T new_state) {
if (state_ != new_state) {
state_ = new_state;
Trace();
}
}
void (*mock_trace_for_test_)(const char*) = nullptr;
private:
void Trace() {
if (UNLIKELY(mock_trace_for_test_)) {
mock_trace_for_test_(converter_(state_));
return;
}
// Null state string means the absence of state.
const char* state_str = nullptr;
if (StateTracer<category>::is_enabled()) {
state_str = converter_(state_);
}
// We have to be explicit to deal with two-phase name lookup in templates:
// http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html
StateTracer<category>::TraceCompileTimeString(state_str);
}
const ConverterFuncPtr converter_;
T state_;
DISALLOW_COPY(TraceableState);
};
template <const char* category, typename TypedValue>
class ProtoStateTracer {
DISALLOW_NEW();
public:
explicit ProtoStateTracer(const char* name)
: name_(name), slice_is_open_(false) {
internal::ValidateTracingCategory(category);
}
ProtoStateTracer(const ProtoStateTracer&) = delete;
ProtoStateTracer& operator=(const ProtoStateTracer&) = delete;
~ProtoStateTracer() {
if (slice_is_open_) {
TRACE_EVENT_END(category, track());
}
}
void TraceProto(TypedValue* value) {
const auto trace_track = track();
if (slice_is_open_) {
TRACE_EVENT_END(category, trace_track);
slice_is_open_ = false;
}
if (!is_enabled())
return;
TRACE_EVENT_BEGIN(category, perfetto::StaticString{name_}, trace_track,
[value](perfetto::EventContext ctx) {
value->AsProtozeroInto(ctx.event());
});
slice_is_open_ = true;
}
protected:
bool is_enabled() const {
bool result = false;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(category, &result); // Cached.
return result;
}
private:
perfetto::Track track() const {
return perfetto::Track(reinterpret_cast<uint64_t>(this));
}
const char* const name_; // Not owned.
// We have to track whether slice is open to avoid confusion since assignment,
// "absent" state and OnTraceLogEnabled can happen anytime.
bool slice_is_open_;
};
template <typename T>
using InitializeProtoFuncPtr =
void (*)(perfetto::protos::pbzero::TrackEvent* event, T e);
template <typename T, const char* category>
class TraceableObjectState
: public TraceableVariable,
public ProtoStateTracer<category, TraceableObjectState<T, category>> {
public:
TraceableObjectState(T initial_state,
const char* name,
TraceableVariableController* controller,
InitializeProtoFuncPtr<T> proto_init_func)
: TraceableVariable(controller),
ProtoStateTracer<category, TraceableObjectState<T, category>>(name),
state_(initial_state),
proto_init_func_(proto_init_func) {
Trace();
}
TraceableObjectState(const TraceableObjectState&) = delete;
~TraceableObjectState() override = default;
TraceableObjectState& operator=(const T& value) {
Assign(value);
return *this;
}
TraceableObjectState& operator=(const TraceableObjectState& another) {
Assign(another.state_);
return *this;
}
const T& get() const { return state_; }
void OnTraceLogEnabled() final { Trace(); }
void AsProtozeroInto(perfetto::protos::pbzero::TrackEvent* event) {
proto_init_func_(event, state_);
}
protected:
void Assign(T new_state) {
if (state_ != new_state) {
state_ = new_state;
Trace();
}
}
private:
void Trace() {
ProtoStateTracer<category, TraceableObjectState<T, category>>::TraceProto(
this);
}
bool is_enabled() const {
bool result = false;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(category, &result); // Cached.
return result;
}
T state_;
InitializeProtoFuncPtr<T> proto_init_func_;
};
template <typename T, const char* category>
class TraceableCounter : public TraceableVariable {
public:
using ConverterFuncPtr = double (*)(const T&);
TraceableCounter(T initial_value,
const char* name,
TraceableVariableController* controller,
ConverterFuncPtr converter)
: TraceableVariable(controller),
name_(name),
converter_(converter),
value_(initial_value) {
internal::ValidateTracingCategory(category);
Trace();
}
TraceableCounter(T initial_value,
const char* name,
TraceableVariableController* controller)
: TraceableCounter(initial_value, name, controller, [](const T& value) {
return static_cast<double>(value);
}) {}
TraceableCounter& operator=(const T& value) {
value_ = value;
Trace();
return *this;
}
TraceableCounter& operator=(const TraceableCounter& another) {
value_ = another.value_;
Trace();
return *this;
}
TraceableCounter& operator+=(const T& value) {
value_ += value;
Trace();
return *this;
}
TraceableCounter& operator-=(const T& value) {
value_ -= value;
Trace();
return *this;
}
const T& value() const { return value_; }
const T* operator->() const { return &value_; }
operator T() const { return value_; }
void OnTraceLogEnabled() final { Trace(); }
void Trace() const {
TRACE_COUNTER_ID1(category, name_, this, converter_(value_));
}
private:
const char* const name_; // Not owned.
const ConverterFuncPtr converter_;
T value_;
DISALLOW_COPY(TraceableCounter);
};
// Add operators when it's needed.
template <typename T, const char* category>
constexpr T operator-(const TraceableCounter<T, category>& counter) {
return -counter.value();
}
template <typename T, const char* category>
constexpr T operator/(const TraceableCounter<T, category>& lhs, const T& rhs) {
return lhs.value() / rhs;
}
template <typename T, const char* category>
constexpr bool operator>(const TraceableCounter<T, category>& lhs,
const T& rhs) {
return lhs.value() > rhs;
}
template <typename T, const char* category>
constexpr bool operator<(const TraceableCounter<T, category>& lhs,
const T& rhs) {
return lhs.value() < rhs;
}
template <typename T, const char* category>
constexpr bool operator!=(const TraceableCounter<T, category>& lhs,
const T& rhs) {
return lhs.value() != rhs;
}
template <typename T, const char* category>
constexpr T operator++(TraceableCounter<T, category>& counter) {
counter = counter.value() + 1;
return counter.value();
}
template <typename T, const char* category>
constexpr T operator--(TraceableCounter<T, category>& counter) {
counter = counter.value() - 1;
return counter.value();
}
template <typename T, const char* category>
constexpr T operator++(TraceableCounter<T, category>& counter, int) {
T value = counter.value();
counter = value + 1;
return value;
}
template <typename T, const char* category>
constexpr T operator--(TraceableCounter<T, category>& counter, int) {
T value = counter.value();
counter = value - 1;
return value;
}
} // namespace scheduler
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_TRACING_HELPER_H_