• 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 #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   void MoveCode(int64_t timestamp,
91                 UniqueTid utid,
92                 IsolateId v8_isolate_id,
93                 const protos::pbzero::V8CodeMove::Decoder& code_move);
94 
95  private:
96   struct JsFunctionHash {
operatorJsFunctionHash97     size_t operator()(const tables::V8JsFunctionTable::Row& v) const {
98       return static_cast<size_t>(base::Hasher::Combine(
99           v.name.raw_id(), v.v8_js_script_id.value, v.is_toplevel,
100           v.kind.raw_id(), v.line.value_or(0), v.col.value_or(0)));
101     }
102   };
103 
104   struct IsolateCodeRanges {
105     AddressSet heap_code;
106     std::optional<AddressRange> embedded_blob;
107 
108     bool operator==(const IsolateCodeRanges& o) const {
109       return heap_code == o.heap_code && embedded_blob == o.embedded_blob;
110     }
111   };
112 
113   struct SharedCodeRanges {
114     IsolateCodeRanges code_ranges;
115     AddressRangeMap<JitCache*> jit_caches;
116   };
117 
118   // V8 internal isolate_id and upid uniquely identify an isolate in a trace.
119   struct IsolateKey {
120     struct Hasher {
operatorIsolateKey::Hasher121       size_t operator()(const IsolateKey& v) const {
122         return base::Hasher::Combine(v.upid, v.isolate_id);
123       }
124     };
125 
126     bool operator==(const IsolateKey& other) const {
127       return upid == other.upid && isolate_id == other.isolate_id;
128     }
129 
130     bool operator!=(const IsolateKey& other) const { return !(*this == other); }
131     UniquePid upid;
132     int32_t isolate_id;
133   };
134 
135   struct ScriptIndexHash {
operatorScriptIndexHash136     size_t operator()(const std::pair<IsolateId, int32_t>& v) const {
137       return static_cast<size_t>(
138           base::Hasher::Combine(v.first.value, v.second));
139     }
140   };
141 
142   explicit V8Tracker(TraceProcessorContext* context);
143 
144   StringId InternV8String(const protos::pbzero::V8String::Decoder& v8_string);
145 
146   tables::V8IsolateTable::ConstRowReference InsertIsolate(
147       const protos::pbzero::InternedV8Isolate::Decoder& isolate);
148 
149   IsolateId CreateIsolate(
150       const protos::pbzero::InternedV8Isolate::Decoder& isolate);
151 
152   // Find JitCache that fully contains the given range. Returns null if not
153   // found and updates error counter.
154   JitCache* FindJitCache(IsolateId isolate_id, AddressRange code_range) const;
155   // Same as `FindJitCache` but error counter is not updated if no cache is
156   // found.
157   JitCache* MaybeFindJitCache(IsolateId isolate_id,
158                               AddressRange code_range) const;
159 
160   UserMemoryMapping* FindEmbeddedBlobMapping(
161       UniquePid upid,
162       AddressRange embedded_blob_code) const;
163 
164   std::pair<IsolateCodeRanges, bool> GetIsolateCodeRanges(
165       UniquePid upid,
166       const protos::pbzero::InternedV8Isolate::Decoder& isolate);
167   AddressRangeMap<JitCache*> GetOrCreateSharedJitCaches(
168       UniquePid upid,
169       const IsolateCodeRanges& code_ranges);
170   AddressRangeMap<JitCache*> CreateJitCaches(
171       UniquePid upid,
172       const IsolateCodeRanges& code_ranges);
173 
174   TraceProcessorContext* const context_;
175 
176   base::FlatHashMap<IsolateId, AddressRangeMap<JitCache*>> isolates_;
177 
178   // Multiple isolates in the same process might share the code. Keep track of
179   // those here.
180   base::FlatHashMap<UniquePid, SharedCodeRanges> shared_code_ranges_;
181 
182   base::FlatHashMap<IsolateKey, std::optional<IsolateId>, IsolateKey::Hasher>
183       isolate_index_;
184   base::FlatHashMap<std::pair<IsolateId, int32_t>,
185                     tables::V8JsScriptTable::Id,
186                     ScriptIndexHash>
187       js_script_index_;
188   base::FlatHashMap<std::pair<IsolateId, int32_t>,
189                     tables::V8WasmScriptTable::Id,
190                     ScriptIndexHash>
191       wasm_script_index_;
192   base::FlatHashMap<tables::V8JsFunctionTable::Row,
193                     tables::V8JsFunctionTable::Id,
194                     JsFunctionHash>
195       js_function_index_;
196 };
197 
198 }  // namespace trace_processor
199 }  // namespace perfetto
200 
201 #endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_V8_TRACKER_H_
202