TrackEvent: Add support for debug annotations
This patch makes it possible to attach one or two debug annotations to a
trace event, e.g.:
TRACE_EVENT_BEGIN("cat", "Name", "color", 0xffaaccee, "density", .5f);
The user can also specify a custom debug annotation object that will
get serialized to the DebugAnnotation proto through a callback. This is
intended for implementing the legacy TracedValue system on top of
TrackEvent.
Bug: 132678367
Change-Id: I769c2eb346d3bdfac50158a333481f83962bfb6b
diff --git a/Android.bp b/Android.bp
index 49b651e..0cc92d8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5079,6 +5079,7 @@
name: "perfetto_src_tracing_client_api",
srcs: [
"src/tracing/data_source.cc",
+ "src/tracing/debug_annotation.cc",
"src/tracing/event_context.cc",
"src/tracing/internal/in_process_tracing_backend.cc",
"src/tracing/internal/system_tracing_backend.cc",
diff --git a/BUILD b/BUILD
index 5c820db..fc391aa 100644
--- a/BUILD
+++ b/BUILD
@@ -418,6 +418,7 @@
srcs = [
"include/perfetto/tracing/buffer_exhausted_policy.h",
"include/perfetto/tracing/data_source.h",
+ "include/perfetto/tracing/debug_annotation.h",
"include/perfetto/tracing/event_context.h",
"include/perfetto/tracing/internal/basic_types.h",
"include/perfetto/tracing/internal/data_source_internal.h",
@@ -1021,6 +1022,7 @@
name = "src_tracing_client_api",
srcs = [
"src/tracing/data_source.cc",
+ "src/tracing/debug_annotation.cc",
"src/tracing/event_context.cc",
"src/tracing/internal/in_process_tracing_backend.cc",
"src/tracing/internal/in_process_tracing_backend.h",
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index 2a65756..01294bd 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -27,6 +27,7 @@
sources = [
"buffer_exhausted_policy.h",
"data_source.h",
+ "debug_annotation.h",
"event_context.h",
"internal/basic_types.h",
"internal/data_source_internal.h",
diff --git a/include/perfetto/tracing/debug_annotation.h b/include/perfetto/tracing/debug_annotation.h
new file mode 100644
index 0000000..996861c
--- /dev/null
+++ b/include/perfetto/tracing/debug_annotation.h
@@ -0,0 +1,62 @@
+/*
+ * 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_DEBUG_ANNOTATION_H_
+#define INCLUDE_PERFETTO_TRACING_DEBUG_ANNOTATION_H_
+
+#include "perfetto/base/export.h"
+
+#include <stdint.h>
+
+#include <string>
+
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+class DebugAnnotation;
+} // namespace pbzero
+} // namespace protos
+
+// A base class for custom track event debug annotations.
+class PERFETTO_EXPORT DebugAnnotation {
+ public:
+ DebugAnnotation() = default;
+ virtual ~DebugAnnotation();
+
+ // Called to write the contents of the debug annotation into the trace.
+ virtual void Add(protos::pbzero::DebugAnnotation*) const = 0;
+};
+
+namespace internal {
+
+// Overloads for all the supported built in debug annotation types.
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, bool);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, uint64_t);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, unsigned);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, int64_t);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, int);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, double);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, float);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, const char*);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, const std::string&);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*, const void*);
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation*,
+ const DebugAnnotation&);
+
+} // namespace internal
+} // namespace perfetto
+
+#endif // INCLUDE_PERFETTO_TRACING_DEBUG_ANNOTATION_H_
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index 6a2ae69..3e3c9fa 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -96,6 +96,49 @@
});
}
+ // Trace point with one debug annotation.
+ template <size_t CategoryIndex, typename ArgType>
+ static void TraceForCategory(uint32_t instances,
+ const char* event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ const char* arg_name,
+ ArgType 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,
+ std::move(arg_value));
+ });
+ }
+
+ // Trace point with two debug annotations. Note that we only support up to two
+ // direct debug annotations. For more complicated arguments, you should
+ // define your own argument type in track_event.proto and use a lambda to fill
+ // it in your trace point.
+ template <size_t CategoryIndex, typename ArgType, typename ArgType2>
+ static void TraceForCategory(uint32_t instances,
+ const char* event_name,
+ perfetto::protos::pbzero::TrackEvent::Type type,
+ const char* arg_name,
+ ArgType arg_value,
+ const char* arg_name2,
+ ArgType2 arg_value2) 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,
+ std::move(arg_value));
+ TrackEventInternal::AddDebugAnnotation(&event_ctx, arg_name2,
+ std::move(arg_value2));
+ });
+ }
+
static bool Register() {
// Registration is performed out-of-line so users don't need to depend on
// DataSourceDescriptor C++ bindings.
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index 7329201..2a68702 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -21,12 +21,18 @@
#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 "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
namespace perfetto {
class EventContext;
+namespace protos {
+namespace pbzero {
+class DebugAnnotation;
+} // namespace pbzero
+} // namespace protos
namespace internal {
class TrackEventCategoryRegistry;
@@ -86,6 +92,19 @@
const char* category,
const char* name,
perfetto::protos::pbzero::TrackEvent::Type);
+
+ template <typename T>
+ static void AddDebugAnnotation(perfetto::EventContext* event_ctx,
+ const char* name,
+ T value) {
+ auto annotation = AddDebugAnnotation(event_ctx, name);
+ WriteDebugAnnotation(annotation, value);
+ }
+
+ private:
+ static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
+ perfetto::EventContext*,
+ const char* name);
};
} // namespace internal
diff --git a/include/perfetto/tracing/track_event.h b/include/perfetto/tracing/track_event.h
index 2b7d1dd..371346b 100644
--- a/include/perfetto/tracing/track_event.h
+++ b/include/perfetto/tracing/track_event.h
@@ -54,8 +54,18 @@
//
// int main() {
// perfetto::TrackEvent::Register();
-// TRACK_EVENT("category", "MyEvent");
-// ...
+//
+// // A basic track event with just a name.
+// TRACE_EVENT("category", "MyEvent");
+//
+// // A track event with (up to two) debug annotations.
+// TRACE_EVENT("category", "MyEvent", "parameter", 42);
+//
+// // A track event with a strongly typed parameter.
+// TRACE_EVENT("category", "MyEvent", [](perfetto::EventContext ctx) {
+// ctx.event()->set_foo(42);
+// ctx.event()->set_bar(.5f);
+// });
// }
//
// ====================
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index c8632c5..040cc97 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -261,6 +261,7 @@
]
sources = [
"data_source.cc",
+ "debug_annotation.cc",
"event_context.cc",
"internal/in_process_tracing_backend.cc",
"internal/in_process_tracing_backend.h",
diff --git a/src/tracing/api_integrationtest.cc b/src/tracing/api_integrationtest.cc
index 9022be2..5112de8 100644
--- a/src/tracing/api_integrationtest.cc
+++ b/src/tracing/api_integrationtest.cc
@@ -320,6 +320,7 @@
std::vector<std::string> slices;
std::map<uint64_t, std::string> categories;
std::map<uint64_t, std::string> event_names;
+ std::map<uint64_t, std::string> debug_annotation_names;
perfetto::protos::Trace parsed_trace;
EXPECT_TRUE(
parsed_trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
@@ -331,6 +332,7 @@
incremental_state_was_cleared = true;
categories.clear();
event_names.clear();
+ debug_annotation_names.clear();
}
if (!packet.has_track_event())
@@ -354,6 +356,11 @@
EXPECT_EQ(event_names.find(it.iid()), event_names.end());
event_names[it.iid()] = it.name();
}
+ for (const auto& it : interned_data.debug_annotation_names()) {
+ EXPECT_EQ(debug_annotation_names.find(it.iid()),
+ debug_annotation_names.end());
+ debug_annotation_names[it.iid()] = it.name();
+ }
}
const auto& track_event = packet.track_event();
std::string slice;
@@ -373,6 +380,37 @@
}
slice += ":" + categories[track_event.category_iids().Get(0)] + "." +
event_names[track_event.name_iid()];
+
+ if (track_event.debug_annotations_size()) {
+ slice += "(";
+ bool first_annotation = true;
+ for (const auto& it : track_event.debug_annotations()) {
+ if (!first_annotation) {
+ slice += ",";
+ }
+ slice += debug_annotation_names[it.name_iid()] + "=";
+ std::stringstream value;
+ if (it.has_bool_value()) {
+ value << "(bool)" << it.bool_value();
+ } else if (it.has_uint_value()) {
+ value << "(uint)" << it.uint_value();
+ } else if (it.has_int_value()) {
+ value << "(int)" << it.int_value();
+ } else if (it.has_double_value()) {
+ value << "(double)" << it.double_value();
+ } else if (it.has_string_value()) {
+ value << "(string)" << it.string_value();
+ } else if (it.has_pointer_value()) {
+ value << "(pointer)" << std::hex << it.pointer_value();
+ } else if (it.has_legacy_json_value()) {
+ value << "(json)" << it.legacy_json_value();
+ }
+ slice += value.str();
+ first_annotation = false;
+ }
+ slice += ")";
+ }
+
slices.push_back(slice);
}
EXPECT_TRUE(incremental_state_was_cleared);
@@ -1108,6 +1146,79 @@
EXPECT_THAT(slices, ElementsAre("I:test.TestEvent", "I:test.AnotherEvent"));
}
+TEST_F(PerfettoApiTest, TrackEventDebugAnnotations) {
+ // 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("test");
+
+ // Create a new trace session.
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+
+ TRACE_EVENT_BEGIN("test", "E", "bool_arg", false);
+ TRACE_EVENT_BEGIN("test", "E", "int_arg", -123);
+ TRACE_EVENT_BEGIN("test", "E", "uint_arg", 456u);
+ TRACE_EVENT_BEGIN("test", "E", "float_arg", 3.14159262f);
+ TRACE_EVENT_BEGIN("test", "E", "double_arg", 6.22);
+ TRACE_EVENT_BEGIN("test", "E", "str_arg", "hello", "str_arg2",
+ std::string("tracing"));
+ TRACE_EVENT_BEGIN("test", "E", "ptr_arg",
+ reinterpret_cast<void*>(0xbaadf00d));
+ perfetto::TrackEvent::Flush();
+
+ tracing_session->get()->StopBlocking();
+ auto slices = ReadSlicesFromTrace(tracing_session->get());
+ EXPECT_THAT(
+ slices,
+ ElementsAre("B:test.E(bool_arg=(bool)0)", "B:test.E(int_arg=(int)-123)",
+ "B:test.E(uint_arg=(uint)456)",
+ "B:test.E(float_arg=(double)3.14159)",
+ "B:test.E(double_arg=(double)6.22)",
+ "B:test.E(str_arg=(string)hello,str_arg2=(string)tracing)",
+ "B:test.E(ptr_arg=(pointer)baadf00d)"));
+}
+
+TEST_F(PerfettoApiTest, TrackEventCustomDebugAnnotations) {
+ // 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("test");
+
+ class MyDebugAnnotation : public perfetto::DebugAnnotation {
+ public:
+ ~MyDebugAnnotation() override = default;
+
+ void Add(
+ perfetto::protos::pbzero::DebugAnnotation* annotation) const override {
+ annotation->set_legacy_json_value(R"({"key": 123})");
+ }
+ };
+
+ // Create a new trace session.
+ auto* tracing_session = NewTrace(cfg);
+ tracing_session->get()->StartBlocking();
+
+ TRACE_EVENT_BEGIN("test", "E", "custom_arg", MyDebugAnnotation());
+ TRACE_EVENT_BEGIN("test", "E", "normal_arg", "x", "custom_arg",
+ MyDebugAnnotation());
+ perfetto::TrackEvent::Flush();
+
+ tracing_session->get()->StopBlocking();
+ auto slices = ReadSlicesFromTrace(tracing_session->get());
+ EXPECT_THAT(
+ slices,
+ ElementsAre(
+ R"(B:test.E(custom_arg=(json){"key": 123}))",
+ R"(B:test.E(normal_arg=(string)x,custom_arg=(json){"key": 123}))"));
+}
+
TEST_F(PerfettoApiTest, OneDataSourceOneEvent) {
auto* data_source = &data_sources_["my_data_source"];
diff --git a/src/tracing/debug_annotation.cc b/src/tracing/debug_annotation.cc
new file mode 100644
index 0000000..d98e6ce
--- /dev/null
+++ b/src/tracing/debug_annotation.cc
@@ -0,0 +1,83 @@
+/*
+ * 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/debug_annotation.h"
+
+#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
+
+namespace perfetto {
+
+DebugAnnotation::~DebugAnnotation() = default;
+
+namespace internal {
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ bool value) {
+ annotation->set_bool_value(value);
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ uint64_t value) {
+ annotation->set_uint_value(value);
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ unsigned value) {
+ annotation->set_uint_value(value);
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ int64_t value) {
+ annotation->set_int_value(value);
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ int value) {
+ annotation->set_int_value(value);
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ double value) {
+ annotation->set_double_value(value);
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ float value) {
+ annotation->set_double_value(static_cast<double>(value));
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ const char* value) {
+ annotation->set_string_value(value);
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ const std::string& value) {
+ annotation->set_string_value(value);
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ const void* value) {
+ annotation->set_pointer_value(reinterpret_cast<uint64_t>(value));
+}
+
+void WriteDebugAnnotation(protos::pbzero::DebugAnnotation* annotation,
+ const DebugAnnotation& custom_annotation) {
+ custom_annotation.Add(annotation);
+}
+
+} // namespace internal
+} // namespace perfetto
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index 198586a..7ca7e27 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -25,6 +25,7 @@
#include "perfetto/tracing/track_event_interned_data_index.h"
#include "protos/perfetto/common/data_source_descriptor.gen.h"
#include "protos/perfetto/trace/interned_data/interned_data.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"
@@ -67,6 +68,22 @@
}
};
+struct InternedDebugAnnotationName
+ : public TrackEventInternedDataIndex<
+ InternedDebugAnnotationName,
+ perfetto::protos::pbzero::InternedData::
+ kDebugAnnotationNamesFieldNumber,
+ const char*,
+ SmallInternedDataTraits> {
+ static void Add(protos::pbzero::InternedData* interned_data,
+ size_t iid,
+ const char* value) {
+ auto name = interned_data->add_debug_annotation_names();
+ name->set_iid(iid);
+ name->set_name(value);
+ }
+};
+
uint64_t GetTimeNs() {
// TODO(skyostil): Consider using boot time where available.
return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
@@ -164,5 +181,14 @@
return ctx;
}
+// static
+protos::pbzero::DebugAnnotation* TrackEventInternal::AddDebugAnnotation(
+ perfetto::EventContext* event_ctx,
+ const char* name) {
+ auto annotation = event_ctx->event()->add_debug_annotations();
+ annotation->set_name_iid(InternedDebugAnnotationName::Get(event_ctx, name));
+ return annotation;
+}
+
} // namespace internal
} // namespace perfetto
diff --git a/src/tracing/test/tracing_module.cc b/src/tracing/test/tracing_module.cc
index 2df377f0..09b6e5f 100644
--- a/src/tracing/test/tracing_module.cc
+++ b/src/tracing/test/tracing_module.cc
@@ -75,4 +75,11 @@
puts("Hello");
}
+void FunctionWithOneTrackEventWithDebugAnnotations() {
+ TRACE_EVENT_BEGIN("cat1", "EventWithAnnotations", "p1", 42, "p2", .5f);
+ // 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 bb255a1..e0fd1b3 100644
--- a/src/tracing/test/tracing_module.h
+++ b/src/tracing/test/tracing_module.h
@@ -36,6 +36,7 @@
void FunctionWithOneTrackEvent();
void FunctionWithOneTrackEventWithTypedArgument();
void FunctionWithOneScopedTrackEvent();
+void FunctionWithOneTrackEventWithDebugAnnotations();
} // namespace tracing_module