Add SQL tables for new v8 data source
Initial parsing code mostly to deal with interned data.
Bug: b/283794416
Change-Id: I3765a5eb044469a833ad4b7d20fea513f99a52eb
diff --git a/Android.bp b/Android.bp
index 25225b3..4ed7036 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11230,6 +11230,9 @@
"src/trace_processor/importers/proto/system_probes_module.cc",
"src/trace_processor/importers/proto/system_probes_parser.cc",
"src/trace_processor/importers/proto/translation_table_module.cc",
+ "src/trace_processor/importers/proto/v8_module.cc",
+ "src/trace_processor/importers/proto/v8_sequence_state.cc",
+ "src/trace_processor/importers/proto/v8_tracker.cc",
"src/trace_processor/importers/proto/vulkan_memory_tracker.cc",
],
}
@@ -11839,6 +11842,7 @@
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
+ "src/trace_processor/tables/v8_tables.py",
"src/trace_processor/tables/winscope_tables.py",
"tools/gen_tp_table_headers.py",
],
@@ -12083,6 +12087,7 @@
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
+ "src/trace_processor/tables/v8_tables.py",
"src/trace_processor/tables/winscope_tables.py",
],
tools: [
@@ -12100,6 +12105,7 @@
"src/trace_processor/tables/slice_tables_py.h",
"src/trace_processor/tables/trace_proto_tables_py.h",
"src/trace_processor/tables/track_tables_py.h",
+ "src/trace_processor/tables/v8_tables_py.h",
"src/trace_processor/tables/winscope_tables_py.h",
],
}
@@ -12121,6 +12127,7 @@
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
+ "src/trace_processor/tables/v8_tables.py",
"src/trace_processor/tables/winscope_tables.py",
"tools/gen_tp_table_headers.py",
],
diff --git a/BUILD b/BUILD
index 7737bec..c8e521b 100644
--- a/BUILD
+++ b/BUILD
@@ -1639,6 +1639,12 @@
"src/trace_processor/importers/proto/system_probes_parser.h",
"src/trace_processor/importers/proto/translation_table_module.cc",
"src/trace_processor/importers/proto/translation_table_module.h",
+ "src/trace_processor/importers/proto/v8_module.cc",
+ "src/trace_processor/importers/proto/v8_module.h",
+ "src/trace_processor/importers/proto/v8_sequence_state.cc",
+ "src/trace_processor/importers/proto/v8_sequence_state.h",
+ "src/trace_processor/importers/proto/v8_tracker.cc",
+ "src/trace_processor/importers/proto/v8_tracker.h",
"src/trace_processor/importers/proto/vulkan_memory_tracker.cc",
"src/trace_processor/importers/proto/vulkan_memory_tracker.h",
],
@@ -2458,6 +2464,7 @@
"src/trace_processor/tables/slice_tables.py",
"src/trace_processor/tables/trace_proto_tables.py",
"src/trace_processor/tables/track_tables.py",
+ "src/trace_processor/tables/v8_tables.py",
"src/trace_processor/tables/winscope_tables.py",
],
outs = [
@@ -2471,6 +2478,7 @@
"src/trace_processor/tables/slice_tables_py.h",
"src/trace_processor/tables/trace_proto_tables_py.h",
"src/trace_processor/tables/track_tables_py.h",
+ "src/trace_processor/tables/v8_tables_py.h",
"src/trace_processor/tables/winscope_tables_py.h",
],
)
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index f5808fd..7f8d3e0 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -73,7 +73,6 @@
"../../../../protos/perfetto/common:zero",
"../../../../protos/perfetto/config:zero",
"../../../../protos/perfetto/trace:zero",
- "../../../../protos/perfetto/trace:zero",
"../../../../protos/perfetto/trace/android:zero",
"../../../../protos/perfetto/trace/chrome:zero",
"../../../../protos/perfetto/trace/ftrace:zero",
@@ -141,6 +140,12 @@
"system_probes_parser.h",
"translation_table_module.cc",
"translation_table_module.h",
+ "v8_module.cc",
+ "v8_module.h",
+ "v8_sequence_state.cc",
+ "v8_sequence_state.h",
+ "v8_tracker.cc",
+ "v8_tracker.h",
"vulkan_memory_tracker.cc",
"vulkan_memory_tracker.h",
]
@@ -153,15 +158,13 @@
"../../../../include/perfetto/ext/traced:sys_stats_counters",
"../../../../protos/perfetto/common:zero",
"../../../../protos/perfetto/config:zero",
- "../../../../protos/perfetto/config:zero",
- "../../../../protos/perfetto/trace:zero",
"../../../../protos/perfetto/trace:zero",
"../../../../protos/perfetto/trace/android:zero",
+ "../../../../protos/perfetto/trace/chrome:zero",
"../../../../protos/perfetto/trace/gpu:zero",
"../../../../protos/perfetto/trace/interned_data:zero",
"../../../../protos/perfetto/trace/power:zero",
"../../../../protos/perfetto/trace/profiling:zero",
- "../../../../protos/perfetto/trace/profiling:zero",
"../../../../protos/perfetto/trace/ps:zero",
"../../../../protos/perfetto/trace/statsd:zero",
"../../../../protos/perfetto/trace/sys_stats:zero",
diff --git a/src/trace_processor/importers/proto/additional_modules.cc b/src/trace_processor/importers/proto/additional_modules.cc
index 2d3e95f..341d6d7 100644
--- a/src/trace_processor/importers/proto/additional_modules.cc
+++ b/src/trace_processor/importers/proto/additional_modules.cc
@@ -25,6 +25,7 @@
#include "src/trace_processor/importers/proto/statsd_module.h"
#include "src/trace_processor/importers/proto/system_probes_module.h"
#include "src/trace_processor/importers/proto/translation_table_module.h"
+#include "src/trace_processor/importers/proto/v8_module.h"
#include "src/trace_processor/importers/proto/winscope/winscope_module.h"
namespace perfetto {
@@ -40,6 +41,7 @@
context->modules.emplace_back(new StatsdModule(context));
context->modules.emplace_back(new AndroidCameraEventModule(context));
context->modules.emplace_back(new MetadataModule(context));
+ context->modules.emplace_back(new V8Module(context));
context->modules.emplace_back(new WinscopeModule(context));
// Ftrace module is special, because it has one extra method for parsing
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.h b/src/trace_processor/importers/proto/packet_sequence_state.h
index d95fccc..8c29222 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state.h
@@ -34,6 +34,17 @@
class PacketSequenceState {
public:
+ // Helper to keep per sequence state. These are not reset when the generation
+ // changes.
+ // Trackers or parsers can add their custom per sequence state here instead of
+ // keeping a map from seq_id to some internal state.
+ // TODO(carlscab): We should come up with a nicer API that allows extensions
+ // to be notified of generation changes.
+ // TODO(carlscab): There is some existing code that could use this. Migrate.
+ struct ExtensibleSequenceState {
+ std::unique_ptr<Destructible> v8_sequence_state;
+ };
+
explicit PacketSequenceState(TraceProcessorContext* context)
: context_(context), sequence_stack_profile_tracker_(context) {
current_generation_.reset(
@@ -112,6 +123,10 @@
return sequence_stack_profile_tracker_;
}
+ ExtensibleSequenceState& extensible_sequence_state() {
+ return extensible_sequence_state_;
+ }
+
// Returns a ref-counted ptr to the current generation.
RefPtr<PacketSequenceStateGeneration> current_generation() const {
return current_generation_;
@@ -160,6 +175,7 @@
RefPtr<PacketSequenceStateGeneration> current_generation_;
SequenceStackProfileTracker sequence_stack_profile_tracker_;
+ ExtensibleSequenceState extensible_sequence_state_;
};
template <uint32_t FieldId, typename MessageType>
diff --git a/src/trace_processor/importers/proto/v8_module.cc b/src/trace_processor/importers/proto/v8_module.cc
new file mode 100644
index 0000000..ac2086a
--- /dev/null
+++ b/src/trace_processor/importers/proto/v8_module.cc
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2024 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 "src/trace_processor/importers/proto/v8_module.h"
+
+#include <optional>
+
+#include "protos/perfetto/trace/chrome/v8.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/v8_sequence_state.h"
+#include "src/trace_processor/importers/proto/v8_tracker.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/v8_tables_py.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using ::perfetto::protos::pbzero::TracePacket;
+using ::perfetto::protos::pbzero::V8CodeMove;
+using ::perfetto::protos::pbzero::V8InternalCode;
+using ::perfetto::protos::pbzero::V8JsCode;
+using ::perfetto::protos::pbzero::V8RegExpCode;
+using ::perfetto::protos::pbzero::V8WasmCode;
+
+} // namespace
+
+V8Module::V8Module(TraceProcessorContext* context)
+ : context_(context), v8_tracker_(V8Tracker::GetOrCreate(context_)) {
+ RegisterForField(TracePacket::kV8JsCodeFieldNumber, context_);
+ RegisterForField(TracePacket::kV8InternalCodeFieldNumber, context_);
+ RegisterForField(TracePacket::kV8WasmCodeFieldNumber, context_);
+ RegisterForField(TracePacket::kV8RegExpCodeFieldNumber, context_);
+ RegisterForField(TracePacket::kV8CodeMoveFieldNumber, context_);
+}
+
+V8Module::~V8Module() = default;
+
+ModuleResult V8Module::TokenizePacket(const TracePacket::Decoder&,
+ TraceBlobView* /*packet*/,
+ int64_t /*packet_timestamp*/,
+ PacketSequenceState* /*state*/,
+ uint32_t /*field_id*/) {
+ return ModuleResult::Ignored();
+}
+
+void V8Module::ParseTracePacketData(const TracePacket::Decoder& decoder,
+ int64_t ts,
+ const TracePacketData& data,
+ uint32_t field_id) {
+ switch (field_id) {
+ case TracePacket::kV8JsCodeFieldNumber:
+ ParseV8JsCode(decoder.v8_js_code(), ts, data);
+ break;
+ case TracePacket::kV8InternalCodeFieldNumber:
+ ParseV8InternalCode(decoder.v8_internal_code(), ts, data);
+ break;
+ case TracePacket::kV8WasmCodeFieldNumber:
+ ParseV8WasmCode(decoder.v8_wasm_code(), ts, data);
+ break;
+ case TracePacket::kV8RegExpCodeFieldNumber:
+ ParseV8RegExpCode(decoder.v8_reg_exp_code(), ts, data);
+ break;
+ case TracePacket::kV8CodeMoveFieldNumber:
+ ParseV8CodeMove(decoder.v8_code_move(), ts, data);
+ break;
+ default:
+ break;
+ }
+}
+
+void V8Module::ParseV8JsCode(protozero::ConstBytes bytes,
+ int64_t ts,
+ const TracePacketData& data) {
+ V8SequenceState& state =
+ *V8SequenceState::GetOrCreate(data.sequence_state->state());
+
+ V8JsCode::Decoder code(bytes);
+
+ auto v8_isolate_id = state.GetOrInsertIsolate(code.v8_isolate_iid());
+ if (!v8_isolate_id) {
+ return;
+ }
+
+ auto v8_function_id =
+ state.GetOrInsertJsFunction(code.v8_js_function_iid(), *v8_isolate_id);
+ if (!v8_function_id) {
+ return;
+ }
+
+ v8_tracker_->AddJsCode(ts, *v8_isolate_id, *v8_function_id, code);
+}
+
+void V8Module::ParseV8InternalCode(protozero::ConstBytes bytes,
+ int64_t ts,
+ const TracePacketData& data) {
+ V8SequenceState& state =
+ *V8SequenceState::GetOrCreate(data.sequence_state->state());
+
+ V8InternalCode::Decoder code(bytes);
+
+ auto v8_isolate_id = state.GetOrInsertIsolate(code.v8_isolate_iid());
+ if (!v8_isolate_id) {
+ return;
+ }
+
+ v8_tracker_->AddInternalCode(ts, *v8_isolate_id, code);
+}
+
+void V8Module::ParseV8WasmCode(protozero::ConstBytes bytes,
+ int64_t ts,
+ const TracePacketData& data) {
+ V8SequenceState& state =
+ *V8SequenceState::GetOrCreate(data.sequence_state->state());
+
+ V8WasmCode::Decoder code(bytes);
+
+ auto v8_isolate_id = state.GetOrInsertIsolate(code.v8_isolate_iid());
+ if (!v8_isolate_id) {
+ return;
+ }
+
+ auto v8_wasm_script_id =
+ state.GetOrInsertWasmScript(code.v8_wasm_script_iid(), *v8_isolate_id);
+ if (!v8_wasm_script_id) {
+ return;
+ }
+
+ v8_tracker_->AddWasmCode(ts, *v8_isolate_id, *v8_wasm_script_id, code);
+}
+
+void V8Module::ParseV8RegExpCode(protozero::ConstBytes bytes,
+ int64_t ts,
+ const TracePacketData& data) {
+ V8SequenceState& state =
+ *V8SequenceState::GetOrCreate(data.sequence_state->state());
+
+ V8RegExpCode::Decoder code(bytes);
+
+ auto v8_isolate_id = state.GetOrInsertIsolate(code.v8_isolate_iid());
+ if (!v8_isolate_id) {
+ return;
+ }
+
+ v8_tracker_->AddRegExpCode(ts, *v8_isolate_id, code);
+}
+
+void V8Module::ParseV8CodeMove(protozero::ConstBytes bytes,
+ int64_t,
+ const TracePacketData& data) {
+ V8SequenceState& state =
+ *V8SequenceState::GetOrCreate(data.sequence_state->state());
+ protos::pbzero::V8CodeMove::Decoder v8_code_move(bytes);
+
+ std::optional<tables::V8IsolateTable::Id> isolate_id =
+ state.GetOrInsertIsolate(v8_code_move.isolate_iid());
+ if (!isolate_id) {
+ return;
+ }
+
+ // TODO(carlscab): Implement
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/v8_module.h b/src/trace_processor/importers/proto/v8_module.h
new file mode 100644
index 0000000..de3f896
--- /dev/null
+++ b/src/trace_processor/importers/proto/v8_module.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 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 SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_MODULE_H_
+
+#include <cstdint>
+
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+
+namespace perfetto {
+namespace protos {
+namespace pbzero {
+class TracePacket_Decoder;
+}
+} // namespace protos
+namespace trace_processor {
+
+class PacketSequenceState;
+struct TracePacketData;
+class V8Tracker;
+
+// Populates v8 related tables.
+//
+// This class processes v8 related trace packets and populates the various
+// tables. In particular it keeps track of v8 Isolates and what code and
+// associated debug information has been loaded in each of the isolates.
+class V8Module : public ProtoImporterModule {
+ public:
+ explicit V8Module(TraceProcessorContext* context);
+
+ ~V8Module() override;
+
+ ModuleResult TokenizePacket(
+ const protos::pbzero::TracePacket_Decoder& decoder,
+ TraceBlobView* packet,
+ int64_t packet_timestamp,
+ PacketSequenceState* state,
+ uint32_t field_id) override;
+
+ void ParseTracePacketData(const protos::pbzero::TracePacket_Decoder&,
+ int64_t ts,
+ const TracePacketData& packet_data,
+ uint32_t field_id) override;
+
+ private:
+ void ParseV8JsCode(protozero::ConstBytes bytes,
+ int64_t ts,
+ const TracePacketData& data);
+ void ParseV8InternalCode(protozero::ConstBytes bytes,
+ int64_t ts,
+ const TracePacketData& data);
+ void ParseV8WasmCode(protozero::ConstBytes bytes,
+ int64_t ts,
+ const TracePacketData& data);
+ void ParseV8RegExpCode(protozero::ConstBytes bytes,
+ int64_t ts,
+ const TracePacketData& data);
+ void ParseV8CodeMove(protozero::ConstBytes bytes,
+ int64_t ts,
+ const TracePacketData& data);
+
+ TraceProcessorContext* const context_;
+ V8Tracker* const v8_tracker_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_MODULE_H_
diff --git a/src/trace_processor/importers/proto/v8_sequence_state.cc b/src/trace_processor/importers/proto/v8_sequence_state.cc
new file mode 100644
index 0000000..946ab22
--- /dev/null
+++ b/src/trace_processor/importers/proto/v8_sequence_state.cc
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2024 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 "src/trace_processor/importers/proto/v8_sequence_state.h"
+#include <optional>
+
+#include "perfetto/ext/base/string_utils.h"
+#include "protos/perfetto/trace/chrome/v8.pbzero.h"
+#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/string_encoding_utils.h"
+#include "src/trace_processor/importers/proto/v8_tracker.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/v8_tables_py.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using ::perfetto::protos::pbzero::InternedData;
+using ::perfetto::protos::pbzero::InternedV8JsFunction;
+using ::perfetto::protos::pbzero::InternedV8String;
+
+protozero::ConstBytes ToConstBytes(const TraceBlobView& view) {
+ return {view.data(), view.size()};
+}
+
+} // namespace
+
+V8SequenceState::V8SequenceState(PacketSequenceState* sequence_state)
+ : sequence_state_(sequence_state),
+ v8_tracker_(V8Tracker::GetOrCreate(sequence_state->context())) {}
+
+V8SequenceState::~V8SequenceState() = default;
+
+std::optional<tables::V8IsolateTable::Id> V8SequenceState::GetOrInsertIsolate(
+ uint64_t iid) {
+ if (auto* id = isolates_.Find(iid); id != nullptr) {
+ return *id;
+ }
+
+ auto* view = sequence_state_->current_generation()->GetInternedMessageView(
+ InternedData::kV8IsolateFieldNumber, iid);
+ if (!view) {
+ sequence_state_->context()->storage->IncrementStats(
+ stats::v8_intern_errors);
+ return std::nullopt;
+ }
+
+ auto isolate_id = v8_tracker_->InternIsolate(ToConstBytes(view->message()));
+ isolates_.Insert(iid, isolate_id);
+ return isolate_id;
+}
+
+std::optional<tables::V8JsFunctionTable::Id>
+V8SequenceState::GetOrInsertJsFunction(uint64_t iid,
+ tables::V8IsolateTable::Id isolate_id) {
+ if (auto* id = js_functions_.Find(iid); id != nullptr) {
+ return *id;
+ }
+
+ auto* view = sequence_state_->current_generation()->GetInternedMessageView(
+ InternedData::kV8JsFunctionFieldNumber, iid);
+ if (!view) {
+ sequence_state_->context()->storage->IncrementStats(
+ stats::v8_intern_errors);
+ return std::nullopt;
+ }
+
+ InternedV8JsFunction::Decoder function(ToConstBytes(view->message()));
+
+ std::optional<tables::V8JsScriptTable::Id> script_id =
+ GetOrInsertJsScript(function.v8_js_script_iid(), isolate_id);
+ if (!script_id) {
+ return std::nullopt;
+ }
+
+ auto name = GetOrInsertJsFunctionName(function.v8_js_function_name_iid());
+ if (!name) {
+ return std::nullopt;
+ }
+
+ auto function_id = v8_tracker_->InternJsFunction(
+ ToConstBytes(view->message()), *name, *script_id);
+
+ js_functions_.Insert(iid, function_id);
+ return function_id;
+}
+
+std::optional<tables::V8WasmScriptTable::Id>
+V8SequenceState::GetOrInsertWasmScript(uint64_t iid,
+ tables::V8IsolateTable::Id isolate_id) {
+ if (auto* id = wasm_scripts_.Find(iid); id != nullptr) {
+ return *id;
+ }
+ auto* view = sequence_state_->current_generation()->GetInternedMessageView(
+ InternedData::kV8WasmScriptFieldNumber, iid);
+ if (!view) {
+ sequence_state_->context()->storage->IncrementStats(
+ stats::v8_intern_errors);
+ return std::nullopt;
+ }
+
+ tables::V8WasmScriptTable::Id script_id =
+ v8_tracker_->InternWasmScript(ToConstBytes(view->message()), isolate_id);
+ wasm_scripts_.Insert(iid, script_id);
+ return script_id;
+}
+
+std::optional<tables::V8JsScriptTable::Id> V8SequenceState::GetOrInsertJsScript(
+ uint64_t iid,
+ tables::V8IsolateTable::Id v8_isolate_id) {
+ if (auto* id = js_scripts_.Find(iid); id != nullptr) {
+ return *id;
+ }
+ auto* view = sequence_state_->current_generation()->GetInternedMessageView(
+ InternedData::kV8JsScriptFieldNumber, iid);
+ if (!view) {
+ sequence_state_->context()->storage->IncrementStats(
+ stats::v8_intern_errors);
+ return std::nullopt;
+ }
+
+ tables::V8JsScriptTable::Id script_id =
+ v8_tracker_->InternJsScript(ToConstBytes(view->message()), v8_isolate_id);
+ js_scripts_.Insert(iid, script_id);
+ return script_id;
+}
+
+std::optional<StringId> V8SequenceState::GetOrInsertJsFunctionName(
+ uint64_t iid) {
+ if (auto* id = js_function_names_.Find(iid); id != nullptr) {
+ return *id;
+ }
+
+ auto* view = sequence_state_->current_generation()->GetInternedMessageView(
+ InternedData::kV8JsFunctionNameFieldNumber, iid);
+
+ if (!view) {
+ sequence_state_->context()->storage->IncrementStats(
+ stats::v8_intern_errors);
+ return std::nullopt;
+ }
+
+ InternedV8String::Decoder function_name(ToConstBytes(view->message()));
+ auto& storage = *sequence_state_->context()->storage;
+ StringId id;
+ if (function_name.has_latin1()) {
+ id = storage.InternString(
+ base::StringView(ConvertLatin1ToUtf8(function_name.latin1())));
+ } else if (function_name.has_utf16_le()) {
+ id = storage.InternString(
+ base::StringView(ConvertUtf16LeToUtf8(function_name.latin1())));
+ } else if (function_name.has_utf16_be()) {
+ id = storage.InternString(
+ base::StringView(ConvertUtf16BeToUtf8(function_name.latin1())));
+ } else {
+ id = storage.InternString("");
+ }
+
+ js_function_names_.Insert(iid, id);
+ return id;
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/v8_sequence_state.h b/src/trace_processor/importers/proto/v8_sequence_state.h
new file mode 100644
index 0000000..781960d
--- /dev/null
+++ b/src/trace_processor/importers/proto/v8_sequence_state.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 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 SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_SEQUENCE_STATE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_SEQUENCE_STATE_H_
+
+#include <cstdint>
+#include <optional>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/v8_tables_py.h"
+#include "src/trace_processor/types/destructible.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class V8Tracker;
+
+// Helper class to deal with V8 related interned data.
+class V8SequenceState : public Destructible {
+ public:
+ static V8SequenceState* GetOrCreate(PacketSequenceState* sequence_state) {
+ auto& v8_sequence_state =
+ sequence_state->extensible_sequence_state().v8_sequence_state;
+ if (!v8_sequence_state) {
+ v8_sequence_state.reset(new V8SequenceState(sequence_state));
+ }
+ return static_cast<V8SequenceState*>(v8_sequence_state.get());
+ }
+
+ ~V8SequenceState() override;
+
+ std::optional<tables::V8IsolateTable::Id> GetOrInsertIsolate(uint64_t iid);
+ std::optional<tables::V8JsFunctionTable::Id> GetOrInsertJsFunction(
+ uint64_t iid,
+ tables::V8IsolateTable::Id isolate_id);
+ std::optional<tables::V8WasmScriptTable::Id> GetOrInsertWasmScript(
+ uint64_t iid,
+ tables::V8IsolateTable::Id isolate_id);
+
+ private:
+ explicit V8SequenceState(PacketSequenceState* sequence_state);
+ std::optional<tables::V8JsScriptTable::Id> GetOrInsertJsScript(
+ uint64_t iid,
+ tables::V8IsolateTable::Id isolate_id);
+ std::optional<StringId> GetOrInsertJsFunctionName(uint64_t iid);
+
+ PacketSequenceState* const sequence_state_;
+ V8Tracker* const v8_tracker_;
+
+ using InterningId = uint64_t;
+ base::FlatHashMap<InterningId, tables::V8IsolateTable::Id> isolates_;
+ base::FlatHashMap<InterningId, tables::V8JsScriptTable::Id> js_scripts_;
+ base::FlatHashMap<InterningId, tables::V8WasmScriptTable::Id> wasm_scripts_;
+ base::FlatHashMap<InterningId, tables::V8JsFunctionTable::Id> js_functions_;
+ base::FlatHashMap<InterningId, StringId> js_function_names_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_SEQUENCE_STATE_H_
diff --git a/src/trace_processor/importers/proto/v8_tracker.cc b/src/trace_processor/importers/proto/v8_tracker.cc
new file mode 100644
index 0000000..963ad5b
--- /dev/null
+++ b/src/trace_processor/importers/proto/v8_tracker.cc
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2024 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 "src/trace_processor/importers/proto/v8_tracker.h"
+
+#include <cstdint>
+#include <utility>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/protozero/field.h"
+#include "protos/perfetto/trace/chrome/v8.pbzero.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/proto/string_encoding_utils.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/metadata_tables_py.h"
+#include "src/trace_processor/tables/v8_tables_py.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using ::perfetto::protos::pbzero::InternedV8Isolate;
+using ::perfetto::protos::pbzero::InternedV8JsFunction;
+using ::perfetto::protos::pbzero::InternedV8JsScript;
+using ::perfetto::protos::pbzero::InternedV8WasmScript;
+using ::perfetto::protos::pbzero::V8InternalCode;
+using ::perfetto::protos::pbzero::V8JsCode;
+using ::perfetto::protos::pbzero::V8String;
+using ::perfetto::protos::pbzero::V8WasmCode;
+
+base::StringView JsScriptTypeToString(int32_t type) {
+ if (type < protos::pbzero::InternedV8JsScript_Type_MIN ||
+ type > protos::pbzero::InternedV8JsScript_Type_MAX) {
+ return "UNKNOWN";
+ }
+ base::StringView name =
+ InternedV8JsScript::Type_Name(InternedV8JsScript::Type(type));
+ // Remove the "TYPE_" prefix
+ return name.substr(5);
+}
+
+base::StringView JsFunctionKindToString(int32_t kind) {
+ if (kind < protos::pbzero::InternedV8JsFunction_Kind_MIN ||
+ kind > protos::pbzero::InternedV8JsFunction_Kind_MAX) {
+ return "UNKNOWN";
+ }
+ base::StringView name =
+ InternedV8JsFunction::Kind_Name(InternedV8JsFunction::Kind(kind));
+ // Remove the "KIND_" prefix
+ return name.substr(5);
+}
+
+} // namespace
+
+V8Tracker::V8Tracker(TraceProcessorContext* context) : context_(context) {}
+
+V8Tracker::~V8Tracker() = default;
+
+tables::V8IsolateTable::Id V8Tracker::InternIsolate(
+ protozero::ConstBytes bytes) {
+ InternedV8Isolate::Decoder isolate(bytes);
+ const UniquePid upid =
+ context_->process_tracker->GetOrCreateProcess(isolate.pid());
+
+ if (auto* id =
+ isolate_index_.Find(std::make_pair(upid, isolate.isolate_id()));
+ id) {
+ return *id;
+ }
+
+ // TODO(carlscab): Implement support for no code range
+ PERFETTO_CHECK(isolate.has_code_range());
+
+ InternedV8Isolate::CodeRange::Decoder code_range(isolate.code_range());
+
+ auto v8_isolate_id =
+ context_->storage->mutable_v8_isolate_table()
+ ->Insert(
+ {upid, isolate.isolate_id(),
+ static_cast<int64_t>(isolate.embedded_blob_code_start_address()),
+ static_cast<int64_t>(isolate.embedded_blob_code_size()),
+ static_cast<int64_t>(code_range.base_address()),
+ static_cast<int64_t>(code_range.size()),
+ code_range.is_process_wide(),
+ code_range.has_embedded_blob_code_copy_start_address()
+ ? std::make_optional(static_cast<int64_t>(
+ code_range.embedded_blob_code_copy_start_address()))
+ : std::nullopt
+
+ })
+ .id;
+ isolate_index_.Insert(std::make_pair(upid, isolate.isolate_id()),
+ v8_isolate_id);
+ return v8_isolate_id;
+}
+
+tables::V8JsScriptTable::Id V8Tracker::InternJsScript(
+ protozero::ConstBytes bytes,
+ tables::V8IsolateTable::Id isolate_id) {
+ InternedV8JsScript::Decoder script(bytes);
+
+ if (auto* id =
+ js_script_index_.Find(std::make_pair(isolate_id, script.script_id()));
+ id) {
+ return *id;
+ }
+
+ tables::V8JsScriptTable::Row row;
+ row.v8_isolate_id = isolate_id;
+ row.internal_script_id = script.script_id();
+ row.script_type =
+ context_->storage->InternString(JsScriptTypeToString(script.type()));
+ row.name = InternV8String(V8String::Decoder(script.name()));
+ row.source = InternV8String(V8String::Decoder(script.source()));
+
+ tables::V8JsScriptTable::Id script_id =
+ context_->storage->mutable_v8_js_script_table()->Insert(row).id;
+ js_script_index_.Insert(std::make_pair(isolate_id, script.script_id()),
+ script_id);
+ return script_id;
+}
+
+tables::V8WasmScriptTable::Id V8Tracker::InternWasmScript(
+ protozero::ConstBytes bytes,
+ tables::V8IsolateTable::Id isolate_id) {
+ InternedV8WasmScript::Decoder script(bytes);
+
+ if (auto* id = wasm_script_index_.Find(
+ std::make_pair(isolate_id, script.script_id()));
+ id) {
+ return *id;
+ }
+
+ tables::V8WasmScriptTable::Row row;
+ row.v8_isolate_id = isolate_id;
+ row.internal_script_id = script.script_id();
+ row.url = context_->storage->InternString(script.url());
+
+ tables::V8WasmScriptTable::Id script_id =
+ context_->storage->mutable_v8_wasm_script_table()->Insert(row).id;
+ wasm_script_index_.Insert(std::make_pair(isolate_id, script.script_id()),
+ script_id);
+ return script_id;
+}
+
+tables::V8JsFunctionTable::Id V8Tracker::InternJsFunction(
+ protozero::ConstBytes bytes,
+ StringId name,
+ tables::V8JsScriptTable::Id script_id) {
+ InternedV8JsFunction::Decoder function(bytes);
+
+ tables::V8JsFunctionTable::Row row;
+ row.name = name;
+ row.v8_js_script_id = script_id;
+ row.is_toplevel = function.is_toplevel();
+ row.kind =
+ context_->storage->InternString(JsFunctionKindToString(function.kind()));
+ // TODO(carlscab): Row and line are hard. Offset is in bytes, row and line are
+ // in characters and we potentially have a multi byte encoding (UTF16). Good
+ // luck!
+
+ if (auto* id = js_function_index_.Find(row); id) {
+ return *id;
+ }
+
+ tables::V8JsFunctionTable::Id function_id =
+ context_->storage->mutable_v8_js_function_table()->Insert(row).id;
+ js_function_index_.Insert(row, function_id);
+ return function_id;
+}
+
+void V8Tracker::AddJsCode(int64_t,
+ tables::V8IsolateTable::Id,
+ tables::V8JsFunctionTable::Id,
+ const protos::pbzero::V8JsCode::Decoder&) {
+ // TODO(carlscab): Implement
+}
+
+void V8Tracker::AddInternalCode(
+ int64_t,
+ tables::V8IsolateTable::Id,
+ const protos::pbzero::V8InternalCode::Decoder&) {
+ // TODO(carlscab): Implement
+}
+
+void V8Tracker::AddWasmCode(int64_t,
+ tables::V8IsolateTable::Id,
+ tables::V8WasmScriptTable::Id,
+ const protos::pbzero::V8WasmCode::Decoder&) {
+ // TODO(carlscab): Implement
+}
+
+void V8Tracker::AddRegExpCode(int64_t,
+ tables::V8IsolateTable::Id,
+ const protos::pbzero::V8RegExpCode::Decoder&) {
+ // TODO(carlscab): Implement
+}
+
+StringId V8Tracker::InternV8String(
+ const protos::pbzero::V8String::Decoder& v8_string) {
+ auto& storage = *context_->storage;
+ if (v8_string.has_latin1()) {
+ return storage.InternString(
+ base::StringView(ConvertLatin1ToUtf8(v8_string.latin1())));
+ }
+
+ if (v8_string.has_utf16_le()) {
+ return storage.InternString(
+ base::StringView(ConvertUtf16LeToUtf8(v8_string.latin1())));
+ }
+
+ if (v8_string.has_utf16_be()) {
+ return storage.InternString(
+ base::StringView(ConvertUtf16BeToUtf8(v8_string.latin1())));
+ }
+ return storage.InternString("");
+}
+
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/importers/proto/v8_tracker.h b/src/trace_processor/importers/proto/v8_tracker.h
new file mode 100644
index 0000000..6da4d12
--- /dev/null
+++ b/src/trace_processor/importers/proto/v8_tracker.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 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 SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_TRACKER_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/protozero/field.h"
+#include "protos/perfetto/trace/chrome/v8.pbzero.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/v8_tables_py.h"
+#include "src/trace_processor/types/destructible.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Keeps track of V8 related objects.
+class V8Tracker : public Destructible {
+ public:
+ static V8Tracker* GetOrCreate(TraceProcessorContext* context) {
+ if (!context->v8_tracker) {
+ context->v8_tracker.reset(new V8Tracker(context));
+ }
+ return static_cast<V8Tracker*>(context->v8_tracker.get());
+ }
+
+ ~V8Tracker() override;
+
+ tables::V8IsolateTable::Id InternIsolate(protozero::ConstBytes bytes);
+ tables::V8JsScriptTable::Id InternJsScript(
+ protozero::ConstBytes bytes,
+ tables::V8IsolateTable::Id isolate_id);
+ tables::V8WasmScriptTable::Id InternWasmScript(
+ protozero::ConstBytes bytes,
+ tables::V8IsolateTable::Id isolate_id);
+ tables::V8JsFunctionTable::Id InternJsFunction(
+ protozero::ConstBytes bytes,
+ StringId name,
+ tables::V8JsScriptTable::Id script_id);
+
+ void AddJsCode(int64_t timestamp,
+ tables::V8IsolateTable::Id isolate_id,
+ tables::V8JsFunctionTable::Id function_id,
+ const protos::pbzero::V8JsCode::Decoder& code);
+
+ void AddInternalCode(int64_t timestamp,
+ tables::V8IsolateTable::Id v8_isolate_id,
+ const protos::pbzero::V8InternalCode::Decoder& code);
+
+ void AddWasmCode(int64_t timestamp,
+ tables::V8IsolateTable::Id isolate_id,
+ tables::V8WasmScriptTable::Id script_id,
+ const protos::pbzero::V8WasmCode::Decoder& code);
+
+ void AddRegExpCode(int64_t timestamp,
+ tables::V8IsolateTable::Id v8_isolate_id,
+ const protos::pbzero::V8RegExpCode::Decoder& code);
+
+ private:
+ explicit V8Tracker(TraceProcessorContext* context);
+
+ StringId InternV8String(const protos::pbzero::V8String::Decoder& v8_string);
+
+ TraceProcessorContext* const context_;
+
+ struct IsolateIndexHash {
+ size_t operator()(const std::pair<UniquePid, int32_t>& v) const {
+ return static_cast<size_t>(base::Hasher::Combine(v.first, v.second));
+ }
+ };
+ base::FlatHashMap<std::pair<UniquePid, int32_t>,
+ tables::V8IsolateTable::Id,
+ IsolateIndexHash>
+ isolate_index_;
+
+ struct ScriptIndexHash {
+ size_t operator()(
+ const std::pair<tables::V8IsolateTable::Id, int32_t>& v) const {
+ return static_cast<size_t>(
+ base::Hasher::Combine(v.first.value, v.second));
+ }
+ };
+ base::FlatHashMap<std::pair<tables::V8IsolateTable::Id, int32_t>,
+ tables::V8JsScriptTable::Id,
+ ScriptIndexHash>
+ js_script_index_;
+ base::FlatHashMap<std::pair<tables::V8IsolateTable::Id, int32_t>,
+ tables::V8WasmScriptTable::Id,
+ ScriptIndexHash>
+ wasm_script_index_;
+
+ struct JsFunctionHash {
+ size_t operator()(const tables::V8JsFunctionTable::Row& v) const {
+ return static_cast<size_t>(base::Hasher::Combine(
+ v.name.raw_id(), v.v8_js_script_id.value, v.is_toplevel,
+ v.kind.raw_id(), v.line.value_or(0), v.column.value_or(0)));
+ }
+ };
+ base::FlatHashMap<tables::V8JsFunctionTable::Row,
+ tables::V8JsFunctionTable::Id,
+ JsFunctionHash>
+ js_function_index_;
+};
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_TRACKER_H_
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index abd7d24..1d572a7 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -255,6 +255,9 @@
"missing. Defaulted to inaccurate packet timestamp."), \
F(atom_unknown, kSingle, kInfo, kAnalysis, \
"Unknown statsd atom. Atom descriptor may need to be updated"), \
+ F(v8_intern_errors, \
+ kSingle, kDataLoss, kAnalysis, \
+ "Failed to resolve V8 interned data."), \
F(winscope_sf_layers_parse_errors, kSingle, kInfo, kAnalysis, \
"SurfaceFlinger layers snapshot has unknown fields, which results in " \
"some arguments missing. You may need a newer version of trace " \
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 4eb9253..51ec58c 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -46,6 +46,7 @@
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/trace_proto_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
+#include "src/trace_processor/tables/v8_tables_py.h"
#include "src/trace_processor/tables/winscope_tables_py.h"
#include "src/trace_processor/types/variadic.h"
#include "src/trace_processor/views/slice_views.h"
@@ -725,6 +726,31 @@
return &actual_frame_timeline_slice_table_;
}
+ const tables::V8IsolateTable& v8_isolate_table() const {
+ return v8_isolate_table_;
+ }
+ tables::V8IsolateTable* mutable_v8_isolate_table() {
+ return &v8_isolate_table_;
+ }
+ const tables::V8JsScriptTable& v8_js_script_table() const {
+ return v8_js_script_table_;
+ }
+ tables::V8JsScriptTable* mutable_v8_js_script_table() {
+ return &v8_js_script_table_;
+ }
+ const tables::V8WasmScriptTable& v8_wasm_script_table() const {
+ return v8_wasm_script_table_;
+ }
+ tables::V8WasmScriptTable* mutable_v8_wasm_script_table() {
+ return &v8_wasm_script_table_;
+ }
+ const tables::V8JsFunctionTable& v8_js_function_table() const {
+ return v8_js_function_table_;
+ }
+ tables::V8JsFunctionTable* mutable_v8_js_function_table() {
+ return &v8_js_function_table_;
+ }
+
const tables::SurfaceFlingerLayersSnapshotTable&
surfaceflinger_layers_snapshot_table() const {
return surfaceflinger_layers_snapshot_table_;
@@ -1022,6 +1048,12 @@
tables::ActualFrameTimelineSliceTable actual_frame_timeline_slice_table_{
&string_pool_, &slice_table_};
+ // V8 tables
+ tables::V8IsolateTable v8_isolate_table_{&string_pool_};
+ tables::V8JsScriptTable v8_js_script_table_{&string_pool_};
+ tables::V8WasmScriptTable v8_wasm_script_table_{&string_pool_};
+ tables::V8JsFunctionTable v8_js_function_table_{&string_pool_};
+
// Winscope tables
tables::SurfaceFlingerLayersSnapshotTable
surfaceflinger_layers_snapshot_table_{&string_pool_};
diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn
index 7d0ec0f..1bbce44 100644
--- a/src/trace_processor/tables/BUILD.gn
+++ b/src/trace_processor/tables/BUILD.gn
@@ -27,6 +27,7 @@
"slice_tables.py",
"trace_proto_tables.py",
"track_tables.py",
+ "v8_tables.py",
"winscope_tables.py",
]
generate_docs = true
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index 553f5cd..4975f81 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -24,6 +24,7 @@
#include "src/trace_processor/tables/slice_tables_py.h"
#include "src/trace_processor/tables/trace_proto_tables_py.h"
#include "src/trace_processor/tables/track_tables_py.h"
+#include "src/trace_processor/tables/v8_tables_py.h"
#include "src/trace_processor/tables/winscope_tables_py.h"
namespace perfetto {
@@ -117,6 +118,12 @@
MemorySnapshotNodeTable::~MemorySnapshotNodeTable() = default;
MemorySnapshotEdgeTable::~MemorySnapshotEdgeTable() = default;
+// v8_tables_py.h
+V8IsolateTable::~V8IsolateTable() = default;
+V8JsScriptTable::~V8JsScriptTable() = default;
+V8WasmScriptTable::~V8WasmScriptTable() = default;
+V8JsFunctionTable::~V8JsFunctionTable() = default;
+
// winscope_tables_py.h
SurfaceFlingerLayersSnapshotTable::~SurfaceFlingerLayersSnapshotTable() =
default;
diff --git a/src/trace_processor/tables/v8_tables.py b/src/trace_processor/tables/v8_tables.py
new file mode 100644
index 0000000..8fb7c01
--- /dev/null
+++ b/src/trace_processor/tables/v8_tables.py
@@ -0,0 +1,175 @@
+# Copyright (C) 2024 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.
+"""Contains tables related to the v8 (Javasrcript Engine) Datasource.
+
+These tables are WIP, the schema is not stable and you should not rely on them
+for any serious business just yet""
+"""
+
+from python.generators.trace_processor_table.public import Alias
+from python.generators.trace_processor_table.public import Column as C
+from python.generators.trace_processor_table.public import ColumnDoc
+from python.generators.trace_processor_table.public import ColumnFlag
+from python.generators.trace_processor_table.public import CppInt32
+from python.generators.trace_processor_table.public import CppInt64
+from python.generators.trace_processor_table.public import CppOptional
+from python.generators.trace_processor_table.public import CppString
+from python.generators.trace_processor_table.public import CppTableId
+from python.generators.trace_processor_table.public import CppUint32
+from python.generators.trace_processor_table.public import CppUint32 as CppBool
+from python.generators.trace_processor_table.public import Table
+from python.generators.trace_processor_table.public import TableDoc
+from .profiler_tables import STACK_PROFILE_FRAME_TABLE
+
+V8_ISOLATE = Table(
+ python_module=__file__,
+ class_name='V8IsolateTable',
+ sql_name='v8_isolate',
+ columns=[
+ C('upid', CppUint32()),
+ C('internal_isolate_id', CppInt32()),
+ C('embedded_blob_code_start_address', CppInt64()),
+ C('embedded_blob_code_size', CppInt64()),
+ C('code_range_base_address', CppOptional(CppInt64())),
+ C('code_range_size', CppOptional(CppInt64())),
+ C('shared_code_range', CppOptional(CppBool())),
+ C('embedded_blob_code_copy_start_address', CppOptional(CppInt64())),
+ C('v8_isolate_id', Alias('id')),
+ ],
+ tabledoc=TableDoc(
+ doc='Represents one Isolate instance',
+ group='v8',
+ columns={
+ 'upid':
+ 'Process the isolate was created in.',
+ 'internal_isolate_id':
+ 'Internal id used by the v8 engine. Unique in a process.',
+ 'embedded_blob_code_start_address':
+ 'Absolute start address of the embedded code blob.',
+ 'embedded_blob_code_size':
+ 'Size in bytes of the embedded code blob.',
+ 'code_range_base_address':
+ 'If this Isolate defines a CodeRange its base address is stored'
+ ' here',
+ 'code_range_size':
+ 'If this Isolate defines a CodeRange its size is stored here',
+ 'shared_code_range':
+ 'Whether the code range for this Isolate is shared with others'
+ ' in the same process. There is at max one such shared code'
+ ' range per process.',
+ 'embedded_blob_code_copy_start_address':
+ 'Used when short builtin calls are enabled, where embedded'
+ ' builtins are copied into the CodeRange so calls can be'
+ ' nearer.',
+ 'v8_isolate_id':
+ 'Alias for id. Makes joins easier',
+ },
+ ),
+)
+
+V8_JS_SCRIPT = Table(
+ python_module=__file__,
+ class_name='V8JsScriptTable',
+ sql_name='v8_js_script',
+ columns=[
+ C('v8_isolate_id', CppTableId(V8_ISOLATE)),
+ C('internal_script_id', CppInt32()),
+ C('script_type', CppString()),
+ C('name', CppString()),
+ C('source', CppOptional(CppString())),
+ C('v8_js_script_id', Alias('id')),
+ ],
+ tabledoc=TableDoc(
+ doc='Represents one Javascript script',
+ group='v8',
+ columns={
+ 'v8_isolate_id': 'V8 Isolate',
+ 'internal_script_id': 'Script id used by the V8 engine',
+ 'script_type': '',
+ 'name': '',
+ 'source': 'Actual contents of the script.',
+ 'v8_js_script_id': 'Alias for id. Makes joins easier',
+ },
+ ),
+)
+
+V8_WASM_SCRIPT = Table(
+ python_module=__file__,
+ class_name='V8WasmScriptTable',
+ sql_name='v8_wasm_script',
+ columns=[
+ C('v8_isolate_id', CppTableId(V8_ISOLATE)),
+ C('internal_script_id', CppInt32()),
+ C('url', CppString()),
+ C('source', CppOptional(CppString())),
+ C('v8_wasm_script_id', Alias('id')),
+ ],
+ tabledoc=TableDoc(
+ doc='Represents one WASM script',
+ group='v8',
+ columns={
+ 'v8_isolate_id': 'V8 Isolate',
+ 'internal_script_id': 'Script id used by the V8 engine',
+ 'url': 'URL of the source',
+ 'source': 'Actual contents of the script.',
+ 'v8_wasm_script_id': 'Alias for id. Makes joins easier',
+ },
+ ),
+)
+
+V8_JS_FUNCTION = Table(
+ python_module=__file__,
+ class_name='V8JsFunctionTable',
+ sql_name='v8_js_function',
+ columns=[
+ C('name', CppString()),
+ C('v8_js_script_id', CppTableId(V8_JS_SCRIPT)),
+ C('is_toplevel', CppBool()),
+ C('kind', CppString()),
+ C('line', CppOptional(CppUint32())),
+ C('column', CppOptional(CppUint32())),
+ C('v8_js_function_id', Alias('id')),
+ ],
+ tabledoc=TableDoc(
+ doc='Represents a v8 Javascript function',
+ group='v8',
+ columns={
+ 'name':
+ '',
+ 'v8_js_script_id':
+ ColumnDoc(
+ doc='Script where the function is defined.',
+ joinable='v8_js_script.id',
+ ),
+ 'is_toplevel':
+ 'Whether this function represents the top level script',
+ 'kind':
+ 'Function kind (e.g. regular function or constructor)',
+ 'line':
+ 'Line in script where function is defined. Starts at 1',
+ 'column':
+ 'Column in script where function is defined. Starts at 1',
+ 'v8_js_function_id':
+ 'Alias for id. Makes joins easier',
+ },
+ ),
+)
+
+# Keep this list sorted.
+ALL_TABLES = [
+ V8_ISOLATE,
+ V8_JS_SCRIPT,
+ V8_WASM_SCRIPT,
+ V8_JS_FUNCTION,
+]
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index e777d6e..86a6111 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -832,6 +832,11 @@
RegisterStaticTable(storage->expected_frame_timeline_slice_table());
RegisterStaticTable(storage->actual_frame_timeline_slice_table());
+ RegisterStaticTable(storage->v8_isolate_table());
+ RegisterStaticTable(storage->v8_js_script_table());
+ RegisterStaticTable(storage->v8_wasm_script_table());
+ RegisterStaticTable(storage->v8_js_function_table());
+
RegisterStaticTable(storage->surfaceflinger_layers_snapshot_table());
RegisterStaticTable(storage->surfaceflinger_layer_table());
RegisterStaticTable(storage->surfaceflinger_transactions_table());
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index 1eb16e4..8d770d0 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -122,8 +122,10 @@
std::unique_ptr<Destructible> thread_state_tracker; // ThreadStateTracker
std::unique_ptr<Destructible> i2c_tracker; // I2CTracker
std::unique_ptr<Destructible> perf_data_tracker; // PerfDataTracker
- std::unique_ptr<Destructible> content_analyzer;
- std::unique_ptr<Destructible> shell_transitions_tracker;
+ std::unique_ptr<Destructible> content_analyzer; // ProtoContentAnalyzer
+ std::unique_ptr<Destructible>
+ shell_transitions_tracker; // ShellTransitionsTracker
+ std::unique_ptr<Destructible> v8_tracker; // V8Tracker
// These fields are trace readers which will be called by |forwarding_parser|
// once the format of the trace is discovered. They are placed here as they
diff --git a/test/data/chrome/v8.code.trace.pb.gz.sha256 b/test/data/chrome/v8.code.trace.pb.gz.sha256
new file mode 100644
index 0000000..e1ac49b
--- /dev/null
+++ b/test/data/chrome/v8.code.trace.pb.gz.sha256
@@ -0,0 +1 @@
+cb21932d5a83cc63b4a4bc49059541075bc2da8865f5dfd0dff3f2bea4b21740
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 3ba2591..0dba5fa 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -56,6 +56,7 @@
from diff_tests.parser.atrace.tests import Atrace
from diff_tests.parser.atrace.tests_error_handling import AtraceErrorHandling
from diff_tests.parser.chrome.tests import ChromeParser
+from diff_tests.parser.chrome.tests_v8 import ChromeV8Parser
from diff_tests.parser.chrome.tests_memory_snapshots import ChromeMemorySnapshots
from diff_tests.parser.cros.tests import Cros
from diff_tests.parser.fs.tests import Fs
@@ -135,6 +136,7 @@
*ChromeMemorySnapshots(index_path, 'parser/chrome',
'ChromeMemorySnapshots').fetch(),
*ChromeParser(index_path, 'parser/chrome', 'ChromeParser').fetch(),
+ *ChromeV8Parser(index_path, 'parser/chrome', 'ChromeV8Parser').fetch(),
*Cros(index_path, 'parser/cros', 'Cros').fetch(),
*Fs(index_path, 'parser/fs', 'Fs').fetch(),
*Fuchsia(index_path, 'parser/fuchsia', 'Fuchsia').fetch(),
diff --git a/test/trace_processor/diff_tests/parser/chrome/tests_v8.py b/test/trace_processor/diff_tests/parser/chrome/tests_v8.py
new file mode 100644
index 0000000..798077a
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/chrome/tests_v8.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+# Copyright (C) 2024 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 a
+#
+# 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.
+
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DataPath, Metric, Path
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+from python.generators.trace_processor_table.public import Alias
+from src.trace_processor.tables.v8_tables import V8_ISOLATE, V8_JS_SCRIPT, V8_JS_FUNCTION, V8_WASM_SCRIPT
+
+
+def _no_duplicates_query(table):
+ group_by_columns = [
+ c.name for c in table.columns if not isinstance(c.type, Alias)
+ ]
+ return f"""
+ SELECT DISTINCT COUNT(*) AS count
+ FROM {table.sql_name}
+ GROUP BY {', '.join( group_by_columns)}"""
+
+
+class ChromeV8Parser(TestSuite):
+
+ def test_no_duplicates_in_v8_js_function(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome/v8.code.trace.pb.gz'),
+ query=_no_duplicates_query(V8_JS_FUNCTION),
+ out=Csv(""""count"\n1\n"""),
+ )
+
+ def test_no_duplicates_in_v8_js_script(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome/v8.code.trace.pb.gz'),
+ query=_no_duplicates_query(V8_JS_SCRIPT),
+ out=Csv(""""count"\n1\n"""),
+ )
+
+ def test_no_duplicates_in_v8_isolate(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome/v8.code.trace.pb.gz'),
+ query=_no_duplicates_query(V8_ISOLATE),
+ out=Csv(""""count"\n1\n"""),
+ )
+
+ def test_no_duplicates_in_v8_wasm_script(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome/v8.code.trace.pb.gz'),
+ query=_no_duplicates_query(V8_WASM_SCRIPT),
+ out=Csv(""""count"\n1\n"""),
+ )