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_module.h"
18
19 #include <cstdint>
20 #include <optional>
21
22 #include "perfetto/base/logging.h"
23 #include "protos/perfetto/trace/chrome/v8.pbzero.h"
24 #include "protos/perfetto/trace/trace_packet.pbzero.h"
25 #include "src/trace_processor/importers/common/parser_types.h"
26 #include "src/trace_processor/importers/common/process_tracker.h"
27 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
28 #include "src/trace_processor/importers/proto/v8_sequence_state.h"
29 #include "src/trace_processor/importers/proto/v8_tracker.h"
30 #include "src/trace_processor/storage/stats.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32 #include "src/trace_processor/tables/metadata_tables_py.h"
33 #include "src/trace_processor/tables/v8_tables_py.h"
34
35 namespace perfetto {
36 namespace trace_processor {
37 namespace {
38
39 using ::perfetto::protos::pbzero::TracePacket;
40 using ::perfetto::protos::pbzero::V8CodeDefaults;
41 using ::perfetto::protos::pbzero::V8CodeMove;
42 using ::perfetto::protos::pbzero::V8InternalCode;
43 using ::perfetto::protos::pbzero::V8JsCode;
44 using ::perfetto::protos::pbzero::V8RegExpCode;
45 using ::perfetto::protos::pbzero::V8WasmCode;
46
47 } // namespace
48
V8Module(TraceProcessorContext * context)49 V8Module::V8Module(TraceProcessorContext* context)
50 : context_(context), v8_tracker_(V8Tracker::GetOrCreate(context_)) {
51 RegisterForField(TracePacket::kV8JsCodeFieldNumber, context_);
52 RegisterForField(TracePacket::kV8InternalCodeFieldNumber, context_);
53 RegisterForField(TracePacket::kV8WasmCodeFieldNumber, context_);
54 RegisterForField(TracePacket::kV8RegExpCodeFieldNumber, context_);
55 RegisterForField(TracePacket::kV8CodeMoveFieldNumber, context_);
56 }
57
58 V8Module::~V8Module() = default;
59
TokenizePacket(const TracePacket::Decoder &,TraceBlobView *,int64_t,RefPtr<PacketSequenceStateGeneration>,uint32_t)60 ModuleResult V8Module::TokenizePacket(
61 const TracePacket::Decoder&,
62 TraceBlobView* /*packet*/,
63 int64_t /*packet_timestamp*/,
64 RefPtr<PacketSequenceStateGeneration> /*state*/,
65 uint32_t /*field_id*/) {
66 return ModuleResult::Ignored();
67 }
68
ParseTracePacketData(const TracePacket::Decoder & decoder,int64_t ts,const TracePacketData & data,uint32_t field_id)69 void V8Module::ParseTracePacketData(const TracePacket::Decoder& decoder,
70 int64_t ts,
71 const TracePacketData& data,
72 uint32_t field_id) {
73 switch (field_id) {
74 case TracePacket::kV8JsCodeFieldNumber:
75 ParseV8JsCode(decoder.v8_js_code(), ts, data);
76 break;
77 case TracePacket::kV8InternalCodeFieldNumber:
78 ParseV8InternalCode(decoder.v8_internal_code(), ts, data);
79 break;
80 case TracePacket::kV8WasmCodeFieldNumber:
81 ParseV8WasmCode(decoder.v8_wasm_code(), ts, data);
82 break;
83 case TracePacket::kV8RegExpCodeFieldNumber:
84 ParseV8RegExpCode(decoder.v8_reg_exp_code(), ts, data);
85 break;
86 case TracePacket::kV8CodeMoveFieldNumber:
87 ParseV8CodeMove(decoder.v8_code_move(), ts, data);
88 break;
89 default:
90 break;
91 }
92 }
93
94 template <typename CodeDecoder>
GetUtid(PacketSequenceStateGeneration & generation,IsolateId isolate_id,const CodeDecoder & code)95 std::optional<UniqueTid> V8Module::GetUtid(
96 PacketSequenceStateGeneration& generation,
97 IsolateId isolate_id,
98 const CodeDecoder& code) {
99 auto* pid = isolate_to_pid_.Find(isolate_id);
100 if (!pid) {
101 tables::ProcessTable::Id upid(
102 context_->storage->v8_isolate_table().FindById(isolate_id)->upid());
103 pid = isolate_to_pid_
104 .Insert(isolate_id,
105 context_->storage->process_table().FindById(upid)->pid())
106 .first;
107 }
108
109 if (code.has_tid()) {
110 return context_->process_tracker->UpdateThread(code.tid(), *pid);
111 }
112
113 if (auto tid = GetDefaultTid(generation); tid.has_value()) {
114 return context_->process_tracker->UpdateThread(*tid, *pid);
115 }
116
117 return std::nullopt;
118 }
119
GetDefaultTid(PacketSequenceStateGeneration & generation) const120 std::optional<uint32_t> V8Module::GetDefaultTid(
121 PacketSequenceStateGeneration& generation) const {
122 auto* tp_defaults = generation.GetTracePacketDefaults();
123 if (!tp_defaults) {
124 context_->storage->IncrementStats(stats::v8_no_defaults);
125 return std::nullopt;
126 }
127 if (!tp_defaults->has_v8_code_defaults()) {
128 context_->storage->IncrementStats(stats::v8_no_defaults);
129 return std::nullopt;
130 }
131
132 V8CodeDefaults::Decoder v8_defaults(tp_defaults->v8_code_defaults());
133
134 if (!v8_defaults.has_tid()) {
135 context_->storage->IncrementStats(stats::v8_no_defaults);
136 return std::nullopt;
137 }
138
139 return v8_defaults.tid();
140 }
141
ParseV8JsCode(protozero::ConstBytes bytes,int64_t ts,const TracePacketData & data)142 void V8Module::ParseV8JsCode(protozero::ConstBytes bytes,
143 int64_t ts,
144 const TracePacketData& data) {
145 V8SequenceState& state =
146 *data.sequence_state->GetCustomState<V8SequenceState>();
147
148 V8JsCode::Decoder code(bytes);
149
150 auto v8_isolate_id = state.GetOrInsertIsolate(code.v8_isolate_iid());
151 if (!v8_isolate_id) {
152 return;
153 }
154
155 std::optional<UniqueTid> utid =
156 GetUtid(*data.sequence_state, *v8_isolate_id, code);
157 if (!utid) {
158 return;
159 }
160
161 auto v8_function_id =
162 state.GetOrInsertJsFunction(code.v8_js_function_iid(), *v8_isolate_id);
163 if (!v8_function_id) {
164 return;
165 }
166
167 v8_tracker_->AddJsCode(ts, *utid, *v8_isolate_id, *v8_function_id, code);
168 }
169
ParseV8InternalCode(protozero::ConstBytes bytes,int64_t ts,const TracePacketData & data)170 void V8Module::ParseV8InternalCode(protozero::ConstBytes bytes,
171 int64_t ts,
172 const TracePacketData& data) {
173 V8SequenceState& state =
174 *data.sequence_state->GetCustomState<V8SequenceState>();
175
176 V8InternalCode::Decoder code(bytes);
177
178 auto v8_isolate_id = state.GetOrInsertIsolate(code.v8_isolate_iid());
179 if (!v8_isolate_id) {
180 return;
181 }
182
183 std::optional<UniqueTid> utid =
184 GetUtid(*data.sequence_state, *v8_isolate_id, code);
185 if (!utid) {
186 return;
187 }
188
189 v8_tracker_->AddInternalCode(ts, *utid, *v8_isolate_id, code);
190 }
191
ParseV8WasmCode(protozero::ConstBytes bytes,int64_t ts,const TracePacketData & data)192 void V8Module::ParseV8WasmCode(protozero::ConstBytes bytes,
193 int64_t ts,
194 const TracePacketData& data) {
195 V8SequenceState& state =
196 *data.sequence_state->GetCustomState<V8SequenceState>();
197
198 V8WasmCode::Decoder code(bytes);
199
200 auto v8_isolate_id = state.GetOrInsertIsolate(code.v8_isolate_iid());
201 if (!v8_isolate_id) {
202 return;
203 }
204
205 auto v8_wasm_script_id =
206 state.GetOrInsertWasmScript(code.v8_wasm_script_iid(), *v8_isolate_id);
207 if (!v8_wasm_script_id) {
208 return;
209 }
210
211 std::optional<UniqueTid> utid =
212 GetUtid(*data.sequence_state, *v8_isolate_id, code);
213 if (!utid) {
214 return;
215 }
216
217 v8_tracker_->AddWasmCode(ts, *utid, *v8_isolate_id, *v8_wasm_script_id, code);
218 }
219
ParseV8RegExpCode(protozero::ConstBytes bytes,int64_t ts,const TracePacketData & data)220 void V8Module::ParseV8RegExpCode(protozero::ConstBytes bytes,
221 int64_t ts,
222 const TracePacketData& data) {
223 V8SequenceState& state =
224 *data.sequence_state->GetCustomState<V8SequenceState>();
225
226 V8RegExpCode::Decoder code(bytes);
227
228 auto v8_isolate_id = state.GetOrInsertIsolate(code.v8_isolate_iid());
229 if (!v8_isolate_id) {
230 return;
231 }
232
233 std::optional<UniqueTid> utid =
234 GetUtid(*data.sequence_state, *v8_isolate_id, code);
235 if (!utid) {
236 return;
237 }
238
239 v8_tracker_->AddRegExpCode(ts, *utid, *v8_isolate_id, code);
240 }
241
ParseV8CodeMove(protozero::ConstBytes bytes,int64_t,const TracePacketData & data)242 void V8Module::ParseV8CodeMove(protozero::ConstBytes bytes,
243 int64_t,
244 const TracePacketData& data) {
245 V8SequenceState& state =
246 *data.sequence_state->GetCustomState<V8SequenceState>();
247 protos::pbzero::V8CodeMove::Decoder v8_code_move(bytes);
248
249 std::optional<IsolateId> isolate_id =
250 state.GetOrInsertIsolate(v8_code_move.isolate_iid());
251 if (!isolate_id) {
252 return;
253 }
254
255 // TODO(carlscab): Implement
256 }
257
258 } // namespace trace_processor
259 } // namespace perfetto
260