Merge "metrics: add support for extension fields in descriptors"
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index c5a6ca5..5fe14a2 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -167,6 +167,7 @@
     "../../protos/perfetto/metrics/android:zero",
     "../../protos/perfetto/trace:zero",
     "../../protos/perfetto/trace/android:zero",
+    "../../protos/perfetto/trace/chrome:zero",
     "../../protos/perfetto/trace/ftrace:zero",
     "../../protos/perfetto/trace/interned_data:zero",
     "../../protos/perfetto/trace/power:zero",
@@ -268,6 +269,7 @@
     "../../gn:gtest_deps",
     "../../protos/perfetto/common:zero",
     "../../protos/perfetto/trace:zero",
+    "../../protos/perfetto/trace/chrome:zero",
     "../../protos/perfetto/trace/ftrace:zero",
     "../../protos/perfetto/trace/interned_data:zero",
     "../../protos/perfetto/trace/ps:zero",
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index 1d3d269..ef0870e 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -15,6 +15,7 @@
  */
 
 #include "src/trace_processor/export_json.h"
+#include "src/trace_processor/metadata.h"
 
 #include <json/value.h>
 #include <json/writer.h>
@@ -78,16 +79,36 @@
     first_event_ = false;
   }
 
+  void AppendTelemetryMetadataString(const char* key, const char* value) {
+    metadata_["telemetry"][key].append(value);
+  }
+
+  void AppendTelemetryMetadataInt(const char* key, int64_t value) {
+    metadata_["telemetry"][key].append(Json::Int64(value));
+  }
+
+  void AppendTelemetryMetadataBool(const char* key, bool value) {
+    metadata_["telemetry"][key].append(value);
+  }
+
+  void SetTelemetryMetadataTimestamp(const char* key, int64_t value) {
+    metadata_["telemetry"][key] = value / 1000.0;
+  }
+
  private:
   void WriteHeader() { fputs("{\"traceEvents\":[\n", output_); }
 
   void WriteFooter() {
-    fputs("]}\n", output_);
+    fputs("],\n\"metadata\":", output_);
+    Json::FastWriter writer;
+    fputs(writer.write(metadata_).c_str(), output_);
+    fputs("\n}", output_);
     fflush(output_);
   }
 
   FILE* output_;
   bool first_event_;
+  Json::Value metadata_;
 };
 
 }  // anonymous namespace
@@ -138,6 +159,57 @@
       return kResultWrongRefType;
     }
   }
+
+  // Add metadata to be written in the footer
+  const auto& trace_metadata = storage->metadata();
+  if (!trace_metadata[metadata::benchmark_description].empty()) {
+    auto desc_id =
+        trace_metadata[metadata::benchmark_description][0].string_value;
+    writer.AppendTelemetryMetadataString("benchmarkDescriptions",
+                                         string_pool.Get(desc_id).c_str());
+  }
+  if (!trace_metadata[metadata::benchmark_name].empty()) {
+    auto name_id = trace_metadata[metadata::benchmark_name][0].string_value;
+    writer.AppendTelemetryMetadataString("benchmarks",
+                                         string_pool.Get(name_id).c_str());
+  }
+  if (!trace_metadata[metadata::benchmark_start_time_us].empty()) {
+    auto start_ts =
+        trace_metadata[metadata::benchmark_start_time_us][0].int_value;
+    writer.SetTelemetryMetadataTimestamp("benchmarkStart", start_ts);
+  }
+  if (!trace_metadata[metadata::benchmark_had_failures].empty()) {
+    auto had_failures =
+        trace_metadata[metadata::benchmark_had_failures][0].int_value;
+    writer.AppendTelemetryMetadataBool("hadFailures", had_failures);
+  }
+  if (!trace_metadata[metadata::benchmark_label].empty()) {
+    auto label_id = trace_metadata[metadata::benchmark_label][0].string_value;
+    writer.AppendTelemetryMetadataString("labels",
+                                         string_pool.Get(label_id).c_str());
+  }
+  if (!trace_metadata[metadata::benchmark_story_name].empty()) {
+    auto name_id =
+        trace_metadata[metadata::benchmark_story_name][0].string_value;
+    writer.AppendTelemetryMetadataString("stories",
+                                         string_pool.Get(name_id).c_str());
+  }
+  if (!trace_metadata[metadata::benchmark_story_run_index].empty()) {
+    auto run_index =
+        trace_metadata[metadata::benchmark_story_run_index][0].int_value;
+    writer.AppendTelemetryMetadataInt("storysetRepeats", run_index);
+  }
+  if (!trace_metadata[metadata::benchmark_story_run_time_us].empty()) {
+    auto story_ts =
+        trace_metadata[metadata::benchmark_story_run_time_us][0].int_value;
+    writer.SetTelemetryMetadataTimestamp("traceStart", story_ts);
+  }
+  for (auto tag : trace_metadata[metadata::benchmark_story_tags]) {
+    auto tag_id = tag.string_value;
+    writer.AppendTelemetryMetadataString("storyTags",
+                                         string_pool.Get(tag_id).c_str());
+  }
+
   return kResultOk;
 }
 
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index 63a0c10..2f6dde5 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -127,6 +127,84 @@
   EXPECT_EQ(code, kResultWrongRefType);
 }
 
+TEST(ExportJsonTest, StorageWithMetadata) {
+  const char* kDescription = "description";
+  const char* kBenchmarkName = "benchmark name";
+  const char* kStoryName = "story name";
+  const char* kStoryTag1 = "tag1";
+  const char* kStoryTag2 = "tag2";
+  const int64_t kBenchmarkStart = 1000000;
+  const int64_t kStoryStart = 2000000;
+  const bool kHadFailures = true;
+
+  TraceStorage storage;
+
+  StringId desc_id = storage.InternString(base::StringView(kDescription));
+  Variadic description = Variadic::String(desc_id);
+  storage.SetMetadata(metadata::benchmark_description, description);
+
+  StringId benchmark_name_id =
+      storage.InternString(base::StringView(kBenchmarkName));
+  Variadic benchmark_name = Variadic::String(benchmark_name_id);
+  storage.SetMetadata(metadata::benchmark_name, benchmark_name);
+
+  StringId story_name_id = storage.InternString(base::StringView(kStoryName));
+  Variadic story_name = Variadic::String(story_name_id);
+  storage.SetMetadata(metadata::benchmark_story_name, story_name);
+
+  StringId tag1_id = storage.InternString(base::StringView(kStoryTag1));
+  StringId tag2_id = storage.InternString(base::StringView(kStoryTag2));
+  Variadic tag1 = Variadic::String(tag1_id);
+  Variadic tag2 = Variadic::String(tag2_id);
+  storage.AppendMetadata(metadata::benchmark_story_tags, tag1);
+  storage.AppendMetadata(metadata::benchmark_story_tags, tag2);
+
+  Variadic benchmark_start = Variadic::Integer(kBenchmarkStart);
+  storage.SetMetadata(metadata::benchmark_start_time_us, benchmark_start);
+
+  Variadic story_start = Variadic::Integer(kStoryStart);
+  storage.SetMetadata(metadata::benchmark_story_run_time_us, story_start);
+
+  Variadic had_failures = Variadic::Integer(kHadFailures);
+  storage.SetMetadata(metadata::benchmark_had_failures, had_failures);
+
+  base::TempFile temp_file = base::TempFile::Create();
+  FILE* output = fopen(temp_file.path().c_str(), "w+");
+  int code = ExportJson(&storage, output);
+
+  EXPECT_EQ(code, kResultOk);
+
+  Json::Reader reader;
+  Json::Value result;
+
+  EXPECT_TRUE(reader.parse(ReadFile(output), result));
+  EXPECT_TRUE(result.isMember("metadata"));
+  EXPECT_TRUE(result["metadata"].isMember("telemetry"));
+  Json::Value telemetry_metadata = result["metadata"]["telemetry"];
+
+  EXPECT_EQ(telemetry_metadata["benchmarkDescriptions"].size(), 1);
+  EXPECT_EQ(telemetry_metadata["benchmarkDescriptions"][0].asString(),
+            kDescription);
+
+  EXPECT_EQ(telemetry_metadata["benchmarks"].size(), 1);
+  EXPECT_EQ(telemetry_metadata["benchmarks"][0].asString(), kBenchmarkName);
+
+  EXPECT_EQ(telemetry_metadata["stories"].size(), 1);
+  EXPECT_EQ(telemetry_metadata["stories"][0].asString(), kStoryName);
+
+  EXPECT_EQ(telemetry_metadata["storyTags"].size(), 2);
+  EXPECT_EQ(telemetry_metadata["storyTags"][0].asString(), kStoryTag1);
+  EXPECT_EQ(telemetry_metadata["storyTags"][1].asString(), kStoryTag2);
+
+  EXPECT_EQ(telemetry_metadata["benchmarkStart"].asInt(),
+            kBenchmarkStart / 1000.0);
+
+  EXPECT_EQ(telemetry_metadata["traceStart"].asInt(), kStoryStart / 1000.0);
+
+  EXPECT_EQ(telemetry_metadata["hadFailures"].size(), 1);
+  EXPECT_EQ(telemetry_metadata["hadFailures"][0].asBool(), kHadFailures);
+}
+
 }  // namespace
 }  // namespace json
 }  // namespace trace_processor
diff --git a/src/trace_processor/proto_trace_parser.cc b/src/trace_processor/proto_trace_parser.cc
index b44e3a7..c3f39d4 100644
--- a/src/trace_processor/proto_trace_parser.cc
+++ b/src/trace_processor/proto_trace_parser.cc
@@ -32,6 +32,7 @@
 #include "src/trace_processor/event_tracker.h"
 #include "src/trace_processor/ftrace_descriptors.h"
 #include "src/trace_processor/heap_profile_tracker.h"
+#include "src/trace_processor/metadata.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/syscall_tracker.h"
@@ -41,6 +42,7 @@
 #include "perfetto/common/android_log_constants.pbzero.h"
 #include "perfetto/common/trace_stats.pbzero.h"
 #include "perfetto/trace/android/android_log.pbzero.h"
+#include "perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h"
 #include "perfetto/trace/clock_snapshot.pbzero.h"
 #include "perfetto/trace/ftrace/ftrace.pbzero.h"
 #include "perfetto/trace/ftrace/ftrace_event.pbzero.h"
@@ -328,6 +330,10 @@
                     packet.track_event());
   }
 
+  if (packet.has_chrome_benchmark_metadata()) {
+    ParseChromeBenchmarkMetadata(packet.chrome_benchmark_metadata());
+  }
+
   // TODO(lalitm): maybe move this to the flush method in the trace processor
   // once we have it. This may reduce performance in the ArgsTracker though so
   // needs to be handled carefully.
@@ -1556,5 +1562,51 @@
   }
 }
 
+void ProtoTraceParser::ParseChromeBenchmarkMetadata(ConstBytes blob) {
+  TraceStorage* storage = context_->storage.get();
+  protos::pbzero::ChromeBenchmarkMetadata::Decoder packet(blob.data, blob.size);
+  if (packet.has_benchmark_name()) {
+    auto benchmark_name_id = storage->InternString(packet.benchmark_name());
+    storage->SetMetadata(metadata::benchmark_name,
+                         Variadic::String(benchmark_name_id));
+  }
+  if (packet.has_benchmark_description()) {
+    auto benchmark_description_id =
+        storage->InternString(packet.benchmark_description());
+    storage->SetMetadata(metadata::benchmark_description,
+                         Variadic::String(benchmark_description_id));
+  }
+  if (packet.has_label()) {
+    auto label_id = storage->InternString(packet.label());
+    storage->SetMetadata(metadata::benchmark_label, Variadic::String(label_id));
+  }
+  if (packet.has_story_name()) {
+    auto story_name_id = storage->InternString(packet.story_name());
+    storage->SetMetadata(metadata::benchmark_story_name,
+                         Variadic::String(story_name_id));
+  }
+  for (auto it = packet.story_tags(); it; ++it) {
+    auto story_tag_id = storage->InternString(it->as_string());
+    storage->AppendMetadata(metadata::benchmark_story_tags,
+                            Variadic::String(story_tag_id));
+  }
+  if (packet.has_benchmark_start_time_us()) {
+    storage->SetMetadata(metadata::benchmark_start_time_us,
+                         Variadic::Integer(packet.benchmark_start_time_us()));
+  }
+  if (packet.has_story_run_time_us()) {
+    storage->SetMetadata(metadata::benchmark_story_run_time_us,
+                         Variadic::Integer(packet.story_run_time_us()));
+  }
+  if (packet.has_story_run_index()) {
+    storage->SetMetadata(metadata::benchmark_story_run_index,
+                         Variadic::Integer(packet.story_run_index()));
+  }
+  if (packet.has_had_failures()) {
+    storage->SetMetadata(metadata::benchmark_had_failures,
+                         Variadic::Integer(packet.had_failures()));
+  }
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/proto_trace_parser.h b/src/trace_processor/proto_trace_parser.h
index c1420ba..3d5251f 100644
--- a/src/trace_processor/proto_trace_parser.h
+++ b/src/trace_processor/proto_trace_parser.h
@@ -114,6 +114,7 @@
                        int64_t tts,
                        ProtoIncrementalState::PacketSequenceState*,
                        ConstBytes);
+  void ParseChromeBenchmarkMetadata(ConstBytes);
 
  private:
   TraceProcessorContext* context_;
@@ -175,7 +176,6 @@
   // Keep kMmEventCounterSize equal to mm_event_type::MM_TYPE_NUM in the kernel.
   static constexpr size_t kMmEventCounterSize = 7;
   std::array<MmEventCounterNames, kMmEventCounterSize> mm_event_counter_names_;
-
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/proto_trace_parser_unittest.cc b/src/trace_processor/proto_trace_parser_unittest.cc
index 05f6dec..a9154e0 100644
--- a/src/trace_processor/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/proto_trace_parser_unittest.cc
@@ -22,12 +22,14 @@
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "src/trace_processor/args_tracker.h"
 #include "src/trace_processor/event_tracker.h"
+#include "src/trace_processor/metadata.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/proto_trace_parser.h"
 #include "src/trace_processor/slice_tracker.h"
 #include "src/trace_processor/trace_sorter.h"
 
 #include "perfetto/common/sys_stats_counters.pbzero.h"
+#include "perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h"
 #include "perfetto/trace/ftrace/ftrace.pbzero.h"
 #include "perfetto/trace/ftrace/ftrace_event.pbzero.h"
 #include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
@@ -102,6 +104,8 @@
   MockTraceStorage() : TraceStorage() {}
 
   MOCK_METHOD1(InternString, StringId(base::StringView));
+  MOCK_METHOD2(SetMetadata, void(size_t, Variadic));
+  MOCK_METHOD2(AppendMetadata, void(size_t, Variadic));
 };
 
 class MockArgsTracker : public ArgsTracker {
@@ -989,6 +993,38 @@
             SystraceParseResult::kUnsupported);
 }
 
+TEST_F(ProtoTraceParserTest, LoadChromeBenchmarkMetadata) {
+  static const char kName[] = "name";
+  static const char kTag2[] = "tag1";
+  static const char kTag1[] = "tag2";
+
+  InitStorage();
+  context_.sorter.reset(new TraceSorter(
+      &context_, std::numeric_limits<int64_t>::max() /*window size*/));
+
+  auto* metadata = trace_.add_packet()->set_chrome_benchmark_metadata();
+  metadata->set_benchmark_name(kName);
+  metadata->add_story_tags(kTag1);
+  metadata->add_story_tags(kTag2);
+
+  Tokenize();
+
+  EXPECT_CALL(*storage_, InternString(base::StringView(kName)))
+      .WillOnce(Return(1));
+  EXPECT_CALL(*storage_, InternString(base::StringView(kTag1)))
+      .WillOnce(Return(2));
+  EXPECT_CALL(*storage_, InternString(base::StringView(kTag2)))
+      .WillOnce(Return(3));
+  EXPECT_CALL(*storage_,
+              SetMetadata(metadata::benchmark_name, Variadic::String(1)));
+  EXPECT_CALL(*storage_, AppendMetadata(metadata::benchmark_story_tags,
+                                        Variadic::String(2)));
+  EXPECT_CALL(*storage_, AppendMetadata(metadata::benchmark_story_tags,
+                                        Variadic::String(3)));
+
+  context_.sorter->ExtractEventsForced();
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index fc26e66..37bcfe8 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -762,7 +762,8 @@
   // Example usage:
   // SetMetadata(metadata::benchmark_name,
   //             Variadic::String(storage->InternString("foo"));
-  void SetMetadata(size_t key, Variadic value) {
+  // Virtual for testing.
+  virtual void SetMetadata(size_t key, Variadic value) {
     PERFETTO_DCHECK(key < metadata::kNumKeys);
     PERFETTO_DCHECK(metadata::kKeyTypes[key] == metadata::kSingle);
     PERFETTO_DCHECK(value.type == metadata::kValueTypes[key]);
@@ -772,7 +773,8 @@
   // Example usage:
   // AppendMetadata(metadata::benchmark_story_tags,
   //                Variadic::String(storage->InternString("bar"));
-  void AppendMetadata(size_t key, Variadic value) {
+  // Virtual for testing.
+  virtual void AppendMetadata(size_t key, Variadic value) {
     PERFETTO_DCHECK(key < metadata::kNumKeys);
     PERFETTO_DCHECK(metadata::kKeyTypes[key] == metadata::kMulti);
     PERFETTO_DCHECK(value.type == metadata::kValueTypes[key]);
diff --git a/src/trace_processor/variadic.h b/src/trace_processor/variadic.h
index cfae4fc..761aea3 100644
--- a/src/trace_processor/variadic.h
+++ b/src/trace_processor/variadic.h
@@ -47,6 +47,21 @@
     return variadic;
   }
 
+  // Used in tests.
+  bool operator==(const Variadic& other) const {
+    if (type == other.type) {
+      switch (type) {
+        case kInt:
+          return int_value == other.int_value;
+        case kString:
+          return string_value == other.string_value;
+        case kReal:
+          return std::equal_to<double>()(real_value, other.real_value);
+      }
+    }
+    return false;
+  }
+
   Type type;
   union {
     int64_t int_value;
diff --git a/tools/traceconv b/tools/traceconv
index aabae93..a2449b4 100755
--- a/tools/traceconv
+++ b/tools/traceconv
@@ -33,8 +33,8 @@
 # Keep this in sync with the SHAs in catapult file
 # systrace/systrace/tracing_agents/atrace_from_file_agent.py.
 TRACE_TO_TEXT_SHAS = {
-  'linux': '70d456c4b6c01db5b06a04bc8d2275cee561516a',
-  'mac': 'd01e5e8ce392aa02026a7a9812839e5a7d279b0b',
+  'linux': 'ec020c3424ea32d8a79372b40567b528df3cd902',
+  'mac': '66df2dc8c52c2c93569aa34d7997cdcd7bd09e3b',
 }
 TRACE_TO_TEXT_PATH = tempfile.gettempdir()
 TRACE_TO_TEXT_BASE_URL = (