blob: 43a8de13d4299387bc1c77582d33542371ffd74f [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDE_PERFETTO_TRACING_TRACK_H_
#define INCLUDE_PERFETTO_TRACING_TRACK_H_
#include "perfetto/base/export.h"
#include "perfetto/base/proc_utils.h"
#include "perfetto/base/thread_utils.h"
#include "perfetto/protozero/message_handle.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "perfetto/tracing/internal/fnv1a.h"
#include "perfetto/tracing/internal/tracing_muxer.h"
#include "perfetto/tracing/platform.h"
#include "perfetto/tracing/string_helpers.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h" // IWYU pragma: export
#include "protos/perfetto/trace/track_event/counter_descriptor.gen.h" // IWYU pragma: export
#include "protos/perfetto/trace/track_event/counter_descriptor.pbzero.h" // IWYU pragma: export
#include "protos/perfetto/trace/track_event/process_descriptor.gen.h" // IWYU pragma: export
#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h" // IWYU pragma: export
#include "protos/perfetto/trace/track_event/thread_descriptor.gen.h" // IWYU pragma: export
#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h" // IWYU pragma: export
#include "protos/perfetto/trace/track_event/track_descriptor.gen.h" // IWYU pragma: export
#include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h" // IWYU pragma: export
#include <stdint.h>
#include <map>
#include <mutex>
namespace perfetto {
namespace internal {
class TrackRegistry;
}
class Flow;
class TerminatingFlow;
// Track events are recorded on a timeline track, which maintains the relative
// time ordering of all events on that track. Each thread has its own default
// track (ThreadTrack), which is by default where all track events are written.
// Thread tracks are grouped under their hosting process (ProcessTrack).
// Events which aren't strictly scoped to a thread or a process, or don't
// correspond to synchronous code execution on a thread can use a custom
// track (Track, ThreadTrack or ProcessTrack). A Track object can also
// optionally be parented to a thread or a process.
//
// A track is represented by a uuid, which must be unique across the entire
// recorded trace.
//
// For example, to record an event that begins and ends on different threads,
// use a matching id to tie the begin and end events together:
//
// TRACE_EVENT_BEGIN("category", "AsyncEvent", perfetto::Track(8086));
// ...
// TRACE_EVENT_END("category", perfetto::Track(8086));
//
// Tracks can also be annotated with metadata:
//
// auto desc = track.Serialize();
// desc.set_name("MyTrack");
// perfetto::TrackEvent::SetTrackDescriptor(track, desc);
//
// Threads and processes can also be named in a similar way, e.g.:
//
// auto desc = perfetto::ProcessTrack::Current().Serialize();
// desc.mutable_process()->set_process_name("MyProcess");
// perfetto::TrackEvent::SetTrackDescriptor(
// perfetto::ProcessTrack::Current(), desc);
//
// The metadata remains valid between tracing sessions. To free up data for a
// track, call EraseTrackDescriptor:
//
// perfetto::TrackEvent::EraseTrackDescriptor(track);
//
struct PERFETTO_EXPORT_COMPONENT Track {
const uint64_t uuid;
const uint64_t parent_uuid;
constexpr Track() : uuid(0), parent_uuid(0) {}
// Construct a track with identifier |id|, optionally parented under |parent|.
// If no parent is specified, the track's parent is the current process's
// track.
//
// To minimize the chances for accidental id collisions across processes, the
// track's effective uuid is generated by xorring |id| with a random,
// per-process cookie.
explicit constexpr Track(uint64_t id, Track parent = MakeProcessTrack())
: uuid(id ^ parent.uuid), parent_uuid(parent.uuid) {}
explicit operator bool() const { return uuid; }
void Serialize(protos::pbzero::TrackDescriptor*) const;
protos::gen::TrackDescriptor Serialize() const;
// Construct a global track with identifier |id|.
//
// Beware: the globally unique |id| should be chosen carefully to avoid
// accidental clashes with track identifiers emitted by other producers.
static Track Global(uint64_t id) { return Track(id, Track()); }
// Construct a track using |ptr| as identifier.
static Track FromPointer(const void* ptr, Track parent = MakeProcessTrack()) {
// Using pointers as global TrackIds isn't supported as pointers are
// per-proccess and the same pointer value can be used in different
// processes. If you hit this check but are providing no |parent| track,
// verify that Tracing::Initialize() was called for the current process.
PERFETTO_DCHECK(parent.uuid != Track().uuid);
return Track(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(ptr)),
parent);
}
// Construct a track using |ptr| as identifier within thread-scope.
// Shorthand for `Track::FromPointer(ptr, ThreadTrack::Current())`
// Usage: TRACE_EVENT_BEGIN("...", "...", perfetto::Track::ThreadScoped(this))
static Track ThreadScoped(const void* ptr, Track parent = Track());
protected:
constexpr Track(uint64_t uuid_, uint64_t parent_uuid_)
: uuid(uuid_), parent_uuid(parent_uuid_) {}
static Track MakeThreadTrack(base::PlatformThreadId tid) {
// If tid were 0 here (which is an invalid tid), we would create a thread
// track with a uuid that conflicts with the corresponding ProcessTrack.
PERFETTO_DCHECK(tid != 0);
return Track(static_cast<uint64_t>(tid), MakeProcessTrack());
}
static Track MakeProcessTrack() { return Track(process_uuid, Track()); }
static constexpr inline uint64_t CompileTimeHash(const char* string) {
return internal::Fnv1a(string);
}
private:
friend class internal::TrackRegistry;
friend class Flow;
friend class TerminatingFlow;
static uint64_t process_uuid;
};
// A process track represents events that describe the state of the entire
// application (e.g., counter events). Currently a ProcessTrack can only
// represent the current process.
struct PERFETTO_EXPORT_COMPONENT ProcessTrack : public Track {
const base::PlatformProcessId pid;
static ProcessTrack Current() { return ProcessTrack(); }
void Serialize(protos::pbzero::TrackDescriptor*) const;
protos::gen::TrackDescriptor Serialize() const;
private:
ProcessTrack()
: Track(MakeProcessTrack()), pid(Platform::GetCurrentProcessId()) {}
};
// A thread track is associated with a specific thread of execution. Currently
// only threads in the current process can be referenced.
struct PERFETTO_EXPORT_COMPONENT ThreadTrack : public Track {
const base::PlatformProcessId pid;
const base::PlatformThreadId tid;
bool disallow_merging_with_system_tracks = false;
static ThreadTrack Current();
// Represents a thread in the current process.
static ThreadTrack ForThread(base::PlatformThreadId tid_);
void Serialize(protos::pbzero::TrackDescriptor*) const;
protos::gen::TrackDescriptor Serialize() const;
private:
explicit ThreadTrack(base::PlatformThreadId tid_,
bool disallow_merging_with_system_tracks_)
: Track(MakeThreadTrack(tid_)),
pid(ProcessTrack::Current().pid),
tid(tid_),
disallow_merging_with_system_tracks(
disallow_merging_with_system_tracks_) {}
};
// A track that's identified by an explcit name, id and its parent.
class PERFETTO_EXPORT_COMPONENT NamedTrack : public Track {
// A random value mixed into named track uuids to avoid collisions with
// other types of tracks.
static constexpr uint64_t kNamedTrackMagic = 0xCD571EC5EAD37024ul;
public:
// `name` is hashed to get a uuid identifying the track. Optionally specify
// `id` to differentiate between multiple tracks with the same `name` and
// `parent`.
NamedTrack(DynamicString name,
uint64_t id = 0,
Track parent = MakeProcessTrack())
: Track(id ^ internal::Fnv1a(name.value, name.length) ^ kNamedTrackMagic,
parent),
static_name_(nullptr),
dynamic_name_(name) {}
constexpr NamedTrack(StaticString name,
uint64_t id = 0,
Track parent = MakeProcessTrack())
: Track(id ^ internal::Fnv1a(name.value) ^ kNamedTrackMagic, parent),
static_name_(name) {}
// Construct a track using `name` and `id` as identifier within thread-scope.
// Shorthand for `Track::NamedTrack("name", id, ThreadTrack::Current())`
// Usage: TRACE_EVENT_BEGIN("...", "...",
// perfetto::NamedTrack::ThreadScoped("rendering"))
template <class TrackEventName>
static NamedTrack ThreadScoped(TrackEventName name,
uint64_t id = 0,
Track parent = Track()) {
if (parent.uuid == 0)
return NamedTrack(std::forward<TrackEventName>(name), id,
ThreadTrack::Current());
return NamedTrack(std::forward<TrackEventName>(name), id, parent);
}
void Serialize(protos::pbzero::TrackDescriptor*) const;
protos::gen::TrackDescriptor Serialize() const;
private:
StaticString static_name_;
DynamicString dynamic_name_;
};
// A track for recording counter values with the TRACE_COUNTER macro. Counter
// tracks can optionally be given units and other metadata. See
// /protos/perfetto/trace/track_event/counter_descriptor.proto for details.
class PERFETTO_EXPORT_COMPONENT CounterTrack : public Track {
// A random value mixed into counter track uuids to avoid collisions with
// other types of tracks.
static constexpr uint64_t kCounterMagic = 0xb1a4a67d7970839eul;
public:
using Unit = perfetto::protos::pbzero::CounterDescriptor::Unit;
using CounterType =
perfetto::protos::gen::CounterDescriptor::BuiltinCounterType;
// |name| must outlive this object.
constexpr explicit CounterTrack(StaticString name,
Track parent = MakeProcessTrack())
: CounterTrack(
name,
0u,
perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
nullptr,
parent) {}
constexpr explicit CounterTrack(StaticString name,
uint64_t id,
Track parent = MakeProcessTrack())
: CounterTrack(
name,
id,
perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
nullptr,
parent) {}
explicit CounterTrack(DynamicString name, Track parent = MakeProcessTrack())
: CounterTrack(
name,
0u,
perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
nullptr,
parent) {}
explicit CounterTrack(DynamicString name,
uint64_t id,
Track parent = MakeProcessTrack())
: CounterTrack(
name,
id,
perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
nullptr,
parent) {}
// |unit_name| is a free-form description of the unit used by this counter. It
// must outlive this object.
template <class TrackEventName>
constexpr CounterTrack(TrackEventName&& name,
const char* unit_name,
Track parent = MakeProcessTrack())
: CounterTrack(
std::forward<TrackEventName>(name),
0u,
perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED,
unit_name,
parent) {}
template <class TrackEventName>
constexpr CounterTrack(TrackEventName&& name,
Unit unit,
Track parent = MakeProcessTrack())
: CounterTrack(std::forward<TrackEventName>(name),
0u,
unit,
nullptr,
parent) {}
template <class TrackEventName>
static constexpr CounterTrack Global(TrackEventName&& name,
const char* unit_name) {
return CounterTrack(std::forward<TrackEventName>(name), unit_name, Track());
}
template <class TrackEventName>
static constexpr CounterTrack Global(TrackEventName&& name, Unit unit) {
return CounterTrack(std::forward<TrackEventName>(name), unit, Track());
}
template <class TrackEventName>
static constexpr CounterTrack Global(TrackEventName&& name) {
return Global(std::forward<TrackEventName>(name), nullptr);
}
constexpr CounterTrack set_unit(Unit unit) const {
return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
category_, unit, unit_name_, unit_multiplier_,
is_incremental_, type_);
}
constexpr CounterTrack set_type(CounterType type) const {
return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
category_, unit_, unit_name_, unit_multiplier_,
is_incremental_, type);
}
constexpr CounterTrack set_unit_name(const char* unit_name) const {
return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
category_, unit_, unit_name, unit_multiplier_,
is_incremental_, type_);
}
constexpr CounterTrack set_unit_multiplier(int64_t unit_multiplier) const {
return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
category_, unit_, unit_name_, unit_multiplier,
is_incremental_, type_);
}
constexpr CounterTrack set_category(const char* category) const {
return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
category, unit_, unit_name_, unit_multiplier_,
is_incremental_, type_);
}
constexpr CounterTrack set_is_incremental(bool is_incremental = true) const {
return CounterTrack(uuid, parent_uuid, static_name_, dynamic_name_,
category_, unit_, unit_name_, unit_multiplier_,
is_incremental, type_);
}
constexpr bool is_incremental() const { return is_incremental_; }
void Serialize(protos::pbzero::TrackDescriptor*) const;
protos::gen::TrackDescriptor Serialize() const;
private:
constexpr CounterTrack(StaticString name,
uint64_t id,
Unit unit,
const char* unit_name,
Track parent)
: Track(id ^ internal::Fnv1a(name.value) ^ kCounterMagic, parent),
static_name_(name),
category_(nullptr),
unit_(unit),
unit_name_(unit_name) {}
CounterTrack(DynamicString name,
uint64_t id,
Unit unit,
const char* unit_name,
Track parent)
: Track(id ^ internal::Fnv1a(name.value, name.length) ^ kCounterMagic,
parent),
static_name_(nullptr),
dynamic_name_(name),
category_(nullptr),
unit_(unit),
unit_name_(unit_name) {}
constexpr CounterTrack(uint64_t uuid_,
uint64_t parent_uuid_,
StaticString static_name,
DynamicString dynamic_name,
const char* category,
Unit unit,
const char* unit_name,
int64_t unit_multiplier,
bool is_incremental,
CounterType type)
: Track(uuid_, parent_uuid_),
static_name_(static_name),
dynamic_name_(dynamic_name),
category_(category),
unit_(unit),
unit_name_(unit_name),
unit_multiplier_(unit_multiplier),
is_incremental_(is_incremental),
type_(type) {}
StaticString static_name_;
DynamicString dynamic_name_;
const char* const category_;
Unit unit_ = perfetto::protos::pbzero::CounterDescriptor::UNIT_UNSPECIFIED;
const char* const unit_name_ = nullptr;
int64_t unit_multiplier_ = 1;
const bool is_incremental_ = false;
CounterType type_ =
perfetto::protos::gen::CounterDescriptor::COUNTER_UNSPECIFIED;
};
namespace internal {
// Keeps a map of uuids to serialized track descriptors and provides a
// thread-safe way to read and write them. Each trace writer keeps a TLS set of
// the tracks it has seen (see TrackEventIncrementalState). In the common case,
// this registry is not consulted (and no locks are taken). However when a new
// track is seen, this registry is used to write either 1) the default
// descriptor for that track (see *Track::Serialize) or 2) a serialized
// descriptor stored in the registry which may have additional metadata (e.g.,
// track name).
// TODO(eseckler): Remove PERFETTO_EXPORT_COMPONENT once Chromium no longer
// calls TrackRegistry::InitializeInstance() directly.
class PERFETTO_EXPORT_COMPONENT TrackRegistry {
public:
using SerializedTrackDescriptor = std::string;
struct TrackInfo {
SerializedTrackDescriptor desc;
uint64_t parent_uuid = 0;
};
TrackRegistry();
~TrackRegistry();
static void InitializeInstance();
static void ResetForTesting();
static uint64_t ComputeProcessUuid();
static TrackRegistry* Get() { return instance_; }
void EraseTrack(Track);
// This variant lets the user supply a serialized track descriptor directly.
void UpdateTrack(Track, const std::string& serialized_desc);
// If |track| exists in the registry, write out the serialized track
// descriptor for it into |packet|. Otherwise just the ephemeral track object
// is serialized without any additional metadata.
//
// Returns the parent track uuid.
template <typename TrackType>
uint64_t SerializeTrack(
const TrackType& track,
protozero::MessageHandle<protos::pbzero::TracePacket> packet) {
// If the track has extra metadata (recorded with UpdateTrack), it will be
// found in the registry. To minimize the time the lock is held, make a copy
// of the data held in the registry and write it outside the lock.
auto track_info = FindTrackInfo(track.uuid);
if (track_info) {
WriteTrackDescriptor(std::move(track_info->desc), std::move(packet));
return track_info->parent_uuid;
} else {
// Otherwise we just write the basic descriptor for this type of track
// (e.g., just uuid, no name).
track.Serialize(packet->set_track_descriptor());
return track.parent_uuid;
}
}
// If saved in the registry, returns the serialize track descriptor and parent
// uuid for `uuid`.
std::optional<TrackInfo> FindTrackInfo(uint64_t uuid) {
std::optional<TrackInfo> track_info;
{
std::lock_guard<std::mutex> lock(mutex_);
const auto it = tracks_.find(uuid);
if (it != tracks_.end()) {
track_info = it->second;
PERFETTO_DCHECK(!track_info->desc.empty());
}
}
return track_info;
}
static void WriteTrackDescriptor(
const SerializedTrackDescriptor& desc,
protozero::MessageHandle<protos::pbzero::TracePacket> packet);
private:
std::mutex mutex_;
std::map<uint64_t /* uuid */, TrackInfo> tracks_;
static TrackRegistry* instance_;
};
} // namespace internal
} // namespace perfetto
#endif // INCLUDE_PERFETTO_TRACING_TRACK_H_