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