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