TrackEvent: Add support for tracks
Track events are recorded on a timeline track, which can be attached to a
process (ProcessTrack) or a thread (ThreadTrack). Custom timelines can be
created using asynchronous tracks (see AsyncTrack), which can also optionally
be scoped to a thread or a process.
A track is represented by a uuid, which must be unique across the entire
recorded trace.
Async event example:
TRACE_EVENT_BEGIN("category", "AsyncEvent", perfetto::AsyncTrack(8086));
...
TRACE_EVENT_END("category", perfetto::AsyncTrack(8086));
Tracks can also be annotated with metadata:
perfetto::TrackEvent::SetAsyncTrackDescriptor(
track, [](perfetto::protos::gen::TrackDescriptor* desc) {
desc->set_name("MyAsyncTrack");
});
Bug: 132678367
Change-Id: I83c5300fe48ae2e9612942b2f73237fd67f948d3
diff --git a/Android.bp b/Android.bp
index 35cec9e..76fb7b1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6229,6 +6229,7 @@
"src/tracing/internal/track_event_internal.cc",
"src/tracing/platform.cc",
"src/tracing/tracing.cc",
+ "src/tracing/track.cc",
"src/tracing/track_event_category_registry.cc",
"src/tracing/virtual_destructors.cc",
],
diff --git a/BUILD b/BUILD
index 4ee218d..ec5393f 100644
--- a/BUILD
+++ b/BUILD
@@ -245,8 +245,11 @@
"include/perfetto/base/build_config.h",
"include/perfetto/base/compiler.h",
"include/perfetto/base/export.h",
+ "include/perfetto/base/flat_set.h",
"include/perfetto/base/logging.h",
+ "include/perfetto/base/proc_utils.h",
"include/perfetto/base/task_runner.h",
+ "include/perfetto/base/thread_utils.h",
"include/perfetto/base/time.h",
],
)
@@ -259,7 +262,6 @@
"include/perfetto/ext/base/container_annotations.h",
"include/perfetto/ext/base/event_fd.h",
"include/perfetto/ext/base/file_utils.h",
- "include/perfetto/ext/base/flat_set.h",
"include/perfetto/ext/base/hash.h",
"include/perfetto/ext/base/lookup_set.h",
"include/perfetto/ext/base/metatrace.h",
@@ -268,7 +270,6 @@
"include/perfetto/ext/base/optional.h",
"include/perfetto/ext/base/paged_memory.h",
"include/perfetto/ext/base/pipe.h",
- "include/perfetto/ext/base/proc_utils.h",
"include/perfetto/ext/base/scoped_file.h",
"include/perfetto/ext/base/small_set.h",
"include/perfetto/ext/base/string_splitter.h",
@@ -279,7 +280,6 @@
"include/perfetto/ext/base/thread_annotations.h",
"include/perfetto/ext/base/thread_checker.h",
"include/perfetto/ext/base/thread_task_runner.h",
- "include/perfetto/ext/base/thread_utils.h",
"include/perfetto/ext/base/unix_socket.h",
"include/perfetto/ext/base/unix_task_runner.h",
"include/perfetto/ext/base/utils.h",
@@ -477,6 +477,7 @@
"include/perfetto/tracing/trace_writer_base.h",
"include/perfetto/tracing/tracing.h",
"include/perfetto/tracing/tracing_backend.h",
+ "include/perfetto/tracing/track.h",
"include/perfetto/tracing/track_event.h",
"include/perfetto/tracing/track_event_category_registry.h",
"include/perfetto/tracing/track_event_interned_data_index.h",
@@ -1126,6 +1127,7 @@
"src/tracing/internal/track_event_internal.cc",
"src/tracing/platform.cc",
"src/tracing/tracing.cc",
+ "src/tracing/track.cc",
"src/tracing/track_event_category_registry.cc",
"src/tracing/virtual_destructors.cc",
],
diff --git a/include/perfetto/base/BUILD.gn b/include/perfetto/base/BUILD.gn
index e490551..85819b7 100644
--- a/include/perfetto/base/BUILD.gn
+++ b/include/perfetto/base/BUILD.gn
@@ -19,8 +19,11 @@
"build_config.h",
"compiler.h",
"export.h",
+ "flat_set.h",
"logging.h",
+ "proc_utils.h",
"task_runner.h",
+ "thread_utils.h",
"time.h",
]
}
diff --git a/include/perfetto/ext/base/flat_set.h b/include/perfetto/base/flat_set.h
similarity index 91%
rename from include/perfetto/ext/base/flat_set.h
rename to include/perfetto/base/flat_set.h
index e33fcf7..068ad3c 100644
--- a/include/perfetto/ext/base/flat_set.h
+++ b/include/perfetto/base/flat_set.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef INCLUDE_PERFETTO_EXT_BASE_FLAT_SET_H_
-#define INCLUDE_PERFETTO_EXT_BASE_FLAT_SET_H_
+#ifndef INCLUDE_PERFETTO_BASE_FLAT_SET_H_
+#define INCLUDE_PERFETTO_BASE_FLAT_SET_H_
#include <algorithm>
#include <vector>
@@ -63,14 +63,15 @@
size_t count(T value) const { return find(value) == entries_.end() ? 0 : 1; }
- void insert(T value) {
+ std::pair<iterator, bool> insert(T value) {
auto entries_end = entries_.end();
auto it = std::lower_bound(entries_.begin(), entries_end, value);
if (it != entries_end && *it == value)
- return;
+ return std::make_pair(it, false);
// If the value is not found |it| is either end() or the next item strictly
// greater than |value|. In both cases we want to insert just before that.
- entries_.insert(it, std::move(value));
+ it = entries_.insert(it, std::move(value));
+ return std::make_pair(it, true);
}
size_t erase(T value) {
@@ -96,4 +97,4 @@
} // namespace base
} // namespace perfetto
-#endif // INCLUDE_PERFETTO_EXT_BASE_FLAT_SET_H_
+#endif // INCLUDE_PERFETTO_BASE_FLAT_SET_H_
diff --git a/include/perfetto/ext/base/proc_utils.h b/include/perfetto/base/proc_utils.h
similarity index 90%
rename from include/perfetto/ext/base/proc_utils.h
rename to include/perfetto/base/proc_utils.h
index d5bacb9..8818ec0 100644
--- a/include/perfetto/ext/base/proc_utils.h
+++ b/include/perfetto/base/proc_utils.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef INCLUDE_PERFETTO_EXT_BASE_PROC_UTILS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_PROC_UTILS_H_
+#ifndef INCLUDE_PERFETTO_BASE_PROC_UTILS_H_
+#define INCLUDE_PERFETTO_BASE_PROC_UTILS_H_
#include <stdint.h>
@@ -54,4 +54,4 @@
} // namespace base
} // namespace perfetto
-#endif // INCLUDE_PERFETTO_EXT_BASE_PROC_UTILS_H_
+#endif // INCLUDE_PERFETTO_BASE_PROC_UTILS_H_
diff --git a/include/perfetto/ext/base/thread_utils.h b/include/perfetto/base/thread_utils.h
similarity index 93%
rename from include/perfetto/ext/base/thread_utils.h
rename to include/perfetto/base/thread_utils.h
index 2e6495c..fa93c27 100644
--- a/include/perfetto/ext/base/thread_utils.h
+++ b/include/perfetto/base/thread_utils.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
-#define INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+#ifndef INCLUDE_PERFETTO_BASE_THREAD_UTILS_H_
+#define INCLUDE_PERFETTO_BASE_THREAD_UTILS_H_
#include <stdint.h>
@@ -79,4 +79,4 @@
} // namespace base
} // namespace perfetto
-#endif // INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
+#endif // INCLUDE_PERFETTO_BASE_THREAD_UTILS_H_
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index 7d76c84..fd369cf 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -20,7 +20,6 @@
"container_annotations.h",
"event_fd.h",
"file_utils.h",
- "flat_set.h",
"hash.h",
"lookup_set.h",
"metatrace.h",
@@ -29,7 +28,6 @@
"optional.h",
"paged_memory.h",
"pipe.h",
- "proc_utils.h",
"scoped_file.h",
"small_set.h",
"string_splitter.h",
@@ -40,7 +38,6 @@
"thread_annotations.h",
"thread_checker.h",
"thread_task_runner.h",
- "thread_utils.h",
"unix_task_runner.h",
"utils.h",
"uuid.h",
diff --git a/include/perfetto/ext/base/metatrace.h b/include/perfetto/ext/base/metatrace.h
index 2c587c3..f5eb057 100644
--- a/include/perfetto/ext/base/metatrace.h
+++ b/include/perfetto/ext/base/metatrace.h
@@ -23,10 +23,10 @@
#include <string>
#include "perfetto/base/logging.h"
+#include "perfetto/base/thread_utils.h"
#include "perfetto/base/time.h"
#include "perfetto/ext/base/metatrace_events.h"
#include "perfetto/ext/base/thread_annotations.h"
-#include "perfetto/ext/base/thread_utils.h"
#include "perfetto/ext/base/utils.h"
// A facility to trace execution of the perfetto codebase itself.
diff --git a/include/perfetto/ext/base/unix_task_runner.h b/include/perfetto/ext/base/unix_task_runner.h
index 9ced3b9..9a743e3 100644
--- a/include/perfetto/ext/base/unix_task_runner.h
+++ b/include/perfetto/ext/base/unix_task_runner.h
@@ -19,11 +19,11 @@
#include "perfetto/base/build_config.h"
#include "perfetto/base/task_runner.h"
+#include "perfetto/base/thread_utils.h"
#include "perfetto/base/time.h"
#include "perfetto/ext/base/event_fd.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/thread_checker.h"
-#include "perfetto/ext/base/thread_utils.h"
#include <poll.h>
#include <chrono>
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index 01294bd..60f4d4e 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -41,6 +41,7 @@
"trace_writer_base.h",
"tracing.h",
"tracing_backend.h",
+ "track.h",
"track_event.h",
"track_event_category_registry.h",
"track_event_interned_data_index.h",
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 1debee3..e6c8af0 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -22,6 +22,7 @@
#include "perfetto/tracing/data_source.h"
#include "perfetto/tracing/event_context.h"
#include "perfetto/tracing/internal/track_event_internal.h"
+#include "perfetto/tracing/track.h"
#include "perfetto/tracing/track_event_category_registry.h"
#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
@@ -30,6 +31,34 @@
namespace perfetto {
namespace internal {
+namespace {
+
+// A template helper for determining whether a type can be used as a track event
+// lambda, i.e., it has the signature "void(EventContext)". This is achieved by
+// checking that we can pass an EventContext value (the inner declval) into a T
+// instance (the outer declval). If this is a valid expression, the result
+// evaluates to sizeof(0), i.e., true.
+// TODO(skyostil): Replace this with std::is_convertible<std::function<...>>
+// once we have C++14.
+template <typename T>
+static constexpr bool IsValidTraceLambdaImpl(
+ typename std::enable_if<static_cast<bool>(
+ sizeof(std::declval<T>()(std::declval<EventContext>()), 0))>::type* =
+ nullptr) {
+ return true;
+}
+
+template <typename T>
+static constexpr bool IsValidTraceLambdaImpl(...) {
+ return false;
+}
+
+template <typename T>
+static constexpr bool IsValidTraceLambda() {
+ return IsValidTraceLambdaImpl<T>(nullptr);
+}
+
+} // namespace
struct TrackEventDataSourceTraits : public perfetto::DefaultDataSourceTraits {
using IncrementalStateType = TrackEventIncrementalState;
@@ -87,8 +116,8 @@
}
// Once we've determined tracing to be enabled for this category, actually
- // write a trace event. Outlined to avoid bloating code at the actual trace
- // point.
+ // write a trace event onto this thread's default track. Outlined to avoid
+ // bloating code at the actual trace point.
// TODO(skyostil): Investigate whether this should be fully outlined to reduce
// binary size.
template <size_t CategoryIndex,
@@ -97,13 +126,51 @@
uint32_t instances,
const char* event_name,
perfetto::protos::pbzero::TrackEvent::Type type,
- ArgumentFunction arg_function = [](EventContext) {}) PERFETTO_NO_INLINE {
+ ArgumentFunction arg_function = [](EventContext) {},
+ typename std::enable_if<IsValidTraceLambda<ArgumentFunction>()>::type* =
+ nullptr) PERFETTO_NO_INLINE {
+ // We don't simply call TraceForCategory(..., Track(), ...) here, since that
+ // would add extra binary bloat to all trace points that target the default
+ // track.
Base::template TraceWithInstances<CategoryTracePointTraits<CategoryIndex>>(
instances, [&](typename Base::TraceContext ctx) {
// TODO(skyostil): Intern categories at compile time.
arg_function(TrackEventInternal::WriteEvent(
ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
Registry->GetCategory(CategoryIndex)->name, event_name, type));
+ // There's no need to emit a track descriptor for the default track
+ // here since that's done in ResetIncrementalState().
+ });
+ }
+
+ // This variant of the inner trace point takes a Track argument which can be
+ // used to emit events on a non-default track.
+ template <size_t CategoryIndex,
+ typename TrackType,
+ typename ArgumentFunction = void (*)(EventContext)>
+ static void TraceForCategory(
+ uint32_t instances,
+ const char* event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ const TrackType& track,
+ ArgumentFunction arg_function = [](EventContext) {},
+ typename std::enable_if<IsValidTraceLambda<ArgumentFunction>()>::type* =
+ nullptr,
+ typename std::enable_if<
+ std::is_convertible<TrackType, Track>::value>::type* = nullptr)
+ PERFETTO_NO_INLINE {
+ PERFETTO_DCHECK(track);
+ Base::template TraceWithInstances<CategoryTracePointTraits<CategoryIndex>>(
+ instances, [&](typename Base::TraceContext ctx) {
+ // TODO(skyostil): Intern categories at compile time.
+ auto event_ctx = TrackEventInternal::WriteEvent(
+ ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
+ Registry->GetCategory(CategoryIndex)->name, event_name, type);
+ event_ctx.event()->set_track_uuid(track.uuid);
+ arg_function(std::move(event_ctx));
+ TrackEventInternal::WriteTrackDescriptorIfNeeded(
+ track, ctx.tls_inst_->trace_writer.get(),
+ ctx.GetIncrementalState());
});
}
@@ -124,27 +191,51 @@
perfetto::protos::pbzero::TrackEvent::Type type,
const char* arg_name,
ArgType&& arg_value) PERFETTO_ALWAYS_INLINE {
- TraceForCategoryWithDebugAnnotations<CategoryIndex, ArgType>(
- instances, event_name, type, arg_name,
+ TraceForCategoryWithDebugAnnotations<CategoryIndex, Track, ArgType>(
+ instances, event_name, type, Track(), arg_name,
std::forward<ArgType>(arg_value));
}
- template <size_t CategoryIndex, typename ArgType>
+ // A one argument trace point which takes an explicit track.
+ template <size_t CategoryIndex, typename TrackType, typename ArgType>
+ static void TraceForCategory(uint32_t instances,
+ const char* event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ const TrackType& track,
+ const char* arg_name,
+ ArgType&& arg_value) PERFETTO_ALWAYS_INLINE {
+ PERFETTO_DCHECK(track);
+ TraceForCategoryWithDebugAnnotations<CategoryIndex, TrackType, ArgType>(
+ instances, event_name, type, track, arg_name,
+ std::forward<ArgType>(arg_value));
+ }
+
+ template <size_t CategoryIndex, typename TrackType, typename ArgType>
static void TraceForCategoryWithDebugAnnotations(
uint32_t instances,
const char* event_name,
perfetto::protos::pbzero::TrackEvent::Type type,
+ const TrackType& track,
const char* arg_name,
typename internal::DebugAnnotationArg<ArgType>::type arg_value)
PERFETTO_NO_INLINE {
Base::template TraceWithInstances<CategoryTracePointTraits<CategoryIndex>>(
instances, [&](typename Base::TraceContext ctx) {
- // TODO(skyostil): Intern categories at compile time.
- auto event_ctx = TrackEventInternal::WriteEvent(
- ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
- Registry->GetCategory(CategoryIndex)->name, event_name, type);
- TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
- arg_value);
+ {
+ // TODO(skyostil): Intern categories at compile time.
+ auto event_ctx = TrackEventInternal::WriteEvent(
+ ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
+ Registry->GetCategory(CategoryIndex)->name, event_name, type);
+ if (track)
+ event_ctx.event()->set_track_uuid(track.uuid);
+ TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
+ arg_value);
+ }
+ if (track) {
+ TrackEventInternal::WriteTrackDescriptorIfNeeded(
+ track, ctx.tls_inst_->trace_writer.get(),
+ ctx.GetIncrementalState());
+ }
});
}
@@ -160,16 +251,43 @@
ArgType&& arg_value,
const char* arg_name2,
ArgType2&& arg_value2) PERFETTO_ALWAYS_INLINE {
- TraceForCategoryWithDebugAnnotations<CategoryIndex, ArgType, ArgType2>(
- instances, event_name, type, arg_name, std::forward<ArgType>(arg_value),
- arg_name2, std::forward<ArgType2>(arg_value2));
+ TraceForCategoryWithDebugAnnotations<CategoryIndex, Track, ArgType,
+ ArgType2>(
+ instances, event_name, type, Track(), arg_name,
+ std::forward<ArgType>(arg_value), arg_name2,
+ std::forward<ArgType2>(arg_value2));
}
- template <size_t CategoryIndex, typename ArgType, typename ArgType2>
+ // A two argument trace point which takes an explicit track.
+ template <size_t CategoryIndex,
+ typename TrackType,
+ typename ArgType,
+ typename ArgType2>
+ static void TraceForCategory(uint32_t instances,
+ const char* event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ const TrackType& track,
+ const char* arg_name,
+ ArgType&& arg_value,
+ const char* arg_name2,
+ ArgType2&& arg_value2) PERFETTO_ALWAYS_INLINE {
+ PERFETTO_DCHECK(track);
+ TraceForCategoryWithDebugAnnotations<CategoryIndex, TrackType, ArgType,
+ ArgType2>(
+ instances, event_name, type, track, arg_name,
+ std::forward<ArgType>(arg_value), arg_name2,
+ std::forward<ArgType2>(arg_value2));
+ }
+
+ template <size_t CategoryIndex,
+ typename TrackType,
+ typename ArgType,
+ typename ArgType2>
static void TraceForCategoryWithDebugAnnotations(
uint32_t instances,
const char* event_name,
perfetto::protos::pbzero::TrackEvent::Type type,
+ TrackType track,
const char* arg_name,
typename internal::DebugAnnotationArg<ArgType>::type arg_value,
const char* arg_name2,
@@ -178,16 +296,27 @@
Base::template TraceWithInstances<CategoryTracePointTraits<CategoryIndex>>(
instances, [&](typename Base::TraceContext ctx) {
// TODO(skyostil): Intern categories at compile time.
- auto event_ctx = TrackEventInternal::WriteEvent(
- ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
- Registry->GetCategory(CategoryIndex)->name, event_name, type);
- TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
- arg_value);
- TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name2,
- arg_value2);
+ {
+ auto event_ctx = TrackEventInternal::WriteEvent(
+ ctx.tls_inst_->trace_writer.get(), ctx.GetIncrementalState(),
+ Registry->GetCategory(CategoryIndex)->name, event_name, type);
+ if (track)
+ event_ctx.event()->set_track_uuid(track.uuid);
+ TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name,
+ arg_value);
+ TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name2,
+ arg_value2);
+ }
+ if (track) {
+ TrackEventInternal::WriteTrackDescriptorIfNeeded(
+ track, ctx.tls_inst_->trace_writer.get(),
+ ctx.GetIncrementalState());
+ }
});
}
+ // Initialize the track event library. Should be called before tracing is
+ // enabled.
static bool Register() {
// Registration is performed out-of-line so users don't need to depend on
// DataSourceDescriptor C++ bindings.
@@ -195,6 +324,29 @@
[](const DataSourceDescriptor& dsd) { return Base::Register(dsd); });
}
+ // Record metadata about different types of timeline tracks. See Track.
+ static void SetTrackDescriptor(
+ const Track& track,
+ std::function<void(protos::pbzero::TrackDescriptor*)> callback) {
+ SetTrackDescriptorImpl(track, std::move(callback));
+ }
+
+ static void SetProcessDescriptor(
+ std::function<void(protos::pbzero::TrackDescriptor*)> callback,
+ const ProcessTrack& track = ProcessTrack::Current()) {
+ SetTrackDescriptorImpl(std::move(track), std::move(callback));
+ }
+
+ static void SetThreadDescriptor(
+ std::function<void(protos::pbzero::TrackDescriptor*)> callback,
+ const ThreadTrack& track = ThreadTrack::Current()) {
+ SetTrackDescriptorImpl(std::move(track), std::move(callback));
+ }
+
+ static void EraseTrackDescriptor(const Track& track) {
+ TrackRegistry::Get()->EraseTrack(track);
+ }
+
private:
// Each category has its own enabled/disabled state, stored in the category
// registry.
@@ -204,6 +356,20 @@
return Registry->GetCategoryState(CategoryIndex);
}
};
+
+ // Records a track descriptor into the track descriptor registry and, if we
+ // are tracing, also mirrors the descriptor into the trace.
+ template <typename TrackType>
+ static void SetTrackDescriptorImpl(
+ const TrackType& track,
+ std::function<void(protos::pbzero::TrackDescriptor*)> callback) {
+ TrackRegistry::Get()->UpdateTrack(
+ track, [&](protos::pbzero::TrackDescriptor* desc) { callback(desc); });
+ Base::template Trace([&](typename Base::TraceContext ctx) {
+ TrackEventInternal::WriteTrackDescriptor(
+ track, ctx.tls_inst_->trace_writer.get());
+ });
+ }
};
} // namespace internal
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index e37a30b..a4ee050 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -17,15 +17,17 @@
#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
#define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNAL_H_
-#include <unordered_map>
-
+#include "perfetto/base/flat_set.h"
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "perfetto/tracing/core/forward_decls.h"
#include "perfetto/tracing/debug_annotation.h"
#include "perfetto/tracing/trace_writer_base.h"
+#include "perfetto/tracing/track.h"
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
+#include <set>
+
namespace perfetto {
class EventContext;
namespace protos {
@@ -70,6 +72,11 @@
std::unique_ptr<BaseTrackEventInternedDataIndex>>;
std::array<InternedDataIndex, kMaxInternedDataFields> interned_data_indices =
{};
+
+ // Track uuids for which we have written descriptors into the trace. If a
+ // trace event uses a track which is not in this set, we'll write out a
+ // descriptor for it.
+ base::FlatSet<uint64_t> seen_tracks;
};
// The backend portion of the track event trace point implemention. Outlined to
@@ -101,7 +108,36 @@
WriteDebugAnnotation(annotation, value);
}
+ // If the given track hasn't been seen by the trace writer yet, write a
+ // descriptor for it into the trace. Doesn't take a lock unless the track
+ // descriptor is new.
+ template <typename TrackType>
+ static void WriteTrackDescriptorIfNeeded(
+ const TrackType& track,
+ TraceWriterBase* trace_writer,
+ TrackEventIncrementalState* incr_state) {
+ auto it_and_inserted = incr_state->seen_tracks.insert(track.uuid);
+ if (PERFETTO_LIKELY(!it_and_inserted.second))
+ return;
+ WriteTrackDescriptor(track, trace_writer);
+ }
+
+ // Unconditionally write a track descriptor into the trace.
+ template <typename TrackType>
+ static void WriteTrackDescriptor(const TrackType& track,
+ TraceWriterBase* trace_writer) {
+ TrackRegistry::Get()->SerializeTrack(
+ track, NewTracePacket(trace_writer, GetTimeNs()));
+ }
+
private:
+ static uint64_t GetTimeNs();
+ static void ResetIncrementalState(TraceWriterBase*, uint64_t timestamp);
+ static protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket(
+ TraceWriterBase*,
+ uint64_t timestamp,
+ uint32_t seq_flags =
+ protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
perfetto::EventContext*,
const char* name);
diff --git a/include/perfetto/tracing/track.h b/include/perfetto/tracing/track.h
new file mode 100644
index 0000000..a93b0f9
--- /dev/null
+++ b/include/perfetto/tracing/track.h
@@ -0,0 +1,214 @@
+/*
+ * 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/proc_utils.h"
+#include "perfetto/base/thread_utils.h"
+#include "perfetto/protozero/message_handle.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
+
+#include <stdint.h>
+#include <map>
+#include <mutex>
+
+namespace perfetto {
+namespace internal {
+class TrackRegistry;
+}
+
+// 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:
+//
+// perfetto::TrackEvent::SetTrackDescriptor(
+// track, [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+// desc->set_name("MyTrack");
+// });
+//
+// The metadata remains valid between tracing sessions. To free up data for a
+// track, call EraseTrackDescriptor:
+//
+// perfetto::TrackEvent::EraseTrackDescriptor(track);
+//
+struct 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 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;
+
+ protected:
+ static Track MakeThreadTrack(base::PlatformThreadID tid_) {
+ return Track(static_cast<uint64_t>(tid_), MakeProcessTrack());
+ }
+
+ static Track MakeProcessTrack() { return Track(process_uuid, Track()); }
+
+ private:
+ friend class internal::TrackRegistry;
+ 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 ProcessTrack : public Track {
+ const base::PlatformProcessId pid;
+
+ static ProcessTrack Current() { return ProcessTrack(); }
+
+ void Serialize(protos::pbzero::TrackDescriptor*) const;
+
+ private:
+ ProcessTrack() : Track(MakeProcessTrack()), pid(base::GetProcessId()) {}
+};
+
+// A thread track is associated with a specific thread of execution. Currently
+// only threads in the current process can be referenced.
+struct ThreadTrack : public Track {
+ const base::PlatformProcessId pid;
+ const base::PlatformThreadID tid;
+
+ static ThreadTrack Current() { return ThreadTrack(base::GetThreadId()); }
+
+ // Represents a thread in the current process.
+ static ThreadTrack ForThread(base::PlatformThreadID tid_) {
+ return ThreadTrack(tid_);
+ }
+
+ void Serialize(protos::pbzero::TrackDescriptor*) const;
+
+ private:
+ explicit ThreadTrack(base::PlatformThreadID tid_)
+ : Track(MakeThreadTrack(tid_)),
+ pid(ProcessTrack::Current().pid),
+ tid(tid_) {}
+};
+
+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).
+class TrackRegistry {
+ public:
+ using SerializedTrackDescriptor = std::string;
+
+ TrackRegistry();
+ ~TrackRegistry();
+
+ static void InitializeInstance();
+ 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);
+ });
+ }
+
+ // 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_
diff --git a/include/perfetto/tracing/track_event.h b/include/perfetto/tracing/track_event.h
index fa4776b..95c2923 100644
--- a/include/perfetto/tracing/track_event.h
+++ b/include/perfetto/tracing/track_event.h
@@ -21,6 +21,7 @@
#include "perfetto/tracing/internal/track_event_data_source.h"
#include "perfetto/tracing/internal/track_event_internal.h"
#include "perfetto/tracing/internal/track_event_macros.h"
+#include "perfetto/tracing/track.h"
#include "perfetto/tracing/track_event_category_registry.h"
#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
@@ -68,6 +69,14 @@
// });
// }
//
+// Note that track events must be nested consistently, i.e., the following is
+// not allowed:
+//
+// TRACE_EVENT_BEGIN("a", "bar", ...);
+// TRACE_EVENT_BEGIN("b", "foo", ...);
+// TRACE_EVENT_END("a"); // "foo" must be closed before "bar".
+// TRACE_EVENT_END("b");
+//
// ====================
// Implementation notes
// ====================
@@ -138,14 +147,17 @@
PERFETTO_INTERNAL_CATEGORY_STORAGE() \
} // namespace PERFETTO_TRACK_EVENT_NAMESPACE
-// Begin a thread-scoped slice under |category| with the title |name|. Both
-// strings must be static constants. The track event is only recorded if
-// |category| is enabled for a tracing session.
+// Begin a slice under |category| with the title |name|. Both strings must be
+// static constants. The track event is only recorded if |category| is enabled
+// for a tracing session.
//
// |name| must be a string with static lifetime (i.e., the same
// address must not be used for a different event name in the future). If you
// want to use a dynamically allocated name, do this:
//
+// The slice is thread-scoped (i.e., written to the default track of the current
+// thread) unless overridden with a custom track object (see Track).
+//
// TRACE_EVENT("category", nullptr, [&](perfetto::EventContext ctx) {
// ctx.event()->set_name(dynamic_name);
// });
@@ -155,18 +167,17 @@
category, name, \
::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN, ##__VA_ARGS__)
-// End a thread-scoped slice under |category|.
+// End a slice under |category|.
#define TRACE_EVENT_END(category, ...) \
PERFETTO_INTERNAL_TRACK_EVENT( \
category, nullptr, \
::perfetto::protos::pbzero::TrackEvent::TYPE_SLICE_END, ##__VA_ARGS__)
-// Begin a thread-scoped slice which gets automatically closed when going out of
-// scope.
+// Begin a slice which gets automatically closed when going out of scope.
#define TRACE_EVENT(category, name, ...) \
PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(category, name, ##__VA_ARGS__)
-// Emit a thread-scoped slice which has zero duration.
+// Emit a slice which has zero duration.
// TODO(skyostil): Add support for process-wide and global instant events.
#define TRACE_EVENT_INSTANT(category, name, ...) \
PERFETTO_INTERNAL_TRACK_EVENT( \
diff --git a/src/base/flat_set_benchmark.cc b/src/base/flat_set_benchmark.cc
index 12e5565..0773fbe 100644
--- a/src/base/flat_set_benchmark.cc
+++ b/src/base/flat_set_benchmark.cc
@@ -18,7 +18,7 @@
#include <benchmark/benchmark.h>
-#include "perfetto/ext/base/flat_set.h"
+#include "perfetto/base/flat_set.h"
namespace {
diff --git a/src/base/flat_set_unittest.cc b/src/base/flat_set_unittest.cc
index c853e13..406e05c 100644
--- a/src/base/flat_set_unittest.cc
+++ b/src/base/flat_set_unittest.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "perfetto/ext/base/flat_set.h"
+#include "perfetto/base/flat_set.h"
#include <random>
#include <set>
@@ -38,7 +38,9 @@
EXPECT_EQ(flat_set.find(42), flat_set.end());
EXPECT_EQ(flat_set.find(42), flat_set.begin());
- flat_set.insert(1);
+ auto it_and_inserted = flat_set.insert(1);
+ EXPECT_EQ(it_and_inserted.first, flat_set.find(1));
+ EXPECT_TRUE(it_and_inserted.second);
EXPECT_FALSE(flat_set.empty());
EXPECT_EQ(flat_set.size(), 1u);
{
@@ -52,7 +54,9 @@
EXPECT_NE(flat_set.begin(), flat_set.end());
EXPECT_EQ(std::distance(flat_set.begin(), flat_set.end()), 1);
- flat_set.insert(1);
+ it_and_inserted = flat_set.insert(1);
+ EXPECT_EQ(it_and_inserted.first, flat_set.find(1));
+ EXPECT_FALSE(it_and_inserted.second);
EXPECT_EQ(flat_set.size(), 1u);
EXPECT_TRUE(flat_set.count(1));
EXPECT_FALSE(flat_set.count(0));
@@ -62,10 +66,10 @@
EXPECT_FALSE(flat_set.count(1));
EXPECT_EQ(flat_set.size(), 0u);
- flat_set.insert(7);
- flat_set.insert(-4);
- flat_set.insert(11);
- flat_set.insert(-13);
+ EXPECT_TRUE(flat_set.insert(7).second);
+ EXPECT_TRUE(flat_set.insert(-4).second);
+ EXPECT_TRUE(flat_set.insert(11).second);
+ EXPECT_TRUE(flat_set.insert(-13).second);
EXPECT_TRUE(flat_set.count(7));
EXPECT_TRUE(flat_set.count(-4));
EXPECT_TRUE(flat_set.count(11));
@@ -94,8 +98,11 @@
for (int i = 0; i < 10000; i++) {
const int val = int_dist(rng);
if (i % 3) {
- flat_set.insert(val);
- gold_set.insert(val);
+ auto flat_result = flat_set.insert(val);
+ auto gold_result = gold_set.insert(val);
+ EXPECT_EQ(flat_result.first, flat_set.find(val));
+ EXPECT_EQ(gold_result.first, gold_set.find(val));
+ EXPECT_EQ(flat_result.second, gold_result.second);
} else {
flat_set.erase(val);
gold_set.erase(val);
diff --git a/src/base/watchdog_posix.cc b/src/base/watchdog_posix.cc
index 401ecdb..3517afd 100644
--- a/src/base/watchdog_posix.cc
+++ b/src/base/watchdog_posix.cc
@@ -28,8 +28,8 @@
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
+#include "perfetto/base/thread_utils.h"
#include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/thread_utils.h"
namespace perfetto {
namespace base {
diff --git a/src/base/watchdog_unittest.cc b/src/base/watchdog_unittest.cc
index f3bc850..2841a79 100644
--- a/src/base/watchdog_unittest.cc
+++ b/src/base/watchdog_unittest.cc
@@ -17,9 +17,9 @@
#include "perfetto/ext/base/watchdog.h"
#include "perfetto/base/logging.h"
+#include "perfetto/base/thread_utils.h"
#include "perfetto/ext/base/paged_memory.h"
#include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/thread_utils.h"
#include "test/gtest_and_gmock.h"
#include <signal.h>
diff --git a/src/profiling/memory/client.cc b/src/profiling/memory/client.cc
index 8bcb2a0..ed65372 100644
--- a/src/profiling/memory/client.cc
+++ b/src/profiling/memory/client.cc
@@ -35,9 +35,9 @@
#include <new>
#include "perfetto/base/logging.h"
+#include "perfetto/base/thread_utils.h"
#include "perfetto/base/time.h"
#include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/thread_utils.h"
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/base/utils.h"
#include "src/profiling/memory/sampler.h"
diff --git a/src/profiling/memory/client_unittest.cc b/src/profiling/memory/client_unittest.cc
index e2f0500..68b2ee7 100644
--- a/src/profiling/memory/client_unittest.cc
+++ b/src/profiling/memory/client_unittest.cc
@@ -18,7 +18,7 @@
#include <thread>
-#include "perfetto/ext/base/thread_utils.h"
+#include "perfetto/base/thread_utils.h"
#include "perfetto/ext/base/unix_socket.h"
#include "test/gtest_and_gmock.h"
diff --git a/src/traced/probes/filesystem/inode_file_data_source.h b/src/traced/probes/filesystem/inode_file_data_source.h
index ac5275c..8ccab1a 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.h
+++ b/src/traced/probes/filesystem/inode_file_data_source.h
@@ -25,8 +25,8 @@
#include <string>
#include <unordered_map>
+#include "perfetto/base/flat_set.h"
#include "perfetto/base/task_runner.h"
-#include "perfetto/ext/base/flat_set.h"
#include "perfetto/ext/base/weak_ptr.h"
#include "perfetto/ext/traced/data_source_types.h"
#include "perfetto/ext/tracing/core/basic_types.h"
diff --git a/src/traced/probes/ftrace/ftrace_metadata.h b/src/traced/probes/ftrace/ftrace_metadata.h
index de9d89a..8294f25 100644
--- a/src/traced/probes/ftrace/ftrace_metadata.h
+++ b/src/traced/probes/ftrace/ftrace_metadata.h
@@ -23,8 +23,8 @@
#include <bitset>
+#include "perfetto/base/flat_set.h"
#include "perfetto/base/logging.h"
-#include "perfetto/ext/base/flat_set.h"
#include "perfetto/ext/traced/data_source_types.h"
namespace perfetto {
diff --git a/src/traced/probes/ps/process_stats_data_source.h b/src/traced/probes/ps/process_stats_data_source.h
index 04c683e..100ef62 100644
--- a/src/traced/probes/ps/process_stats_data_source.h
+++ b/src/traced/probes/ps/process_stats_data_source.h
@@ -23,7 +23,7 @@
#include <unordered_map>
#include <vector>
-#include "perfetto/ext/base/flat_set.h"
+#include "perfetto/base/flat_set.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/weak_ptr.h"
#include "perfetto/ext/tracing/core/basic_types.h"
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index aa7c6f6..6fd71dc 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -250,6 +250,7 @@
"internal/track_event_internal.cc",
"platform.cc",
"tracing.cc",
+ "track.cc",
"track_event_category_registry.cc",
"virtual_destructors.cc",
]
diff --git a/src/tracing/api_integrationtest.cc b/src/tracing/api_integrationtest.cc
index a9f3fbb..2fea0d0 100644
--- a/src/tracing/api_integrationtest.cc
+++ b/src/tracing/api_integrationtest.cc
@@ -21,6 +21,7 @@
#include <functional>
#include <list>
#include <mutex>
+#include <thread>
#include <vector>
#include "perfetto/tracing.h"
@@ -50,14 +51,19 @@
#include "protos/perfetto/trace/trace.gen.h"
#include "protos/perfetto/trace/trace_packet.gen.h"
#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h"
+#include "protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"
#include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
#include "protos/perfetto/trace/track_event/log_message.gen.h"
#include "protos/perfetto/trace/track_event/log_message.pbzero.h"
#include "protos/perfetto/trace/track_event/process_descriptor.gen.h"
+#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
#include "protos/perfetto/trace/track_event/source_location.gen.h"
#include "protos/perfetto/trace/track_event/source_location.pbzero.h"
#include "protos/perfetto/trace/track_event/thread_descriptor.gen.h"
+#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_descriptor.gen.h"
#include "protos/perfetto/trace/track_event/track_event.gen.h"
// Trace categories used in the tests.
@@ -345,7 +351,8 @@
bool incremental_state_was_cleared = false;
uint32_t sequence_id = 0;
for (const auto& packet : parsed_trace.packet()) {
- if (packet.incremental_state_cleared()) {
+ if (packet.sequence_flags() & perfetto::protos::pbzero::TracePacket::
+ SEQ_INCREMENTAL_STATE_CLEARED) {
incremental_state_was_cleared = true;
categories.clear();
event_names.clear();
@@ -558,25 +565,21 @@
bool begin_found = false;
bool end_found = false;
bool process_descriptor_found = false;
- bool thread_descriptor_found = false;
auto now = perfetto::test::GetTraceTimeNs();
uint32_t sequence_id = 0;
int32_t cur_pid = perfetto::test::GetCurrentProcessId();
for (const auto& packet : trace.packet()) {
- if (packet.has_process_descriptor()) {
- EXPECT_FALSE(process_descriptor_found);
- const auto& pd = packet.process_descriptor();
- EXPECT_EQ(cur_pid, pd.pid());
- process_descriptor_found = true;
+ if (packet.has_track_descriptor()) {
+ const auto& desc = packet.track_descriptor();
+ if (desc.has_process()) {
+ EXPECT_FALSE(process_descriptor_found);
+ const auto& pd = desc.process();
+ EXPECT_EQ(cur_pid, pd.pid());
+ process_descriptor_found = true;
+ }
}
- if (packet.has_thread_descriptor()) {
- EXPECT_FALSE(thread_descriptor_found);
- const auto& td = packet.thread_descriptor();
- EXPECT_EQ(cur_pid, td.pid());
- EXPECT_NE(0, td.tid());
- thread_descriptor_found = true;
- }
- if (packet.incremental_state_cleared()) {
+ if (packet.sequence_flags() &
+ perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) {
EXPECT_TRUE(packet.has_trace_packet_defaults());
incremental_state_was_cleared = true;
categories.clear();
@@ -585,6 +588,10 @@
if (!packet.has_track_event())
continue;
+ EXPECT_TRUE(
+ packet.sequence_flags() &
+ (perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED |
+ perfetto::protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE));
const auto& track_event = packet.track_event();
// Make sure we only see track events on one sequence.
@@ -636,7 +643,6 @@
}
EXPECT_TRUE(incremental_state_was_cleared);
EXPECT_TRUE(process_descriptor_found);
- EXPECT_TRUE(thread_descriptor_found);
EXPECT_TRUE(begin_found);
EXPECT_TRUE(end_found);
}
@@ -828,6 +834,230 @@
EXPECT_THAT(trace2, Not(HasSubstr("Session2_Third")));
}
+TEST_F(PerfettoApiTest, TrackEventProcessAndThreadDescriptors) {
+ // Thread and process descriptors can be set before tracing is enabled.
+ perfetto::TrackEvent::SetProcessDescriptor(
+ [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+ desc->set_name("hello.exe");
+ desc->set_chrome_process()->set_process_priority(1);
+ });
+
+ // Erased tracks shouldn't show up anywhere.
+ perfetto::Track erased(1234u);
+ perfetto::TrackEvent::SetTrackDescriptor(
+ erased, [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+ desc->set_name("ErasedTrack");
+ });
+ perfetto::TrackEvent::EraseTrackDescriptor(erased);
+
+ // Setup the trace config.
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name("track_event");
+
+ // Create a new trace session.
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+ TRACE_EVENT_INSTANT("test", "MainThreadEvent");
+
+ std::thread thread([&] {
+ perfetto::TrackEvent::SetThreadDescriptor(
+ [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+ desc->set_name("TestThread");
+ });
+ TRACE_EVENT_INSTANT("test", "ThreadEvent");
+ });
+ thread.join();
+
+ // Update the process descriptor while tracing is enabled. It should be
+ // immediately reflected in the trace.
+ perfetto::TrackEvent::SetProcessDescriptor(
+ [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+ desc->set_name("goodbye.exe");
+ });
+ perfetto::TrackEvent::Flush();
+
+ tracing_session->get()->StopBlocking();
+
+ // After tracing ends, setting the descriptor has no immediate effect.
+ perfetto::TrackEvent::SetProcessDescriptor(
+ [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+ desc->set_name("noop.exe");
+ });
+
+ std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+ perfetto::protos::gen::Trace trace;
+ ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+ std::vector<perfetto::protos::gen::TrackDescriptor> descs;
+ std::vector<perfetto::protos::gen::TrackDescriptor> thread_descs;
+ constexpr uint32_t kMainThreadSequence = 2;
+ for (const auto& packet : trace.packet()) {
+ if (packet.has_track_descriptor()) {
+ if (packet.trusted_packet_sequence_id() == kMainThreadSequence) {
+ descs.push_back(packet.track_descriptor());
+ } else {
+ thread_descs.push_back(packet.track_descriptor());
+ }
+ }
+ }
+
+ // The main thread records the initial process name as well as the one that's
+ // set during tracing. Additionally it records a thread descriptor for the
+ // main thread.
+
+ EXPECT_EQ(3u, descs.size());
+
+ // Default track for the main thread.
+ EXPECT_EQ(0, descs[0].process().pid());
+ EXPECT_NE(0, descs[0].thread().pid());
+
+ // First process descriptor.
+ EXPECT_NE(0, descs[1].process().pid());
+ EXPECT_EQ("hello.exe", descs[1].name());
+
+ // Second process descriptor.
+ EXPECT_NE(0, descs[2].process().pid());
+ EXPECT_EQ("goodbye.exe", descs[2].name());
+
+ // The child thread records only its own thread descriptor (twice, since it
+ // was mutated).
+ EXPECT_EQ(2u, thread_descs.size());
+ EXPECT_EQ("TestThread", thread_descs[0].name());
+ EXPECT_NE(0, thread_descs[0].thread().pid());
+ EXPECT_NE(0, thread_descs[0].thread().tid());
+ EXPECT_EQ("TestThread", thread_descs[1].name());
+ EXPECT_NE(0, thread_descs[1].thread().pid());
+ EXPECT_NE(0, thread_descs[1].thread().tid());
+}
+
+TEST_F(PerfettoApiTest, TrackEventCustomTrack) {
+ // Setup the trace config.
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name("track_event");
+ ds_cfg->set_legacy_config("bar");
+
+ // Create a new trace session.
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+
+ // Declare a custom track and give it a name.
+ uint64_t async_id = 123;
+ perfetto::TrackEvent::SetTrackDescriptor(
+ perfetto::Track(async_id),
+ [](perfetto::protos::pbzero::TrackDescriptor* desc) {
+ desc->set_name("MyCustomTrack");
+ });
+
+ // Start events on one thread and end them on another.
+ TRACE_EVENT_BEGIN("bar", "AsyncEvent", perfetto::Track(async_id), "debug_arg",
+ 123);
+
+ TRACE_EVENT_BEGIN("bar", "SubEvent", perfetto::Track(async_id),
+ [](perfetto::EventContext) {});
+ const auto main_thread_track =
+ perfetto::Track(async_id, perfetto::ThreadTrack::Current());
+ std::thread thread([&] {
+ TRACE_EVENT_END("bar", perfetto::Track(async_id));
+ TRACE_EVENT_END("bar", perfetto::Track(async_id), "arg1", false, "arg2",
+ true);
+ const auto thread_track =
+ perfetto::Track(async_id, perfetto::ThreadTrack::Current());
+ // Thread-scoped tracks will have different uuids on different thread even
+ // if the id matches.
+ ASSERT_NE(main_thread_track.uuid, thread_track.uuid);
+ });
+ thread.join();
+
+ perfetto::TrackEvent::Flush();
+ tracing_session->get()->StopBlocking();
+
+ std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+ perfetto::protos::gen::Trace trace;
+ ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+ // Check that the track uuids match on the begin and end events.
+ const auto track = perfetto::Track(async_id);
+ constexpr uint32_t kMainThreadSequence = 2;
+ int event_count = 0;
+ bool found_descriptor = false;
+ for (const auto& packet : trace.packet()) {
+ if (packet.has_track_descriptor() &&
+ !packet.track_descriptor().has_process() &&
+ !packet.track_descriptor().has_thread()) {
+ auto td = packet.track_descriptor();
+ EXPECT_EQ("MyCustomTrack", td.name());
+ EXPECT_EQ(track.uuid, td.uuid());
+ EXPECT_EQ(perfetto::ProcessTrack::Current().uuid, td.parent_uuid());
+ found_descriptor = true;
+ continue;
+ }
+
+ if (!packet.has_track_event())
+ continue;
+ auto track_event = packet.track_event();
+ if (track_event.type() ==
+ perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN) {
+ EXPECT_EQ(kMainThreadSequence, packet.trusted_packet_sequence_id());
+ EXPECT_EQ(track.uuid, track_event.track_uuid());
+ } else {
+ EXPECT_NE(kMainThreadSequence, packet.trusted_packet_sequence_id());
+ EXPECT_EQ(track.uuid, track_event.track_uuid());
+ }
+ event_count++;
+ }
+ EXPECT_TRUE(found_descriptor);
+ EXPECT_EQ(4, event_count);
+ perfetto::TrackEvent::EraseTrackDescriptor(track);
+}
+
+TEST_F(PerfettoApiTest, TrackEventAnonymousCustomTrack) {
+ // Setup the trace config.
+ perfetto::TraceConfig cfg;
+ cfg.set_duration_ms(500);
+ cfg.add_buffers()->set_size_kb(1024);
+ auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+ ds_cfg->set_name("track_event");
+ ds_cfg->set_legacy_config("bar");
+
+ // Create a new trace session.
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+
+ // Emit an async event without giving it an explicit descriptor.
+ uint64_t async_id = 4004;
+ auto track = perfetto::Track(async_id, perfetto::ThreadTrack::Current());
+ TRACE_EVENT_BEGIN("bar", "AsyncEvent", track);
+ std::thread thread([&] { TRACE_EVENT_END("bar", track); });
+ thread.join();
+
+ perfetto::TrackEvent::Flush();
+ tracing_session->get()->StopBlocking();
+
+ std::vector<char> raw_trace = tracing_session->get()->ReadTraceBlocking();
+ perfetto::protos::gen::Trace trace;
+ ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+
+ // Check that a descriptor for the track was emitted.
+ bool found_descriptor = false;
+ for (const auto& packet : trace.packet()) {
+ if (packet.has_track_descriptor() &&
+ !packet.track_descriptor().has_process() &&
+ !packet.track_descriptor().has_thread()) {
+ auto td = packet.track_descriptor();
+ EXPECT_EQ(track.uuid, td.uuid());
+ EXPECT_EQ(perfetto::ThreadTrack::Current().uuid, td.parent_uuid());
+ found_descriptor = true;
+ }
+ }
+ EXPECT_TRUE(found_descriptor);
+}
+
TEST_F(PerfettoApiTest, TrackEventTypedArgs) {
// Setup the trace config.
perfetto::TraceConfig cfg;
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index 71afc7b..7bd766c 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -16,9 +16,9 @@
#include "perfetto/tracing/internal/track_event_internal.h"
+#include "perfetto/base/proc_utils.h"
+#include "perfetto/base/thread_utils.h"
#include "perfetto/base/time.h"
-#include "perfetto/ext/base/proc_utils.h"
-#include "perfetto/ext/base/thread_utils.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/track_event.h"
#include "perfetto/tracing/track_event_category_registry.h"
@@ -28,8 +28,7 @@
#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
-#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
-#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
namespace perfetto {
namespace internal {
@@ -95,49 +94,6 @@
#endif
}
-uint64_t GetTimeNs() {
- if (GetClockType() == protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
- return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
- PERFETTO_DCHECK(GetClockType() ==
- protos::pbzero::ClockSnapshot::Clock::MONOTONIC);
- return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
-}
-
-protozero::MessageHandle<protos::pbzero::TracePacket> NewTracePacket(
- TraceWriterBase* trace_writer,
- uint64_t timestamp) {
- auto packet = trace_writer->NewTracePacket();
- packet->set_timestamp(timestamp);
- // TODO(skyostil): Stop emitting this for every event once the trace processor
- // understands trace packet defaults.
- if (GetClockType() != protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
- packet->set_timestamp_clock_id(GetClockType());
- return packet;
-}
-
-// static
-void WriteSequenceDescriptors(TraceWriterBase* trace_writer,
- uint64_t timestamp) {
- if (perfetto::base::GetThreadId() == g_main_thread) {
- auto packet = NewTracePacket(trace_writer, timestamp);
- packet->set_incremental_state_cleared(true);
- auto defaults = packet->set_trace_packet_defaults();
- defaults->set_timestamp_clock_id(GetClockType());
- auto pd = packet->set_process_descriptor();
- pd->set_pid(static_cast<int32_t>(base::GetProcessId()));
- // TODO(skyostil): Record command line.
- }
- {
- auto packet = NewTracePacket(trace_writer, timestamp);
- packet->set_incremental_state_cleared(true);
- auto defaults = packet->set_trace_packet_defaults();
- defaults->set_timestamp_clock_id(GetClockType());
- auto td = packet->set_thread_descriptor();
- td->set_pid(static_cast<int32_t>(base::GetProcessId()));
- td->set_tid(static_cast<int32_t>(perfetto::base::GetThreadId()));
- }
-}
-
} // namespace
// static
@@ -177,6 +133,57 @@
}
// static
+uint64_t TrackEventInternal::GetTimeNs() {
+ if (GetClockType() == protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
+ return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
+ PERFETTO_DCHECK(GetClockType() ==
+ protos::pbzero::ClockSnapshot::Clock::MONOTONIC);
+ return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
+}
+
+// static
+void TrackEventInternal::ResetIncrementalState(TraceWriterBase* trace_writer,
+ uint64_t timestamp) {
+ auto default_track = ThreadTrack::Current();
+ {
+ // Mark any incremental state before this point invalid. Also set up
+ // defaults so that we don't need to repeat constant data for each packet.
+ auto packet = NewTracePacket(
+ trace_writer, timestamp,
+ protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
+ auto defaults = packet->set_trace_packet_defaults();
+ defaults->set_timestamp_clock_id(GetClockType());
+
+ // Establish the default track for this event sequence.
+ auto track_defaults = defaults->set_track_event_defaults();
+ track_defaults->set_track_uuid(default_track.uuid);
+ }
+
+ // Every thread should write a descriptor for its default track, because most
+ // trace points won't explicitly reference it.
+ WriteTrackDescriptor(default_track, trace_writer);
+
+ // Additionally the main thread should dump the process descriptor.
+ if (perfetto::base::GetThreadId() == g_main_thread)
+ WriteTrackDescriptor(ProcessTrack::Current(), trace_writer);
+}
+
+// static
+protozero::MessageHandle<protos::pbzero::TracePacket>
+TrackEventInternal::NewTracePacket(TraceWriterBase* trace_writer,
+ uint64_t timestamp,
+ uint32_t seq_flags) {
+ auto packet = trace_writer->NewTracePacket();
+ packet->set_timestamp(timestamp);
+ // TODO(skyostil): Stop emitting this for every event once the trace processor
+ // understands trace packet defaults.
+ if (GetClockType() != protos::pbzero::ClockSnapshot::Clock::BOOTTIME)
+ packet->set_timestamp_clock_id(GetClockType());
+ packet->set_sequence_flags(seq_flags);
+ return packet;
+}
+
+// static
EventContext TrackEventInternal::WriteEvent(
TraceWriterBase* trace_writer,
TrackEventIncrementalState* incr_state,
@@ -189,7 +196,7 @@
if (incr_state->was_cleared) {
incr_state->was_cleared = false;
- WriteSequenceDescriptors(trace_writer, timestamp);
+ ResetIncrementalState(trace_writer, timestamp);
}
auto packet = NewTracePacket(trace_writer, timestamp);
diff --git a/src/tracing/test/api_test_support.cc b/src/tracing/test/api_test_support.cc
index 24d8d67..1667ffc 100644
--- a/src/tracing/test/api_test_support.cc
+++ b/src/tracing/test/api_test_support.cc
@@ -16,8 +16,8 @@
#include "src/tracing/test/api_test_support.h"
+#include "perfetto/base/proc_utils.h"
#include "perfetto/base/time.h"
-#include "perfetto/ext/base/proc_utils.h"
namespace perfetto {
namespace test {
diff --git a/src/tracing/test/tracing_module.cc b/src/tracing/test/tracing_module.cc
index 09b6e5f..7c10d4e 100644
--- a/src/tracing/test/tracing_module.cc
+++ b/src/tracing/test/tracing_module.cc
@@ -82,4 +82,11 @@
puts("Hello");
}
+void FunctionWithOneTrackEventWithCustomTrack() {
+ TRACE_EVENT_BEGIN("cat1", "EventWithTrack", perfetto::Track(8086));
+ // Simulates the non-tracing work of this function, which should take priority
+ // over the above trace event in terms of instruction scheduling.
+ puts("Hello");
+}
+
} // namespace tracing_module
diff --git a/src/tracing/test/tracing_module.h b/src/tracing/test/tracing_module.h
index e0fd1b3..c005a6f 100644
--- a/src/tracing/test/tracing_module.h
+++ b/src/tracing/test/tracing_module.h
@@ -37,6 +37,7 @@
void FunctionWithOneTrackEventWithTypedArgument();
void FunctionWithOneScopedTrackEvent();
void FunctionWithOneTrackEventWithDebugAnnotations();
+void FunctionWithOneTrackEventWithCustomTrack();
} // namespace tracing_module
diff --git a/src/tracing/tracing.cc b/src/tracing/tracing.cc
index fc6a696..54d5722 100644
--- a/src/tracing/tracing.cc
+++ b/src/tracing/tracing.cc
@@ -15,6 +15,7 @@
*/
#include "perfetto/tracing/tracing.h"
+#include "perfetto/tracing/internal/track_event_internal.h"
#include "src/tracing/internal/tracing_muxer_impl.h"
#include <condition_variable>
@@ -27,6 +28,7 @@
// Make sure the headers and implementation files agree on the build config.
PERFETTO_CHECK(args.dcheck_is_on_ == PERFETTO_DCHECK_IS_ON());
internal::TracingMuxerImpl::InitializeInstance(args);
+ internal::TrackRegistry::InitializeInstance();
}
// static
diff --git a/src/tracing/track.cc b/src/tracing/track.cc
new file mode 100644
index 0000000..91045c4
--- /dev/null
+++ b/src/tracing/track.cc
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#include "perfetto/tracing/track.h"
+
+#include "perfetto/ext/base/uuid.h"
+#include "perfetto/tracing/internal/track_event_data_source.h"
+#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+
+namespace perfetto {
+
+// static
+uint64_t Track::process_uuid;
+
+void Track::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+ desc->set_uuid(uuid);
+ if (parent_uuid)
+ desc->set_parent_uuid(parent_uuid);
+}
+
+void ProcessTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+ Track::Serialize(desc);
+ auto pd = desc->set_process();
+ pd->set_pid(pid);
+ // TODO(skyostil): Record command line.
+}
+
+void ThreadTrack::Serialize(protos::pbzero::TrackDescriptor* desc) const {
+ Track::Serialize(desc);
+ auto td = desc->set_thread();
+ td->set_pid(pid);
+ td->set_tid(tid);
+ // TODO(skyostil): Record thread name.
+}
+
+namespace internal {
+
+// static
+TrackRegistry* TrackRegistry::instance_;
+
+TrackRegistry::TrackRegistry() = default;
+TrackRegistry::~TrackRegistry() = default;
+
+// static
+void TrackRegistry::InitializeInstance() {
+ PERFETTO_DCHECK(!instance_);
+ instance_ = new TrackRegistry();
+ Track::process_uuid = static_cast<uint64_t>(base::Uuidv4().lsb());
+}
+
+void TrackRegistry::UpdateTrackImpl(
+ Track track,
+ std::function<void(protos::pbzero::TrackDescriptor*)> fill_function) {
+ constexpr size_t kInitialSliceSize = 32;
+ constexpr size_t kMaximumSliceSize = 4096;
+ protozero::HeapBuffered<protos::pbzero::TrackDescriptor> new_descriptor(
+ kInitialSliceSize, kMaximumSliceSize);
+ fill_function(new_descriptor.get());
+ auto serialized_desc = new_descriptor.SerializeAsString();
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ tracks_[track.uuid] = std::move(serialized_desc);
+ }
+}
+
+void TrackRegistry::EraseTrack(Track track) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ tracks_.erase(track.uuid);
+}
+
+// static
+void TrackRegistry::WriteTrackDescriptor(
+ const SerializedTrackDescriptor& desc,
+ protozero::MessageHandle<protos::pbzero::TracePacket> packet) {
+ packet->AppendString(
+ perfetto::protos::pbzero::TracePacket::kTrackDescriptorFieldNumber, desc);
+}
+
+} // namespace internal
+} // namespace perfetto