| /* |
| * 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 uint64_t ComputeProcessUuid(); |
| 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_ |