xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/proto/v8_tracker.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_TRACKER_H_
18 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_TRACKER_H_
19 
20 #include <cstddef>
21 #include <cstdint>
22 #include <memory>
23 #include <optional>
24 
25 #include "perfetto/ext/base/flat_hash_map.h"
26 #include "perfetto/ext/base/hash.h"
27 #include "perfetto/protozero/field.h"
28 #include "protos/perfetto/trace/chrome/v8.pbzero.h"
29 #include "src/trace_processor/importers/common/address_range.h"
30 #include "src/trace_processor/importers/proto/jit_tracker.h"
31 #include "src/trace_processor/storage/trace_storage.h"
32 #include "src/trace_processor/tables/v8_tables_py.h"
33 #include "src/trace_processor/types/destructible.h"
34 #include "src/trace_processor/types/trace_processor_context.h"
35 
36 namespace perfetto {
37 namespace trace_processor {
38 
39 class TraceStorage;
40 class UserMemoryMapping;
41 
42 using IsolateId = tables::V8IsolateTable::Id;
43 
44 // Keeps track of V8 related objects.
45 class V8Tracker : public Destructible {
46  public:
GetOrCreate(TraceProcessorContext * context)47   static V8Tracker* GetOrCreate(TraceProcessorContext* context) {
48     if (!context->v8_tracker) {
49       context->v8_tracker.reset(new V8Tracker(context));
50     }
51     return static_cast<V8Tracker*>(context->v8_tracker.get());
52   }
53 
54   ~V8Tracker() override;
55 
56   // Might return `std::nullopt` if we can not create an isolate because it has
57   // no code range (we do not support this yet).
58   std::optional<IsolateId> InternIsolate(protozero::ConstBytes bytes);
59   tables::V8JsScriptTable::Id InternJsScript(protozero::ConstBytes bytes,
60                                              IsolateId isolate_id);
61   tables::V8WasmScriptTable::Id InternWasmScript(protozero::ConstBytes bytes,
62                                                  IsolateId isolate_id);
63   tables::V8JsFunctionTable::Id InternJsFunction(
64       protozero::ConstBytes bytes,
65       StringId name,
66       tables::V8JsScriptTable::Id script_id);
67 
68   void AddJsCode(int64_t timestamp,
69                  UniqueTid utid,
70                  IsolateId isolate_id,
71                  tables::V8JsFunctionTable::Id function_id,
72                  const protos::pbzero::V8JsCode::Decoder& code);
73 
74   void AddInternalCode(int64_t timestamp,
75                        UniqueTid utid,
76                        IsolateId v8_isolate_id,
77                        const protos::pbzero::V8InternalCode::Decoder& code);
78 
79   void AddWasmCode(int64_t timestamp,
80                    UniqueTid utid,
81                    IsolateId isolate_id,
82                    tables::V8WasmScriptTable::Id script_id,
83                    const protos::pbzero::V8WasmCode::Decoder& code);
84 
85   void AddRegExpCode(int64_t timestamp,
86                      UniqueTid utid,
87                      IsolateId v8_isolate_id,
88                      const protos::pbzero::V8RegExpCode::Decoder& code);
89 
90  private:
91   struct JsFunctionHash {
operatorJsFunctionHash92     size_t operator()(const tables::V8JsFunctionTable::Row& v) const {
93       return static_cast<size_t>(base::Hasher::Combine(
94           v.name.raw_id(), v.v8_js_script_id.value, v.is_toplevel,
95           v.kind.raw_id(), v.line.value_or(0), v.col.value_or(0)));
96     }
97   };
98 
99   struct IsolateCodeRanges {
100     AddressSet heap_code;
101     std::optional<AddressRange> embedded_blob;
102 
103     bool operator==(const IsolateCodeRanges& o) const {
104       return heap_code == o.heap_code && embedded_blob == o.embedded_blob;
105     }
106   };
107 
108   struct SharedCodeRanges {
109     IsolateCodeRanges code_ranges;
110     AddressRangeMap<JitCache*> jit_caches;
111   };
112 
113   // V8 internal isolate_id and upid uniquely identify an isolate in a trace.
114   struct IsolateKey {
115     struct Hasher {
operatorIsolateKey::Hasher116       size_t operator()(const IsolateKey& v) const {
117         return base::Hasher::Combine(v.upid, v.isolate_id);
118       }
119     };
120 
121     bool operator==(const IsolateKey& other) const {
122       return upid == other.upid && isolate_id == other.isolate_id;
123     }
124 
125     bool operator!=(const IsolateKey& other) const { return !(*this == other); }
126     UniquePid upid;
127     int32_t isolate_id;
128   };
129 
130   struct ScriptIndexHash {
operatorScriptIndexHash131     size_t operator()(const std::pair<IsolateId, int32_t>& v) const {
132       return static_cast<size_t>(
133           base::Hasher::Combine(v.first.value, v.second));
134     }
135   };
136 
137   explicit V8Tracker(TraceProcessorContext* context);
138 
139   StringId InternV8String(const protos::pbzero::V8String::Decoder& v8_string);
140 
141   tables::V8IsolateTable::ConstRowReference InsertIsolate(
142       const protos::pbzero::InternedV8Isolate::Decoder& isolate);
143 
144   IsolateId CreateIsolate(
145       const protos::pbzero::InternedV8Isolate::Decoder& isolate);
146 
147   // Find JitCache that fully contains the given range. Returns null if not
148   // found and updates error counter.
149   JitCache* FindJitCache(IsolateId isolate_id, AddressRange code_range) const;
150   // Same as `FindJitCache` but error counter is not updated if no cache is
151   // found.
152   JitCache* MaybeFindJitCache(IsolateId isolate_id,
153                               AddressRange code_range) const;
154 
155   UserMemoryMapping* FindEmbeddedBlobMapping(
156       UniquePid upid,
157       AddressRange embedded_blob_code) const;
158 
159   std::pair<IsolateCodeRanges, bool> GetIsolateCodeRanges(
160       UniquePid upid,
161       const protos::pbzero::InternedV8Isolate::Decoder& isolate);
162   AddressRangeMap<JitCache*> GetOrCreateSharedJitCaches(
163       UniquePid upid,
164       const IsolateCodeRanges& code_ranges);
165   AddressRangeMap<JitCache*> CreateJitCaches(
166       UniquePid upid,
167       const IsolateCodeRanges& code_ranges);
168 
169   TraceProcessorContext* const context_;
170 
171   base::FlatHashMap<IsolateId, AddressRangeMap<JitCache*>> isolates_;
172 
173   // Multiple isolates in the same process might share the code. Keep track of
174   // those here.
175   base::FlatHashMap<UniquePid, SharedCodeRanges> shared_code_ranges_;
176 
177   base::FlatHashMap<IsolateKey, std::optional<IsolateId>, IsolateKey::Hasher>
178       isolate_index_;
179   base::FlatHashMap<std::pair<IsolateId, int32_t>,
180                     tables::V8JsScriptTable::Id,
181                     ScriptIndexHash>
182       js_script_index_;
183   base::FlatHashMap<std::pair<IsolateId, int32_t>,
184                     tables::V8WasmScriptTable::Id,
185                     ScriptIndexHash>
186       wasm_script_index_;
187   base::FlatHashMap<tables::V8JsFunctionTable::Row,
188                     tables::V8JsFunctionTable::Id,
189                     JsFunctionHash>
190       js_function_index_;
191 };
192 
193 }  // namespace trace_processor
194 }  // namespace perfetto
195 
196 #endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_TRACKER_H_
197