1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/importers/proto/v8_sequence_state.h"
18 #include <optional>
19
20 #include "protos/perfetto/trace/chrome/v8.pbzero.h"
21 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
22 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
23 #include "src/trace_processor/importers/proto/string_encoding_utils.h"
24 #include "src/trace_processor/importers/proto/v8_tracker.h"
25 #include "src/trace_processor/storage/stats.h"
26 #include "src/trace_processor/storage/trace_storage.h"
27 #include "src/trace_processor/tables/v8_tables_py.h"
28
29 namespace perfetto {
30 namespace trace_processor {
31 namespace {
32
33 using ::perfetto::protos::pbzero::InternedData;
34 using ::perfetto::protos::pbzero::InternedV8JsFunction;
35 using ::perfetto::protos::pbzero::InternedV8String;
36
ToConstBytes(const TraceBlobView & view)37 protozero::ConstBytes ToConstBytes(const TraceBlobView& view) {
38 return {view.data(), view.size()};
39 }
40
41 } // namespace
42
V8SequenceState(TraceProcessorContext * context)43 V8SequenceState::V8SequenceState(TraceProcessorContext* context)
44 : context_(context), v8_tracker_(V8Tracker::GetOrCreate(context_)) {}
45
46 V8SequenceState::~V8SequenceState() = default;
47
GetOrInsertIsolate(uint64_t iid)48 std::optional<IsolateId> V8SequenceState::GetOrInsertIsolate(uint64_t iid) {
49 if (auto* id = isolates_.Find(iid); id != nullptr) {
50 return *id;
51 }
52
53 auto* view = GetInternedMessageView(InternedData::kV8IsolateFieldNumber, iid);
54 if (!view) {
55 context_->storage->IncrementStats(stats::v8_intern_errors);
56 return std::nullopt;
57 }
58
59 auto isolate_id = v8_tracker_->InternIsolate(ToConstBytes(view->message()));
60 isolates_.Insert(iid, isolate_id);
61 return isolate_id;
62 }
63
64 std::optional<tables::V8JsFunctionTable::Id>
GetOrInsertJsFunction(uint64_t iid,IsolateId isolate_id)65 V8SequenceState::GetOrInsertJsFunction(uint64_t iid, IsolateId isolate_id) {
66 if (auto* id = js_functions_.Find(iid); id != nullptr) {
67 return *id;
68 }
69
70 auto* view =
71 GetInternedMessageView(InternedData::kV8JsFunctionFieldNumber, iid);
72 if (!view) {
73 context_->storage->IncrementStats(stats::v8_intern_errors);
74 return std::nullopt;
75 }
76
77 InternedV8JsFunction::Decoder function(ToConstBytes(view->message()));
78
79 std::optional<tables::V8JsScriptTable::Id> script_id =
80 GetOrInsertJsScript(function.v8_js_script_iid(), isolate_id);
81 if (!script_id) {
82 return std::nullopt;
83 }
84
85 auto name = GetOrInsertJsFunctionName(function.v8_js_function_name_iid());
86 if (!name) {
87 return std::nullopt;
88 }
89
90 auto function_id = v8_tracker_->InternJsFunction(
91 ToConstBytes(view->message()), *name, *script_id);
92
93 js_functions_.Insert(iid, function_id);
94 return function_id;
95 }
96
97 std::optional<tables::V8WasmScriptTable::Id>
GetOrInsertWasmScript(uint64_t iid,IsolateId isolate_id)98 V8SequenceState::GetOrInsertWasmScript(uint64_t iid, IsolateId isolate_id) {
99 if (auto* id = wasm_scripts_.Find(iid); id != nullptr) {
100 return *id;
101 }
102 auto* view =
103 GetInternedMessageView(InternedData::kV8WasmScriptFieldNumber, iid);
104 if (!view) {
105 context_->storage->IncrementStats(stats::v8_intern_errors);
106 return std::nullopt;
107 }
108
109 tables::V8WasmScriptTable::Id script_id =
110 v8_tracker_->InternWasmScript(ToConstBytes(view->message()), isolate_id);
111 wasm_scripts_.Insert(iid, script_id);
112 return script_id;
113 }
114
GetOrInsertJsScript(uint64_t iid,IsolateId v8_isolate_id)115 std::optional<tables::V8JsScriptTable::Id> V8SequenceState::GetOrInsertJsScript(
116 uint64_t iid,
117 IsolateId v8_isolate_id) {
118 if (auto* id = js_scripts_.Find(iid); id != nullptr) {
119 return *id;
120 }
121 auto* view =
122 GetInternedMessageView(InternedData::kV8JsScriptFieldNumber, iid);
123 if (!view) {
124 context_->storage->IncrementStats(stats::v8_intern_errors);
125 return std::nullopt;
126 }
127
128 tables::V8JsScriptTable::Id script_id =
129 v8_tracker_->InternJsScript(ToConstBytes(view->message()), v8_isolate_id);
130 js_scripts_.Insert(iid, script_id);
131 return script_id;
132 }
133
GetOrInsertJsFunctionName(uint64_t iid)134 std::optional<StringId> V8SequenceState::GetOrInsertJsFunctionName(
135 uint64_t iid) {
136 if (auto* id = js_function_names_.Find(iid); id != nullptr) {
137 return *id;
138 }
139
140 auto* view =
141 GetInternedMessageView(InternedData::kV8JsFunctionNameFieldNumber, iid);
142
143 if (!view) {
144 context_->storage->IncrementStats(stats::v8_intern_errors);
145 return std::nullopt;
146 }
147
148 InternedV8String::Decoder function_name(ToConstBytes(view->message()));
149 auto& storage = *context_->storage;
150 StringId id;
151 if (function_name.has_latin1()) {
152 id = storage.InternString(
153 base::StringView(ConvertLatin1ToUtf8(function_name.latin1())));
154 } else if (function_name.has_utf16_le()) {
155 id = storage.InternString(
156 base::StringView(ConvertUtf16LeToUtf8(function_name.utf16_le())));
157 } else if (function_name.has_utf16_be()) {
158 id = storage.InternString(
159 base::StringView(ConvertUtf16BeToUtf8(function_name.utf16_be())));
160 } else {
161 id = storage.InternString("");
162 }
163
164 js_function_names_.Insert(iid, id);
165 return id;
166 }
167
168 } // namespace trace_processor
169 } // namespace perfetto
170