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