xref: /third_party/node/deps/v8/include/v8-metrics.h (revision 1cb0ef41)
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
16namespace v8 {
17
18class Context;
19class Isolate;
20
21namespace metrics {
22
23struct 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
31struct GarbageCollectionSizes {
32  int64_t bytes_before = -1;
33  int64_t bytes_after = -1;
34  int64_t bytes_freed = -1;
35};
36
37struct 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
59struct GarbageCollectionFullMainThreadIncrementalMark {
60  int64_t wall_clock_duration_in_us = -1;
61  int64_t cpp_wall_clock_duration_in_us = -1;
62};
63
64struct GarbageCollectionFullMainThreadIncrementalSweep {
65  int64_t wall_clock_duration_in_us = -1;
66  int64_t cpp_wall_clock_duration_in_us = -1;
67};
68
69template <typename EventType>
70struct GarbageCollectionBatchedEvents {
71  std::vector<EventType> events;
72};
73
74using GarbageCollectionFullMainThreadBatchedIncrementalMark =
75    GarbageCollectionBatchedEvents<
76        GarbageCollectionFullMainThreadIncrementalMark>;
77using GarbageCollectionFullMainThreadBatchedIncrementalSweep =
78    GarbageCollectionBatchedEvents<
79        GarbageCollectionFullMainThreadIncrementalSweep>;
80
81struct 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
98struct 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
108struct 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
121struct 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
128struct 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
135struct 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 */
179class 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:
185    ContextId() : id_(kEmptyId) {}
186
187    bool IsEmpty() const { return id_ == kEmptyId; }
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
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) {}
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 */
232struct V8_EXPORT LongTaskStats {
233  /**
234   * Resets durations of V8 work for the new task.
235   */
236  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