1 // Copyright 2020 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_METRICS_H_ 6 #define V8_METRICS_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <vector> 12 13 #include "v8-internal.h" // NOLINT(build/include_directory) 14 #include "v8-local-handle.h" // NOLINT(build/include_directory) 15 16 namespace v8 { 17 18 class Context; 19 class Isolate; 20 21 namespace metrics { 22 23 struct GarbageCollectionPhases { 24 int64_t total_wall_clock_duration_in_us = -1; 25 int64_t compact_wall_clock_duration_in_us = -1; 26 int64_t mark_wall_clock_duration_in_us = -1; 27 int64_t sweep_wall_clock_duration_in_us = -1; 28 int64_t weak_wall_clock_duration_in_us = -1; 29 }; 30 31 struct GarbageCollectionSizes { 32 int64_t bytes_before = -1; 33 int64_t bytes_after = -1; 34 int64_t bytes_freed = -1; 35 }; 36 37 struct GarbageCollectionFullCycle { 38 int reason = -1; 39 GarbageCollectionPhases total; 40 GarbageCollectionPhases total_cpp; 41 GarbageCollectionPhases main_thread; 42 GarbageCollectionPhases main_thread_cpp; 43 GarbageCollectionPhases main_thread_atomic; 44 GarbageCollectionPhases main_thread_atomic_cpp; 45 GarbageCollectionPhases main_thread_incremental; 46 GarbageCollectionPhases main_thread_incremental_cpp; 47 GarbageCollectionSizes objects; 48 GarbageCollectionSizes objects_cpp; 49 GarbageCollectionSizes memory; 50 GarbageCollectionSizes memory_cpp; 51 double collection_rate_in_percent = -1.0; 52 double collection_rate_cpp_in_percent = -1.0; 53 double efficiency_in_bytes_per_us = -1.0; 54 double efficiency_cpp_in_bytes_per_us = -1.0; 55 double main_thread_efficiency_in_bytes_per_us = -1.0; 56 double main_thread_efficiency_cpp_in_bytes_per_us = -1.0; 57 }; 58 59 struct GarbageCollectionFullMainThreadIncrementalMark { 60 int64_t wall_clock_duration_in_us = -1; 61 int64_t cpp_wall_clock_duration_in_us = -1; 62 }; 63 64 struct GarbageCollectionFullMainThreadIncrementalSweep { 65 int64_t wall_clock_duration_in_us = -1; 66 int64_t cpp_wall_clock_duration_in_us = -1; 67 }; 68 69 template <typename EventType> 70 struct GarbageCollectionBatchedEvents { 71 std::vector<EventType> events; 72 }; 73 74 using GarbageCollectionFullMainThreadBatchedIncrementalMark = 75 GarbageCollectionBatchedEvents< 76 GarbageCollectionFullMainThreadIncrementalMark>; 77 using GarbageCollectionFullMainThreadBatchedIncrementalSweep = 78 GarbageCollectionBatchedEvents< 79 GarbageCollectionFullMainThreadIncrementalSweep>; 80 81 struct GarbageCollectionYoungCycle { 82 int reason = -1; 83 int64_t total_wall_clock_duration_in_us = -1; 84 int64_t main_thread_wall_clock_duration_in_us = -1; 85 double collection_rate_in_percent = -1.0; 86 double efficiency_in_bytes_per_us = -1.0; 87 double main_thread_efficiency_in_bytes_per_us = -1.0; 88 #if defined(CPPGC_YOUNG_GENERATION) 89 GarbageCollectionPhases total_cpp; 90 GarbageCollectionSizes objects_cpp; 91 GarbageCollectionSizes memory_cpp; 92 double collection_rate_cpp_in_percent = -1.0; 93 double efficiency_cpp_in_bytes_per_us = -1.0; 94 double main_thread_efficiency_cpp_in_bytes_per_us = -1.0; 95 #endif // defined(CPPGC_YOUNG_GENERATION) 96 }; 97 98 struct WasmModuleDecoded { 99 bool async = false; 100 bool streamed = false; 101 bool success = false; 102 size_t module_size_in_bytes = 0; 103 size_t function_count = 0; 104 int64_t wall_clock_duration_in_us = -1; 105 int64_t cpu_duration_in_us = -1; 106 }; 107 108 struct WasmModuleCompiled { 109 bool async = false; 110 bool streamed = false; 111 bool cached = false; 112 bool deserialized = false; 113 bool lazy = false; 114 bool success = false; 115 size_t code_size_in_bytes = 0; 116 size_t liftoff_bailout_count = 0; 117 int64_t wall_clock_duration_in_us = -1; 118 int64_t cpu_duration_in_us = -1; 119 }; 120 121 struct WasmModuleInstantiated { 122 bool async = false; 123 bool success = false; 124 size_t imported_function_count = 0; 125 int64_t wall_clock_duration_in_us = -1; 126 }; 127 128 struct WasmModuleTieredUp { 129 bool lazy = false; 130 size_t code_size_in_bytes = 0; 131 int64_t wall_clock_duration_in_us = -1; 132 int64_t cpu_duration_in_us = -1; 133 }; 134 135 struct WasmModulesPerIsolate { 136 size_t count = 0; 137 }; 138 139 #define V8_MAIN_THREAD_METRICS_EVENTS(V) \ 140 V(GarbageCollectionFullCycle) \ 141 V(GarbageCollectionFullMainThreadIncrementalMark) \ 142 V(GarbageCollectionFullMainThreadBatchedIncrementalMark) \ 143 V(GarbageCollectionFullMainThreadIncrementalSweep) \ 144 V(GarbageCollectionFullMainThreadBatchedIncrementalSweep) \ 145 V(GarbageCollectionYoungCycle) \ 146 V(WasmModuleDecoded) \ 147 V(WasmModuleCompiled) \ 148 V(WasmModuleInstantiated) \ 149 V(WasmModuleTieredUp) 150 151 #define V8_THREAD_SAFE_METRICS_EVENTS(V) V(WasmModulesPerIsolate) 152 153 /** 154 * This class serves as a base class for recording event-based metrics in V8. 155 * There a two kinds of metrics, those which are expected to be thread-safe and 156 * whose implementation is required to fulfill this requirement and those whose 157 * implementation does not have that requirement and only needs to be 158 * executable on the main thread. If such an event is triggered from a 159 * background thread, it will be delayed and executed by the foreground task 160 * runner. 161 * 162 * The thread-safe events are listed in the V8_THREAD_SAFE_METRICS_EVENTS 163 * macro above while the main thread event are listed in 164 * V8_MAIN_THREAD_METRICS_EVENTS above. For the former, a virtual method 165 * AddMainThreadEvent(const E& event, v8::Context::Token token) will be 166 * generated and for the latter AddThreadSafeEvent(const E& event). 167 * 168 * Thread-safe events are not allowed to access the context and therefore do 169 * not carry a context ID with them. These IDs can be generated using 170 * Recorder::GetContextId() and the ID will be valid throughout the lifetime 171 * of the isolate. It is not guaranteed that the ID will still resolve to 172 * a valid context using Recorder::GetContext() at the time the metric is 173 * recorded. In this case, an empty handle will be returned. 174 * 175 * The embedder is expected to call v8::Isolate::SetMetricsRecorder() 176 * providing its implementation and have the virtual methods overwritten 177 * for the events it cares about. 178 */ 179 class V8_EXPORT Recorder { 180 public: 181 // A unique identifier for a context in this Isolate. 182 // It is guaranteed to not be reused throughout the lifetime of the Isolate. 183 class ContextId { 184 public: ContextId()185 ContextId() : id_(kEmptyId) {} 186 IsEmpty()187 bool IsEmpty() const { return id_ == kEmptyId; } Empty()188 static const ContextId Empty() { return ContextId{kEmptyId}; } 189 190 bool operator==(const ContextId& other) const { return id_ == other.id_; } 191 bool operator!=(const ContextId& other) const { return id_ != other.id_; } 192 193 private: 194 friend class ::v8::Context; 195 friend class ::v8::internal::Isolate; 196 ContextId(uintptr_t id)197 explicit ContextId(uintptr_t id) : id_(id) {} 198 199 static constexpr uintptr_t kEmptyId = 0; 200 uintptr_t id_; 201 }; 202 203 virtual ~Recorder() = default; 204 205 #define ADD_MAIN_THREAD_EVENT(E) \ 206 virtual void AddMainThreadEvent(const E& event, ContextId context_id) {} 207 V8_MAIN_THREAD_METRICS_EVENTS(ADD_MAIN_THREAD_EVENT) 208 #undef ADD_MAIN_THREAD_EVENT 209 210 #define ADD_THREAD_SAFE_EVENT(E) \ 211 virtual void AddThreadSafeEvent(const E& event) {} V8_THREAD_SAFE_METRICS_EVENTS(ADD_THREAD_SAFE_EVENT)212 V8_THREAD_SAFE_METRICS_EVENTS(ADD_THREAD_SAFE_EVENT) 213 #undef ADD_THREAD_SAFE_EVENT 214 215 virtual void NotifyIsolateDisposal() {} 216 217 // Return the context with the given id or an empty handle if the context 218 // was already garbage collected. 219 static MaybeLocal<Context> GetContext(Isolate* isolate, ContextId id); 220 // Return the unique id corresponding to the given context. 221 static ContextId GetContextId(Local<Context> context); 222 }; 223 224 /** 225 * Experimental API intended for the LongTasks UKM (crbug.com/1173527). 226 * The Reset() method should be called at the start of a potential 227 * long task. The Get() method returns durations of V8 work that 228 * happened during the task. 229 * 230 * This API is experimental and may be removed/changed in the future. 231 */ 232 struct V8_EXPORT LongTaskStats { 233 /** 234 * Resets durations of V8 work for the new task. 235 */ ResetLongTaskStats236 V8_INLINE static void Reset(Isolate* isolate) { 237 v8::internal::Internals::IncrementLongTasksStatsCounter(isolate); 238 } 239 240 /** 241 * Returns durations of V8 work that happened since the last Reset(). 242 */ 243 static LongTaskStats Get(Isolate* isolate); 244 245 int64_t gc_full_atomic_wall_clock_duration_us = 0; 246 int64_t gc_full_incremental_wall_clock_duration_us = 0; 247 int64_t gc_young_wall_clock_duration_us = 0; 248 // Only collected with --slow-histograms 249 int64_t v8_execute_us = 0; 250 }; 251 252 } // namespace metrics 253 } // namespace v8 254 255 #endif // V8_METRICS_H_ 256