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