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 
17 namespace v8 {
18 
19 class Context;
20 class Isolate;
21 
22 namespace metrics {
23 
24 struct 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 
32 struct GarbageCollectionSizes {
33   int64_t bytes_before = -1;
34   int64_t bytes_after = -1;
35   int64_t bytes_freed = -1;
36 };
37 
38 struct 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 
60 struct GarbageCollectionFullMainThreadIncrementalMark {
61   int64_t wall_clock_duration_in_us = -1;
62   int64_t cpp_wall_clock_duration_in_us = -1;
63 };
64 
65 struct GarbageCollectionFullMainThreadIncrementalSweep {
66   int64_t wall_clock_duration_in_us = -1;
67   int64_t cpp_wall_clock_duration_in_us = -1;
68 };
69 
70 template <typename EventType>
71 struct GarbageCollectionBatchedEvents {
72   std::vector<EventType> events;
73 };
74 
75 using GarbageCollectionFullMainThreadBatchedIncrementalMark =
76     GarbageCollectionBatchedEvents<
77         GarbageCollectionFullMainThreadIncrementalMark>;
78 using GarbageCollectionFullMainThreadBatchedIncrementalSweep =
79     GarbageCollectionBatchedEvents<
80         GarbageCollectionFullMainThreadIncrementalSweep>;
81 
82 struct 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 
99 struct WasmModuleDecoded {
100   WasmModuleDecoded() = default;
WasmModuleDecodedv8::metrics::WasmModuleDecoded101   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 
119 struct WasmModuleCompiled {
120   WasmModuleCompiled() = default;
121 
WasmModuleCompiledv8::metrics::WasmModuleCompiled122   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 
147 struct 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 
154 struct 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  */
171 class 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:
ContextId()177     ContextId() : id_(kEmptyId) {}
178 
IsEmpty() const179     bool IsEmpty() const { return id_ == kEmptyId; }
Empty()180     static const ContextId Empty() { return ContextId{kEmptyId}; }
181 
operator ==(const ContextId& other) const182     bool operator==(const ContextId& other) const { return id_ == other.id_; }
operator !=(const ContextId& other) const183     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 
ContextId(uintptr_t id)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 
NotifyIsolateDisposal()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  */
240 struct V8_EXPORT LongTaskStats {
241   /**
242    * Resets durations of V8 work for the new task.
243    */
Resetv8::metrics::LongTaskStats244   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