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