processor: Export LegacyEvent raw events as JSON
Adds support for exporting raw events with "track_event.legacy_event"
name to the json exporter. We use these raw events for pass-through of
legacy event types not natively supported in the trace processor
tables.
Bug: 130786269
Change-Id: I5c6018c8245703b4804d3b1e6f09fc6897e21212
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index ba505c3..2e7c279 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -19,6 +19,7 @@
#include <json/value.h>
#include <json/writer.h>
#include <stdio.h>
+#include <cstring>
#include "perfetto/ext/base/string_splitter.h"
#include "src/trace_processor/export_json.h"
@@ -120,8 +121,7 @@
class ArgsBuilder {
public:
- explicit ArgsBuilder(const TraceStorage* storage)
- : string_pool_(&storage->string_pool()) {
+ explicit ArgsBuilder(const TraceStorage* storage) : storage_(storage) {
const TraceStorage::Args& args = storage->args();
Json::Value empty_value(Json::objectValue);
if (args.args_count() == 0) {
@@ -131,7 +131,7 @@
args_sets_.resize(args.set_ids().back() + 1, empty_value);
for (size_t i = 0; i < args.args_count(); ++i) {
ArgSetId set_id = args.set_ids()[i];
- const char* key = string_pool_->Get(args.keys()[i]).c_str();
+ const char* key = storage_->GetString(args.keys()[i]).c_str();
Variadic value = args.arg_values()[i];
AppendArg(set_id, key, VariadicToJson(value));
}
@@ -150,7 +150,7 @@
case Variadic::kUint:
return Json::UInt64(variadic.uint_value);
case Variadic::kString:
- return string_pool_->Get(variadic.string_value).c_str();
+ return storage_->GetString(variadic.string_value).c_str();
case Variadic::kReal:
return variadic.real_value;
case Variadic::kPointer:
@@ -160,7 +160,8 @@
case Variadic::kJson:
Json::Reader reader;
Json::Value result;
- reader.parse(string_pool_->Get(variadic.string_value).c_str(), result);
+ reader.parse(storage_->GetString(variadic.string_value).c_str(),
+ result);
return result;
}
PERFETTO_FATAL("Not reached"); // For gcc.
@@ -218,17 +219,16 @@
}
}
- const StringPool* string_pool_;
+ const TraceStorage* storage_;
std::vector<Json::Value> args_sets_;
};
ResultCode ExportThreadNames(const TraceStorage* storage,
TraceFormatWriter* writer) {
- const StringPool& string_pool = storage->string_pool();
for (UniqueTid i = 1; i < storage->thread_count(); ++i) {
auto thread = storage->GetThread(i);
if (thread.name_id > 0) {
- const char* thread_name = string_pool.Get(thread.name_id).c_str();
+ const char* thread_name = storage->GetString(thread.name_id).c_str();
uint32_t pid = thread.upid ? storage->GetProcess(*thread.upid).pid : 0;
writer->WriteMetadataEvent("thread_name", thread_name, thread.tid, pid);
}
@@ -238,11 +238,10 @@
ResultCode ExportProcessNames(const TraceStorage* storage,
TraceFormatWriter* writer) {
- const StringPool& string_pool = storage->string_pool();
for (UniquePid i = 1; i < storage->process_count(); ++i) {
auto process = storage->GetProcess(i);
if (process.name_id > 0) {
- const char* process_name = string_pool.Get(process.name_id).c_str();
+ const char* process_name = storage->GetString(process.name_id).c_str();
writer->WriteMetadataEvent("process_name", process_name, 0, process.pid);
}
}
@@ -250,17 +249,16 @@
}
ResultCode ExportSlices(const TraceStorage* storage,
+ const ArgsBuilder& args_builder,
TraceFormatWriter* writer) {
- const StringPool& string_pool = storage->string_pool();
- ArgsBuilder args_builder(storage);
const auto& slices = storage->nestable_slices();
for (uint32_t i = 0; i < slices.slice_count(); ++i) {
Json::Value event;
event["ts"] = Json::Int64(slices.start_ns()[i] / 1000);
- event["cat"] = string_pool.Get(slices.categories()[i]).c_str();
- event["name"] = string_pool.Get(slices.names()[i]).c_str();
+ event["cat"] = storage->GetString(slices.categories()[i]).c_str();
+ event["name"] = storage->GetString(slices.names()[i]).c_str();
event["pid"] = 0;
- Json::Value args = args_builder.GetArgs(slices.arg_set_ids()[i]);
+ const Json::Value& args = args_builder.GetArgs(slices.arg_set_ids()[i]);
if (!args.empty()) {
event["args"] = args;
}
@@ -416,9 +414,138 @@
return kResultOk;
}
+ResultCode ExportRawEvents(const TraceStorage* storage,
+ const ArgsBuilder& args_builder,
+ TraceFormatWriter* writer) {
+ base::Optional<StringId> raw_legacy_event_key_id =
+ storage->string_pool().GetId("track_event.legacy_event");
+ if (!raw_legacy_event_key_id)
+ return kResultOk;
+
+ const char kLegacyEventArgsKey[] = "legacy_event";
+ const char kLegacyEventCategoryKey[] = "category";
+ const char kLegacyEventNameKey[] = "name";
+ const char kLegacyEventPhaseKey[] = "phase";
+ const char kLegacyEventDurationNsKey[] = "duration_ns";
+ const char kLegacyEventThreadTimestampNsKey[] = "thread_timestamp_ns";
+ const char kLegacyEventThreadDurationNsKey[] = "thread_duration_ns";
+ const char kLegacyEventThreadInstructionCountKey[] =
+ "thread_instruction_count";
+ const char kLegacyEventThreadInstructionDeltaKey[] =
+ "thread_instruction_delta";
+ const char kLegacyEventUseAsyncTtsKey[] = "use_async_tts";
+ const char kLegacyEventGlobalIdKey[] = "global_id";
+ const char kLegacyEventLocalIdKey[] = "local_id";
+ const char kLegacyEventIdScopeKey[] = "id_scope";
+ const char kLegacyEventBindIdKey[] = "bind_id";
+ const char kLegacyEventBindToEnclosingKey[] = "bind_to_enclosing";
+ const char kLegacyEventFlowDirectionKey[] = "flow_direction";
+ const char kFlowDirectionValueIn[] = "in";
+ const char kFlowDirectionValueOut[] = "out";
+ const char kFlowDirectionValueInout[] = "inout";
+
+ const auto& events = storage->raw_events();
+ for (uint32_t i = 0; i < events.raw_event_count(); ++i) {
+ if (events.name_ids()[i] != *raw_legacy_event_key_id)
+ continue;
+
+ Json::Value event;
+ event["ts"] = Json::Int64(events.timestamps()[i] / 1000);
+
+ UniqueTid utid = static_cast<UniqueTid>(events.utids()[i]);
+ auto thread = storage->GetThread(utid);
+ event["tid"] = thread.tid;
+ if (thread.upid) {
+ event["pid"] = storage->GetProcess(*thread.upid).pid;
+ }
+
+ // Raw legacy events store all other params in the arg set. Make a copy of
+ // the converted args here and remove these params.
+ Json::Value args = args_builder.GetArgs(events.arg_set_ids()[i]);
+ Json::Value legacy_args = args.removeMember(kLegacyEventArgsKey);
+
+ PERFETTO_DCHECK(legacy_args.isMember(kLegacyEventCategoryKey));
+ event["cat"] = legacy_args[kLegacyEventCategoryKey];
+
+ PERFETTO_DCHECK(legacy_args.isMember(kLegacyEventNameKey));
+ event["name"] = legacy_args[kLegacyEventNameKey];
+
+ PERFETTO_DCHECK(legacy_args.isMember(kLegacyEventPhaseKey));
+ event["ph"] = legacy_args[kLegacyEventPhaseKey];
+
+ if (legacy_args.isMember(kLegacyEventDurationNsKey)) {
+ event["dur"] = legacy_args[kLegacyEventDurationNsKey].asInt64() / 1000;
+ }
+
+ if (legacy_args.isMember(kLegacyEventThreadTimestampNsKey)) {
+ event["tts"] =
+ legacy_args[kLegacyEventThreadTimestampNsKey].asInt64() / 1000;
+ }
+
+ if (legacy_args.isMember(kLegacyEventThreadDurationNsKey)) {
+ event["tdur"] =
+ legacy_args[kLegacyEventThreadDurationNsKey].asInt64() / 1000;
+ }
+
+ if (legacy_args.isMember(kLegacyEventThreadInstructionCountKey)) {
+ event["ticount"] = legacy_args[kLegacyEventThreadInstructionCountKey];
+ }
+
+ if (legacy_args.isMember(kLegacyEventThreadInstructionDeltaKey)) {
+ event["tidelta"] = legacy_args[kLegacyEventThreadInstructionDeltaKey];
+ }
+
+ if (legacy_args.isMember(kLegacyEventUseAsyncTtsKey)) {
+ event["use_async_tts"] = legacy_args[kLegacyEventUseAsyncTtsKey];
+ }
+
+ if (legacy_args.isMember(kLegacyEventGlobalIdKey)) {
+ event["id2"]["global"] =
+ PrintUint64(legacy_args[kLegacyEventGlobalIdKey].asUInt64());
+ }
+
+ if (legacy_args.isMember(kLegacyEventLocalIdKey)) {
+ event["id2"]["local"] =
+ PrintUint64(legacy_args[kLegacyEventLocalIdKey].asUInt64());
+ }
+
+ if (legacy_args.isMember(kLegacyEventIdScopeKey)) {
+ event["scope"] = legacy_args[kLegacyEventIdScopeKey];
+ }
+
+ if (legacy_args.isMember(kLegacyEventBindIdKey)) {
+ event["bind_id"] =
+ PrintUint64(legacy_args[kLegacyEventBindIdKey].asUInt64());
+ }
+
+ if (legacy_args.isMember(kLegacyEventBindToEnclosingKey)) {
+ event["bp"] = "e";
+ }
+
+ if (legacy_args.isMember(kLegacyEventFlowDirectionKey)) {
+ const char* val = legacy_args[kLegacyEventFlowDirectionKey].asCString();
+ if (strcmp(val, kFlowDirectionValueIn) == 0) {
+ event["flow_in"] = true;
+ } else if (strcmp(val, kFlowDirectionValueOut) == 0) {
+ event["flow_out"] = true;
+ } else {
+ PERFETTO_DCHECK(strcmp(val, kFlowDirectionValueInout) == 0);
+ event["flow_in"] = true;
+ event["flow_out"] = true;
+ }
+ }
+
+ if (!args.empty()) {
+ event["args"] = args;
+ }
+
+ writer->WriteCommonEvent(event);
+ }
+ return kResultOk;
+}
+
ResultCode ExportMetadata(const TraceStorage* storage,
TraceFormatWriter* writer) {
- const StringPool& string_pool = storage->string_pool();
const auto& trace_metadata = storage->metadata();
const auto& keys = trace_metadata.keys();
const auto& values = trace_metadata.values();
@@ -429,12 +556,12 @@
case metadata::benchmark_description:
writer->AppendTelemetryMetadataString(
"benchmarkDescriptions",
- string_pool.Get(values[pos].string_value).c_str());
+ storage->GetString(values[pos].string_value).c_str());
break;
case metadata::benchmark_name:
writer->AppendTelemetryMetadataString(
- "benchmarks", string_pool.Get(values[pos].string_value).c_str());
+ "benchmarks", storage->GetString(values[pos].string_value).c_str());
break;
case metadata::benchmark_start_time_us:
@@ -451,12 +578,12 @@
case metadata::benchmark_label:
writer->AppendTelemetryMetadataString(
- "labels", string_pool.Get(values[pos].string_value).c_str());
+ "labels", storage->GetString(values[pos].string_value).c_str());
break;
case metadata::benchmark_story_name:
writer->AppendTelemetryMetadataString(
- "stories", string_pool.Get(values[pos].string_value).c_str());
+ "stories", storage->GetString(values[pos].string_value).c_str());
break;
case metadata::benchmark_story_run_index:
@@ -471,7 +598,7 @@
case metadata::benchmark_story_tags: // repeated
writer->AppendTelemetryMetadataString(
- "storyTags", string_pool.Get(values[pos].string_value).c_str());
+ "storyTags", storage->GetString(values[pos].string_value).c_str());
break;
default:
@@ -486,6 +613,7 @@
ResultCode ExportJson(const TraceStorage* storage, FILE* output) {
TraceFormatWriter writer(output);
+ ArgsBuilder args_builder(storage);
ResultCode code = ExportThreadNames(storage, &writer);
if (code != kResultOk)
@@ -495,7 +623,11 @@
if (code != kResultOk)
return code;
- code = ExportSlices(storage, &writer);
+ code = ExportSlices(storage, args_builder, &writer);
+ if (code != kResultOk)
+ return code;
+
+ code = ExportRawEvents(storage, args_builder, &writer);
if (code != kResultOk)
return code;
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index fef2f3f..8e5a289 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -17,6 +17,8 @@
#include "src/trace_processor/export_json.h"
#include "perfetto/ext/base/temp_file.h"
+#include "src/trace_processor/args_tracker.h"
+#include "src/trace_processor/trace_processor_context.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -761,6 +763,104 @@
EXPECT_EQ(event["args"][kArgName].asInt(), kArgValue);
}
+TEST(ExportJsonTest, RawEvent) {
+ const int64_t kTimestamp = 10000000;
+ const int64_t kDuration = 10000;
+ const int64_t kThreadTimestamp = 20000000;
+ const int64_t kThreadDuration = 20000;
+ const int64_t kThreadInstructionCount = 30000000;
+ const int64_t kThreadInstructionDelta = 30000;
+ const int64_t kProcessID = 100;
+ const int64_t kThreadID = 200;
+ const char* kCategory = "cat";
+ const char* kName = "name";
+ const char* kPhase = "?";
+ const uint64_t kGlobalId = 0xaaffaaffaaffaaff;
+ const char* kIdScope = "my_id";
+ const uint64_t kBindId = 0xaa00aa00aa00aa00;
+ const char* kFlowDirection = "inout";
+ const char* kArgName = "arg_name";
+ const int kArgValue = 123;
+
+ TraceProcessorContext context;
+ context.storage.reset(new TraceStorage());
+ TraceStorage* storage = context.storage.get();
+
+ UniquePid upid = storage->AddEmptyProcess(kProcessID);
+ UniqueTid utid = storage->AddEmptyThread(kThreadID);
+ storage->GetMutableThread(utid)->upid = upid;
+
+ RowId row_id = storage->mutable_raw_events()->AddRawEvent(
+ kTimestamp, storage->InternString("track_event.legacy_event"), /*cpu=*/0,
+ utid);
+
+ ArgsTracker args(&context);
+ auto add_arg = [&](const char* key, Variadic value) {
+ StringId key_id = storage->InternString(key);
+ args.AddArg(row_id, key_id, key_id, value);
+ };
+
+ StringId cat_id = storage->InternString(base::StringView(kCategory));
+ add_arg("legacy_event.category", Variadic::String(cat_id));
+ StringId name_id = storage->InternString(base::StringView(kName));
+ add_arg("legacy_event.name", Variadic::String(name_id));
+ StringId phase_id = storage->InternString(base::StringView(kPhase));
+ add_arg("legacy_event.phase", Variadic::String(phase_id));
+
+ add_arg("legacy_event.duration_ns", Variadic::Integer(kDuration));
+ add_arg("legacy_event.thread_timestamp_ns",
+ Variadic::Integer(kThreadTimestamp));
+ add_arg("legacy_event.thread_duration_ns",
+ Variadic::Integer(kThreadDuration));
+ add_arg("legacy_event.thread_instruction_count",
+ Variadic::Integer(kThreadInstructionCount));
+ add_arg("legacy_event.thread_instruction_delta",
+ Variadic::Integer(kThreadInstructionDelta));
+ add_arg("legacy_event.use_async_tts", Variadic::Boolean(true));
+ add_arg("legacy_event.global_id", Variadic::UnsignedInteger(kGlobalId));
+ StringId scope_id = storage->InternString(base::StringView(kIdScope));
+ add_arg("legacy_event.id_scope", Variadic::String(scope_id));
+ add_arg("legacy_event.bind_id", Variadic::UnsignedInteger(kBindId));
+ add_arg("legacy_event.bind_to_enclosing", Variadic::Boolean(true));
+ StringId flow_direction_id = storage->InternString(kFlowDirection);
+ add_arg("legacy_event.flow_direction", Variadic::String(flow_direction_id));
+
+ add_arg(kArgName, Variadic::Integer(kArgValue));
+
+ args.Flush();
+
+ 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_EQ(result["traceEvents"].size(), 1u);
+
+ Json::Value event = result["traceEvents"][0];
+ EXPECT_EQ(event["ph"].asString(), kPhase);
+ EXPECT_EQ(event["ts"].asInt64(), kTimestamp / 1000);
+ EXPECT_EQ(event["dur"].asInt64(), kDuration / 1000);
+ EXPECT_EQ(event["tts"].asInt64(), kThreadTimestamp / 1000);
+ EXPECT_EQ(event["tdur"].asInt64(), kThreadDuration / 1000);
+ EXPECT_EQ(event["ticount"].asInt64(), kThreadInstructionCount);
+ EXPECT_EQ(event["tidelta"].asInt64(), kThreadInstructionDelta);
+ EXPECT_EQ(event["tid"].asUInt(), kThreadID);
+ EXPECT_EQ(event["cat"].asString(), kCategory);
+ EXPECT_EQ(event["name"].asString(), kName);
+ EXPECT_EQ(event["use_async_tts"].asInt(), 1);
+ EXPECT_EQ(event["id2"]["global"].asString(), "0xaaffaaffaaffaaff");
+ EXPECT_EQ(event["scope"].asString(), kIdScope);
+ EXPECT_EQ(event["bind_id"].asString(), "0xaa00aa00aa00aa00");
+ EXPECT_EQ(event["bp"].asString(), "e");
+ EXPECT_EQ(event["flow_in"].asBool(), true);
+ EXPECT_EQ(event["flow_out"].asBool(), true);
+ EXPECT_EQ(event["args"][kArgName].asInt(), kArgValue);
+}
+
} // 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 cf00dee..2b7d217 100644
--- a/src/trace_processor/proto_trace_parser.cc
+++ b/src/trace_processor/proto_trace_parser.cc
@@ -1875,8 +1875,11 @@
legacy_event_category_key_id_, Variadic::String(category_id));
args.AddArg(row_id, legacy_event_name_key_id_, legacy_event_name_key_id_,
Variadic::String(name_id));
+
+ std::string phase_string(1, static_cast<char>(legacy_event.phase()));
+ StringId phase_id = context_->storage->InternString(phase_string.c_str());
args.AddArg(row_id, legacy_event_phase_key_id_, legacy_event_phase_key_id_,
- Variadic::Integer(legacy_event.phase()));
+ Variadic::String(phase_id));
if (legacy_event.has_duration_us()) {
args.AddArg(row_id, legacy_event_duration_ns_key_id_,
@@ -1971,6 +1974,9 @@
legacy_event_flow_direction_key_id_, Variadic::String(value));
}
+ // No need to parse legacy_event.instant_event_scope() because we import
+ // instant events into the slice table.
+
args_callback(&args, row_id);
}
diff --git a/src/trace_processor/proto_trace_parser_unittest.cc b/src/trace_processor/proto_trace_parser_unittest.cc
index 7f83415..c67e98a 100644
--- a/src/trace_processor/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/proto_trace_parser_unittest.cc
@@ -1613,8 +1613,6 @@
EXPECT_CALL(*process_, UpdateThread(16, 15)).WillOnce(Return(1));
- InSequence in_sequence; // Below slices should be sorted by timestamp.
-
EXPECT_CALL(*storage_, InternString(base::StringView("cat1")))
.WillOnce(Return(1));
EXPECT_CALL(*storage_, InternString(base::StringView("ev1")))
@@ -1622,9 +1620,10 @@
EXPECT_CALL(*storage_, InternString(base::StringView("scope1")))
.Times(2)
.WillRepeatedly(Return(3));
-
- EXPECT_CALL(*storage_, InternString(base::StringView("debug.an1")))
+ EXPECT_CALL(*storage_, InternString(base::StringView("?")))
.WillOnce(Return(4));
+ EXPECT_CALL(*storage_, InternString(base::StringView("debug.an1")))
+ .WillOnce(Return(5));
context_.sorter->ExtractEventsForced();
@@ -1647,7 +1646,7 @@
EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.name"),
Variadic::String(2u)));
EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.phase"),
- Variadic::Integer('?')));
+ Variadic::String(4u)));
EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.duration_ns"),
Variadic::Integer(23000)));
EXPECT_TRUE(HasArg(1u,
@@ -1669,7 +1668,7 @@
Variadic::Boolean(true)));
EXPECT_TRUE(HasArg(1u, storage_->InternString("legacy_event.flow_direction"),
Variadic::String(storage_->InternString("inout"))));
- EXPECT_TRUE(HasArg(1u, 4u, Variadic::UnsignedInteger(10u)));
+ EXPECT_TRUE(HasArg(1u, 5u, Variadic::UnsignedInteger(10u)));
}
TEST_F(ProtoTraceParserTest, LoadChromeBenchmarkMetadata) {
diff --git a/src/trace_processor/string_pool.h b/src/trace_processor/string_pool.h
index f5acdc7..5d1a295 100644
--- a/src/trace_processor/string_pool.h
+++ b/src/trace_processor/string_pool.h
@@ -17,6 +17,7 @@
#ifndef SRC_TRACE_PROCESSOR_STRING_POOL_H_
#define SRC_TRACE_PROCESSOR_STRING_POOL_H_
+#include "perfetto/ext/base/optional.h"
#include "perfetto/ext/base/paged_memory.h"
#include "src/trace_processor/null_term_string_view.h"
@@ -77,6 +78,19 @@
return InsertString(str, hash);
}
+ base::Optional<Id> GetId(base::StringView str) const {
+ if (str.data() == nullptr)
+ return 0u;
+
+ auto hash = str.Hash();
+ auto id_it = string_index_.find(hash);
+ if (id_it != string_index_.end()) {
+ PERFETTO_DCHECK(Get(id_it->second) == str);
+ return id_it->second;
+ }
+ return base::nullopt;
+ }
+
NullTermStringView Get(Id id) const {
if (id == 0)
return NullTermStringView();