• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_tracker.h"
18 
19 #include <cstdint>
20 #include <memory>
21 #include <optional>
22 #include <string>
23 #include <utility>
24 
25 #include "perfetto/base/logging.h"
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/base64.h"
28 #include "perfetto/ext/base/flat_hash_map.h"
29 #include "perfetto/ext/base/string_view.h"
30 #include "perfetto/protozero/field.h"
31 #include "perfetto/trace_processor/trace_blob.h"
32 #include "perfetto/trace_processor/trace_blob_view.h"
33 #include "protos/perfetto/trace/chrome/v8.pbzero.h"
34 #include "src/trace_processor/importers/common/address_range.h"
35 #include "src/trace_processor/importers/common/jit_cache.h"
36 #include "src/trace_processor/importers/common/mapping_tracker.h"
37 #include "src/trace_processor/importers/common/process_tracker.h"
38 #include "src/trace_processor/importers/proto/jit_tracker.h"
39 #include "src/trace_processor/importers/proto/string_encoding_utils.h"
40 #include "src/trace_processor/storage/stats.h"
41 #include "src/trace_processor/storage/trace_storage.h"
42 #include "src/trace_processor/tables/jit_tables_py.h"
43 #include "src/trace_processor/tables/v8_tables_py.h"
44 #include "src/trace_processor/types/trace_processor_context.h"
45 
46 namespace perfetto {
47 namespace trace_processor {
48 namespace {
49 
50 using ::perfetto::protos::pbzero::InternedV8Isolate;
51 using ::perfetto::protos::pbzero::InternedV8JsFunction;
52 using ::perfetto::protos::pbzero::InternedV8JsScript;
53 using ::perfetto::protos::pbzero::InternedV8WasmScript;
54 using ::perfetto::protos::pbzero::V8CodeMove;
55 using ::perfetto::protos::pbzero::V8InternalCode;
56 using ::perfetto::protos::pbzero::V8JsCode;
57 using ::perfetto::protos::pbzero::V8RegExpCode;
58 using ::perfetto::protos::pbzero::V8String;
59 using ::perfetto::protos::pbzero::V8WasmCode;
60 
IsInterpretedCode(const V8JsCode::Decoder & code)61 bool IsInterpretedCode(const V8JsCode::Decoder& code) {
62   switch (code.tier()) {
63     case V8JsCode::TIER_IGNITION:
64       return true;
65 
66     case V8JsCode::TIER_UNKNOWN:
67     case V8JsCode::TIER_SPARKPLUG:
68     case V8JsCode::TIER_MAGLEV:
69     case V8JsCode::TIER_TURBOSHAFT:
70     case V8JsCode::TIER_TURBOFAN:
71       return false;
72   }
73   PERFETTO_FATAL("Unreachable");
74 }
75 
IsNativeCode(const V8JsCode::Decoder & code)76 bool IsNativeCode(const V8JsCode::Decoder& code) {
77   switch (code.tier()) {
78     case V8JsCode::TIER_UNKNOWN:
79     case V8JsCode::TIER_IGNITION:
80       return false;
81 
82     case V8JsCode::TIER_SPARKPLUG:
83     case V8JsCode::TIER_MAGLEV:
84     case V8JsCode::TIER_TURBOSHAFT:
85     case V8JsCode::TIER_TURBOFAN:
86       return true;
87   }
88   PERFETTO_FATAL("Unreachable");
89 }
90 
JsScriptTypeToString(int32_t type)91 base::StringView JsScriptTypeToString(int32_t type) {
92   if (type < protos::pbzero::InternedV8JsScript_Type_MIN ||
93       type > protos::pbzero::InternedV8JsScript_Type_MAX) {
94     return "UNKNOWN";
95   }
96   base::StringView name =
97       InternedV8JsScript::Type_Name(InternedV8JsScript::Type(type));
98   // Remove the "TYPE_" prefix
99   return name.substr(5);
100 }
101 
JsFunctionKindToString(int32_t kind)102 base::StringView JsFunctionKindToString(int32_t kind) {
103   if (kind < protos::pbzero::InternedV8JsFunction_Kind_MIN ||
104       kind > protos::pbzero::InternedV8JsFunction_Kind_MAX) {
105     return "UNKNOWN";
106   }
107   base::StringView name =
108       InternedV8JsFunction::Kind_Name(InternedV8JsFunction::Kind(kind));
109   // Remove the "KIND_" prefix
110   return name.substr(5);
111 }
112 
JsCodeTierToString(int32_t tier)113 base::StringView JsCodeTierToString(int32_t tier) {
114   if (tier < protos::pbzero::V8JsCode_Tier_MIN ||
115       tier > protos::pbzero::V8JsCode_Tier_MAX) {
116     return "UNKNOWN";
117   }
118   base::StringView name = V8JsCode::Tier_Name(V8JsCode::Tier(tier));
119   // Remove the "TIER_" prefix
120   return name.substr(5);
121 }
122 
InternalCodeTypeToString(int32_t type)123 base::StringView InternalCodeTypeToString(int32_t type) {
124   if (type < protos::pbzero::V8InternalCode_Type_MIN ||
125       type > protos::pbzero::V8InternalCode_Type_MAX) {
126     return "UNKNOWN";
127   }
128   base::StringView name = V8InternalCode::Type_Name(V8InternalCode::Type(type));
129   // Remove the "TYPE_" prefix
130   return name.substr(5);
131 }
132 
WasmCodeTierToString(int32_t tier)133 base::StringView WasmCodeTierToString(int32_t tier) {
134   if (tier < protos::pbzero::V8WasmCode_Tier_MIN ||
135       tier > protos::pbzero::V8WasmCode_Tier_MAX) {
136     return "UNKNOWN";
137   }
138   base::StringView name = V8WasmCode::Tier_Name(V8WasmCode::Tier(tier));
139   // Remove the "TIER_" prefix
140   return name.substr(5);
141 }
142 
143 }  // namespace
144 
V8Tracker(TraceProcessorContext * context)145 V8Tracker::V8Tracker(TraceProcessorContext* context) : context_(context) {}
146 
147 V8Tracker::~V8Tracker() = default;
148 
InternIsolate(protozero::ConstBytes bytes)149 std::optional<IsolateId> V8Tracker::InternIsolate(protozero::ConstBytes bytes) {
150   InternedV8Isolate::Decoder isolate(bytes);
151 
152   const IsolateKey isolate_key{
153       context_->process_tracker->GetOrCreateProcess(isolate.pid()),
154       isolate.isolate_id()};
155 
156   if (auto* id = isolate_index_.Find(isolate_key); id) {
157     return *id;
158   }
159 
160   // TODO(b/347250452): Implement support for no code range
161   if (!isolate.has_code_range()) {
162     context_->storage->IncrementStats(stats::v8_isolate_has_no_code_range);
163     isolate_index_.Insert(isolate_key, std::nullopt);
164     return std::nullopt;
165   }
166 
167   return *isolate_index_.Insert(isolate_key, CreateIsolate(isolate)).first;
168 }
169 
FindEmbeddedBlobMapping(UniquePid upid,AddressRange embedded_blob_code) const170 UserMemoryMapping* V8Tracker::FindEmbeddedBlobMapping(
171     UniquePid upid,
172     AddressRange embedded_blob_code) const {
173   UserMemoryMapping* m = context_->mapping_tracker->FindUserMappingForAddress(
174       upid, embedded_blob_code.start());
175   if (!m) {
176     return nullptr;
177   }
178 
179   if (m->memory_range().start() == embedded_blob_code.start() &&
180       embedded_blob_code.end() <= m->memory_range().end()) {
181     return m;
182   }
183 
184   return nullptr;
185 }
186 
GetIsolateCodeRanges(UniquePid upid,const protos::pbzero::InternedV8Isolate::Decoder & isolate)187 std::pair<V8Tracker::IsolateCodeRanges, bool> V8Tracker::GetIsolateCodeRanges(
188     UniquePid upid,
189     const protos::pbzero::InternedV8Isolate::Decoder& isolate) {
190   PERFETTO_CHECK(isolate.has_code_range());
191 
192   IsolateCodeRanges res;
193 
194   InternedV8Isolate::CodeRange::Decoder code_range_proto(isolate.code_range());
195   AddressRange code_range = AddressRange::FromStartAndSize(
196       code_range_proto.base_address(), code_range_proto.size());
197 
198   res.heap_code.Add(code_range);
199   if (isolate.has_embedded_blob_code_start_address() &&
200       isolate.embedded_blob_code_size() != 0) {
201     res.embedded_blob = AddressRange::FromStartAndSize(
202         isolate.embedded_blob_code_start_address(),
203         isolate.embedded_blob_code_size());
204 
205     if (UserMemoryMapping* m =
206             FindEmbeddedBlobMapping(upid, *res.embedded_blob);
207         m) {
208       res.embedded_blob = m->memory_range();
209     }
210 
211     res.heap_code.Remove(*res.embedded_blob);
212   }
213 
214   return {std::move(res), code_range_proto.is_process_wide()};
215 }
216 
CreateJitCaches(UniquePid upid,const IsolateCodeRanges & code_ranges)217 AddressRangeMap<JitCache*> V8Tracker::CreateJitCaches(
218     UniquePid upid,
219     const IsolateCodeRanges& code_ranges) {
220   JitTracker* const jit_tracker = JitTracker::GetOrCreate(context_);
221   AddressRangeMap<JitCache*> jit_caches;
222   for (const AddressRange& r : code_ranges.heap_code) {
223     jit_caches.Emplace(r, jit_tracker->CreateJitCache("v8 code", upid, r));
224   }
225   if (code_ranges.embedded_blob) {
226     jit_caches.Emplace(*code_ranges.embedded_blob,
227                        jit_tracker->CreateJitCache("v8 blob", upid,
228                                                    *code_ranges.embedded_blob));
229   }
230 
231   return jit_caches;
232 }
233 
GetOrCreateSharedJitCaches(UniquePid upid,const IsolateCodeRanges & code_ranges)234 AddressRangeMap<JitCache*> V8Tracker::GetOrCreateSharedJitCaches(
235     UniquePid upid,
236     const IsolateCodeRanges& code_ranges) {
237   if (auto* shared = shared_code_ranges_.Find(upid); shared) {
238     PERFETTO_CHECK(shared->code_ranges == code_ranges);
239     return shared->jit_caches;
240   }
241 
242   return shared_code_ranges_
243       .Insert(upid, {code_ranges, CreateJitCaches(upid, code_ranges)})
244       .first->jit_caches;
245 }
246 
CreateIsolate(const InternedV8Isolate::Decoder & isolate_proto)247 IsolateId V8Tracker::CreateIsolate(
248     const InternedV8Isolate::Decoder& isolate_proto) {
249   auto v8_isolate = InsertIsolate(isolate_proto);
250   const UniquePid upid = v8_isolate.upid();
251 
252   auto [code_ranges, is_process_wide] =
253       GetIsolateCodeRanges(upid, isolate_proto);
254 
255   PERFETTO_CHECK(isolates_
256                      .Insert(v8_isolate.id(),
257                              is_process_wide
258                                  ? GetOrCreateSharedJitCaches(upid, code_ranges)
259                                  : CreateJitCaches(upid, code_ranges))
260                      .second);
261 
262   return v8_isolate.id();
263 }
264 
InsertIsolate(const InternedV8Isolate::Decoder & isolate)265 tables::V8IsolateTable::ConstRowReference V8Tracker::InsertIsolate(
266     const InternedV8Isolate::Decoder& isolate) {
267   InternedV8Isolate::CodeRange::Decoder code_range(isolate.code_range());
268   return context_->storage->mutable_v8_isolate_table()
269       ->Insert(
270           {context_->process_tracker->GetOrCreateProcess(isolate.pid()),
271            isolate.isolate_id(),
272            static_cast<int64_t>(isolate.embedded_blob_code_start_address()),
273            static_cast<int64_t>(isolate.embedded_blob_code_size()),
274            static_cast<int64_t>(code_range.base_address()),
275            static_cast<int64_t>(code_range.size()),
276            code_range.is_process_wide(),
277            code_range.has_embedded_blob_code_copy_start_address()
278                ? std::make_optional(static_cast<int64_t>(
279                      code_range.embedded_blob_code_copy_start_address()))
280                : std::nullopt})
281       .row_reference;
282 }
283 
InternJsScript(protozero::ConstBytes bytes,IsolateId isolate_id)284 tables::V8JsScriptTable::Id V8Tracker::InternJsScript(
285     protozero::ConstBytes bytes,
286     IsolateId isolate_id) {
287   InternedV8JsScript::Decoder script(bytes);
288 
289   if (auto* id =
290           js_script_index_.Find(std::make_pair(isolate_id, script.script_id()));
291       id) {
292     return *id;
293   }
294 
295   tables::V8JsScriptTable::Row row;
296   row.v8_isolate_id = isolate_id;
297   row.internal_script_id = script.script_id();
298   row.script_type =
299       context_->storage->InternString(JsScriptTypeToString(script.type()));
300   row.name = InternV8String(V8String::Decoder(script.name()));
301   row.source = InternV8String(V8String::Decoder(script.source()));
302 
303   tables::V8JsScriptTable::Id script_id =
304       context_->storage->mutable_v8_js_script_table()->Insert(row).id;
305   js_script_index_.Insert(std::make_pair(isolate_id, script.script_id()),
306                           script_id);
307   return script_id;
308 }
309 
InternWasmScript(protozero::ConstBytes bytes,IsolateId isolate_id)310 tables::V8WasmScriptTable::Id V8Tracker::InternWasmScript(
311     protozero::ConstBytes bytes,
312     IsolateId isolate_id) {
313   InternedV8WasmScript::Decoder script(bytes);
314 
315   if (auto* id = wasm_script_index_.Find(
316           std::make_pair(isolate_id, script.script_id()));
317       id) {
318     return *id;
319   }
320 
321   tables::V8WasmScriptTable::Row row;
322   row.v8_isolate_id = isolate_id;
323   row.internal_script_id = script.script_id();
324   row.url = context_->storage->InternString(script.url());
325   row.wire_bytes_base64 = context_->storage->InternString(base::StringView(
326       base::Base64Encode(script.wire_bytes().data, script.wire_bytes().size)));
327 
328   tables::V8WasmScriptTable::Id script_id =
329       context_->storage->mutable_v8_wasm_script_table()->Insert(row).id;
330   wasm_script_index_.Insert(std::make_pair(isolate_id, script.script_id()),
331                             script_id);
332   return script_id;
333 }
334 
InternJsFunction(protozero::ConstBytes bytes,StringId name,tables::V8JsScriptTable::Id script_id)335 tables::V8JsFunctionTable::Id V8Tracker::InternJsFunction(
336     protozero::ConstBytes bytes,
337     StringId name,
338     tables::V8JsScriptTable::Id script_id) {
339   InternedV8JsFunction::Decoder function(bytes);
340 
341   tables::V8JsFunctionTable::Row row;
342   row.name = name;
343   row.v8_js_script_id = script_id;
344   row.is_toplevel = function.is_toplevel();
345   row.kind =
346       context_->storage->InternString(JsFunctionKindToString(function.kind()));
347   if (function.has_line() && function.has_column()) {
348     row.line = function.line();
349     row.col = function.column();
350   } else if (function.has_byte_offset()) {
351     // TODO(carlscab): Line and column are hard. Offset is in bytes, line and
352     // column are in characters and we potentially have a multi byte encoding
353     // (UTF16). Good luck!
354     row.line = 1;
355     row.col = function.byte_offset();
356   }
357 
358   if (auto* id = js_function_index_.Find(row); id) {
359     return *id;
360   }
361 
362   tables::V8JsFunctionTable::Id function_id =
363       context_->storage->mutable_v8_js_function_table()->Insert(row).id;
364   js_function_index_.Insert(row, function_id);
365   return function_id;
366 }
367 
MaybeFindJitCache(IsolateId isolate_id,AddressRange code_range) const368 JitCache* V8Tracker::MaybeFindJitCache(IsolateId isolate_id,
369                                        AddressRange code_range) const {
370   if (code_range.empty()) {
371     context_->storage->IncrementStats(stats::v8_code_load_missing_code_range);
372     return nullptr;
373   }
374   auto* isolate = isolates_.Find(isolate_id);
375   PERFETTO_CHECK(isolate);
376   if (auto it = isolate->FindRangeThatContains(code_range);
377       it != isolate->end()) {
378     return it->second;
379   }
380 
381   return nullptr;
382 }
383 
FindJitCache(IsolateId isolate_id,AddressRange code_range) const384 JitCache* V8Tracker::FindJitCache(IsolateId isolate_id,
385                                   AddressRange code_range) const {
386   if (code_range.empty()) {
387     context_->storage->IncrementStats(stats::v8_code_load_missing_code_range);
388     return nullptr;
389   }
390   JitCache* cache = MaybeFindJitCache(isolate_id, code_range);
391   if (!cache) {
392     context_->storage->IncrementStats(stats::v8_no_code_range);
393   }
394   return cache;
395 }
396 
AddJsCode(int64_t timestamp,UniqueTid utid,IsolateId isolate_id,tables::V8JsFunctionTable::Id function_id,const V8JsCode::Decoder & code)397 void V8Tracker::AddJsCode(int64_t timestamp,
398                           UniqueTid utid,
399                           IsolateId isolate_id,
400                           tables::V8JsFunctionTable::Id function_id,
401                           const V8JsCode::Decoder& code) {
402   const StringId tier =
403       context_->storage->InternString(JsCodeTierToString(code.tier()));
404 
405   const AddressRange code_range = AddressRange::FromStartAndSize(
406       code.instruction_start(), code.instruction_size_bytes());
407 
408   JitCache* jit_cache = nullptr;
409 
410   if (IsInterpretedCode(code)) {
411     // If --interpreted_frames_native_stack is specified interpreted frames will
412     // also be emitted as native functions.
413     // TODO(carlscab): Add an additional tier to for NATIVE_IGNITION_FRAME. Int
414     // he meantime we can infer that this is the case if we have a hit in the
415     // jit cache. NOte we call MaybeFindJitCache to not log an error if there is
416     // no hit.
417     jit_cache = MaybeFindJitCache(isolate_id, code_range);
418     if (!jit_cache) {
419       context_->storage->mutable_v8_js_code_table()->Insert(
420           {std::nullopt, function_id, tier,
421            context_->storage->InternString(base::StringView(base::Base64Encode(
422                code.bytecode().data, code.bytecode().size)))});
423       return;
424     }
425   } else if (IsNativeCode(code)) {
426     jit_cache = FindJitCache(isolate_id, code_range);
427   } else {
428     context_->storage->IncrementStats(stats::v8_unknown_code_type);
429     return;
430   }
431 
432   if (!jit_cache) {
433     return;
434   }
435 
436   auto function =
437       *context_->storage->v8_js_function_table().FindById(function_id);
438   auto script = *context_->storage->v8_js_script_table().FindById(
439       function.v8_js_script_id());
440   const auto jit_code_id = jit_cache->LoadCode(
441       timestamp, utid, code_range, function.name(),
442       JitCache::SourceLocation{script.name(), function.line().value_or(0)},
443       code.has_machine_code()
444           ? TraceBlobView(TraceBlob::CopyFrom(code.machine_code().data,
445                                               code.machine_code().size))
446           : TraceBlobView());
447 
448   context_->storage->mutable_v8_js_code_table()->Insert(
449       {jit_code_id, function_id, tier});
450 }
451 
AddInternalCode(int64_t timestamp,UniqueTid utid,IsolateId isolate_id,const V8InternalCode::Decoder & code)452 void V8Tracker::AddInternalCode(int64_t timestamp,
453                                 UniqueTid utid,
454                                 IsolateId isolate_id,
455                                 const V8InternalCode::Decoder& code) {
456   const AddressRange code_range = AddressRange::FromStartAndSize(
457       code.instruction_start(), code.instruction_size_bytes());
458   JitCache* const jit_cache = FindJitCache(isolate_id, code_range);
459   if (!jit_cache) {
460     return;
461   }
462 
463   const StringId function_name = context_->storage->InternString(code.name());
464   const StringId type =
465       context_->storage->InternString(InternalCodeTypeToString(code.type()));
466   const auto jit_code_id = jit_cache->LoadCode(
467       timestamp, utid, code_range, function_name, std::nullopt,
468       code.has_machine_code()
469           ? TraceBlobView(TraceBlob::CopyFrom(code.machine_code().data,
470                                               code.machine_code().size))
471           : TraceBlobView());
472 
473   context_->storage->mutable_v8_internal_code_table()->Insert(
474       {jit_code_id, isolate_id, function_name, type});
475 }
476 
AddWasmCode(int64_t timestamp,UniqueTid utid,IsolateId isolate_id,tables::V8WasmScriptTable::Id script_id,const V8WasmCode::Decoder & code)477 void V8Tracker::AddWasmCode(int64_t timestamp,
478                             UniqueTid utid,
479                             IsolateId isolate_id,
480                             tables::V8WasmScriptTable::Id script_id,
481                             const V8WasmCode::Decoder& code) {
482   const AddressRange code_range = AddressRange::FromStartAndSize(
483       code.instruction_start(), code.instruction_size_bytes());
484   JitCache* const jit_cache = FindJitCache(isolate_id, code_range);
485   if (!jit_cache) {
486     return;
487   }
488 
489   const StringId function_name =
490       context_->storage->InternString(code.function_name());
491   const StringId tier =
492       context_->storage->InternString(WasmCodeTierToString(code.tier()));
493 
494   const auto jit_code_id = jit_cache->LoadCode(
495       timestamp, utid, code_range, function_name, std::nullopt,
496       code.has_machine_code()
497           ? TraceBlobView(TraceBlob::CopyFrom(code.machine_code().data,
498                                               code.machine_code().size))
499           : TraceBlobView());
500 
501   context_->storage->mutable_v8_wasm_code_table()->Insert(
502       {jit_code_id, isolate_id, script_id, function_name, tier});
503 }
504 
AddRegExpCode(int64_t timestamp,UniqueTid utid,IsolateId isolate_id,const V8RegExpCode::Decoder & code)505 void V8Tracker::AddRegExpCode(int64_t timestamp,
506                               UniqueTid utid,
507                               IsolateId isolate_id,
508                               const V8RegExpCode::Decoder& code) {
509   const AddressRange code_range = AddressRange::FromStartAndSize(
510       code.instruction_start(), code.instruction_size_bytes());
511   JitCache* const jit_cache = FindJitCache(isolate_id, code_range);
512   if (!jit_cache) {
513     return;
514   }
515 
516   const StringId function_name = context_->storage->InternString("[RegExp]");
517   const StringId pattern = InternV8String(V8String::Decoder(code.pattern()));
518   const auto jit_code_id = jit_cache->LoadCode(
519       timestamp, utid, code_range, function_name, std::nullopt,
520       code.has_machine_code()
521           ? TraceBlobView(TraceBlob::CopyFrom(code.machine_code().data,
522                                               code.machine_code().size))
523           : TraceBlobView());
524 
525   context_->storage->mutable_v8_regexp_code_table()->Insert(
526       {jit_code_id, isolate_id, pattern});
527 }
528 
MoveCode(int64_t timestamp,UniqueTid utid,IsolateId isolate_id,const V8CodeMove::Decoder & code)529 void V8Tracker::MoveCode(int64_t timestamp,
530                          UniqueTid utid,
531                          IsolateId isolate_id,
532                          const V8CodeMove::Decoder& code) {
533   if (!code.has_from_instruction_start_address())
534     return;
535 
536   const AddressRange code_range = AddressRange::FromStartAndSize(
537       code.from_instruction_start_address(), code.instruction_size_bytes());
538   JitCache* const jit_cache = FindJitCache(isolate_id, code_range);
539   if (!jit_cache) {
540     return;
541   }
542 
543   jit_cache->MoveCode(timestamp, utid, code.from_instruction_start_address(),
544                       code.to_instruction_start_address());
545 }
546 
InternV8String(const V8String::Decoder & v8_string)547 StringId V8Tracker::InternV8String(const V8String::Decoder& v8_string) {
548   auto& storage = *context_->storage;
549   if (v8_string.has_latin1()) {
550     return storage.InternString(
551         base::StringView(ConvertLatin1ToUtf8(v8_string.latin1())));
552   }
553 
554   if (v8_string.has_utf16_le()) {
555     return storage.InternString(
556         base::StringView(ConvertUtf16LeToUtf8(v8_string.utf16_le())));
557   }
558 
559   if (v8_string.has_utf16_be()) {
560     return storage.InternString(
561         base::StringView(ConvertUtf16BeToUtf8(v8_string.utf16_be())));
562   }
563   return storage.InternString("");
564 }
565 
566 }  // namespace trace_processor
567 }  // namespace perfetto
568