| /* |
| * 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_ |