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