blob: 4f0b1795782494596f3bd082e0cc40d8a24230bd [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/compile_time_hash.h"
#include "perfetto/tracing/internal/tracing_muxer.h"
#include "perfetto/tracing/platform.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
#include "protos/perfetto/trace/track_event/counter_descriptor.gen.h"
#include "protos/perfetto/trace/track_event/counter_descriptor.pbzero.h"
#include "protos/perfetto/trace/track_event/track_descriptor.gen.h"
#include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
#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 = MakeThreadTrack(base::GetThreadId())) {
return Track::FromPointer(ptr, parent);
}
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::CompileTimeHash()
.Update(string, static_cast<size_t>(base::StrEnd(string) - string))
.digest();
}
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 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 be a string with static lifetime.
constexpr explicit CounterTrack(const char* name,
Track parent = MakeProcessTrack())
: Track(CompileTimeHash(name) ^ kCounterMagic, parent),
name_(name),
category_(nullptr) {}
// |unit_name| is a free-form description of the unit used by this counter. It
// must have static lifetime.
constexpr CounterTrack(const char* name,
const char* unit_name,
Track parent = MakeProcessTrack())
: Track(CompileTimeHash(name) ^ kCounterMagic, parent),
name_(name),
category_(nullptr),
unit_name_(unit_name) {}
constexpr CounterTrack(const char* name,
Unit unit,
Track parent = MakeProcessTrack())
: Track(CompileTimeHash(name) ^ kCounterMagic, parent),
name_(name),
category_(nullptr),
unit_(unit) {}
static constexpr CounterTrack Global(const char* name,
const char* unit_name) {
return CounterTrack(name, unit_name, Track());
}
static constexpr CounterTrack Global(const char* name, Unit unit) {
return CounterTrack(name, unit, Track());
}
static constexpr CounterTrack Global(const char* name) {
return Global(name, nullptr);
}
constexpr CounterTrack set_unit(Unit unit) const {
return CounterTrack(uuid, parent_uuid, name_, category_, unit, unit_name_,
unit_multiplier_, is_incremental_, type_);
}
constexpr CounterTrack set_type(CounterType type) const {
return CounterTrack(uuid, parent_uuid, 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, 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, name_, category_, unit_, unit_name_,
unit_multiplier, is_incremental_, type_);
}
constexpr CounterTrack set_category(const char* category) const {
return CounterTrack(uuid, parent_uuid, 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, 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(uint64_t uuid_,
uint64_t parent_uuid_,
const char* name,
const char* category,
Unit unit,
const char* unit_name,
int64_t unit_multiplier,
bool is_incremental,
CounterType type)
: Track(uuid_, parent_uuid_),
name_(name),
category_(category),
unit_(unit),
unit_name_(unit_name),
unit_multiplier_(unit_multiplier),
is_incremental_(is_incremental),
type_(type) {}
const char* const 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;
TrackRegistry();
~TrackRegistry();
static void InitializeInstance();
static void ResetForTesting();
static TrackRegistry* Get() { return instance_; }
void EraseTrack(Track);
// Store metadata for |track| in the registry. |fill_function| is called
// synchronously to record additional properties for the track.
template <typename TrackType>
void UpdateTrack(
const TrackType& track,
std::function<void(protos::pbzero::TrackDescriptor*)> fill_function) {
UpdateTrackImpl(track, [&](protos::pbzero::TrackDescriptor* desc) {
track.Serialize(desc);
fill_function(desc);
});
}
// 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.
template <typename TrackType>
void 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.
std::string desc_copy;
{
std::lock_guard<std::mutex> lock(mutex_);
const auto& it = tracks_.find(track.uuid);
if (it != tracks_.end()) {
desc_copy = it->second;
PERFETTO_DCHECK(!desc_copy.empty());
}
}
if (!desc_copy.empty()) {
WriteTrackDescriptor(std::move(desc_copy), std::move(packet));
} 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());
}
}
static void WriteTrackDescriptor(
const SerializedTrackDescriptor& desc,
protozero::MessageHandle<protos::pbzero::TracePacket> packet);
private:
void UpdateTrackImpl(
Track,
std::function<void(protos::pbzero::TrackDescriptor*)> fill_function);
std::mutex mutex_;
std::map<uint64_t /* uuid */, SerializedTrackDescriptor> tracks_;
static TrackRegistry* instance_;
};
} // namespace internal
} // namespace perfetto
#endif // INCLUDE_PERFETTO_TRACING_TRACK_H_