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