• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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