v8-metrics.h 8.44 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_METRICS_H_
#define V8_METRICS_H_

8 9 10 11 12 13 14
#include <stddef.h>
#include <stdint.h>

#include <vector>

#include "v8-internal.h"      // NOLINT(build/include_directory)
#include "v8-local-handle.h"  // NOLINT(build/include_directory)
15 16

namespace v8 {
17 18 19 20

class Context;
class Isolate;

21 22
namespace metrics {

23
struct GarbageCollectionPhases {
24
  int64_t total_wall_clock_duration_in_us = -1;
25 26 27 28 29 30 31 32 33 34 35 36 37
  int64_t compact_wall_clock_duration_in_us = -1;
  int64_t mark_wall_clock_duration_in_us = -1;
  int64_t sweep_wall_clock_duration_in_us = -1;
  int64_t weak_wall_clock_duration_in_us = -1;
};

struct GarbageCollectionSizes {
  int64_t bytes_before = -1;
  int64_t bytes_after = -1;
  int64_t bytes_freed = -1;
};

struct GarbageCollectionFullCycle {
38
  int reason = -1;
39 40 41 42 43 44 45 46 47 48 49 50
  GarbageCollectionPhases total;
  GarbageCollectionPhases total_cpp;
  GarbageCollectionPhases main_thread;
  GarbageCollectionPhases main_thread_cpp;
  GarbageCollectionPhases main_thread_atomic;
  GarbageCollectionPhases main_thread_atomic_cpp;
  GarbageCollectionPhases main_thread_incremental;
  GarbageCollectionPhases main_thread_incremental_cpp;
  GarbageCollectionSizes objects;
  GarbageCollectionSizes objects_cpp;
  GarbageCollectionSizes memory;
  GarbageCollectionSizes memory_cpp;
51 52 53 54 55 56
  double collection_rate_in_percent = -1.0;
  double collection_rate_cpp_in_percent = -1.0;
  double efficiency_in_bytes_per_us = -1.0;
  double efficiency_cpp_in_bytes_per_us = -1.0;
  double main_thread_efficiency_in_bytes_per_us = -1.0;
  double main_thread_efficiency_cpp_in_bytes_per_us = -1.0;
57 58 59 60 61 62 63 64 65 66 67 68
};

struct GarbageCollectionFullMainThreadIncrementalMark {
  int64_t wall_clock_duration_in_us = -1;
  int64_t cpp_wall_clock_duration_in_us = -1;
};

struct GarbageCollectionFullMainThreadIncrementalSweep {
  int64_t wall_clock_duration_in_us = -1;
  int64_t cpp_wall_clock_duration_in_us = -1;
};

69 70 71
template <typename EventType>
struct GarbageCollectionBatchedEvents {
  std::vector<EventType> events;
72 73
};

74 75 76 77 78 79 80
using GarbageCollectionFullMainThreadBatchedIncrementalMark =
    GarbageCollectionBatchedEvents<
        GarbageCollectionFullMainThreadIncrementalMark>;
using GarbageCollectionFullMainThreadBatchedIncrementalSweep =
    GarbageCollectionBatchedEvents<
        GarbageCollectionFullMainThreadIncrementalSweep>;

81
struct GarbageCollectionYoungCycle {
82
  int reason = -1;
83 84
  int64_t total_wall_clock_duration_in_us = -1;
  int64_t main_thread_wall_clock_duration_in_us = -1;
85 86 87 88 89 90 91 92 93 94 95
  double collection_rate_in_percent = -1.0;
  double efficiency_in_bytes_per_us = -1.0;
  double main_thread_efficiency_in_bytes_per_us = -1.0;
#if defined(CPPGC_YOUNG_GENERATION)
  GarbageCollectionPhases total_cpp;
  GarbageCollectionSizes objects_cpp;
  GarbageCollectionSizes memory_cpp;
  double collection_rate_cpp_in_percent = -1.0;
  double efficiency_cpp_in_bytes_per_us = -1.0;
  double main_thread_efficiency_cpp_in_bytes_per_us = -1.0;
#endif  // defined(CPPGC_YOUNG_GENERATION)
96 97
};

98 99 100 101 102 103
struct WasmModuleDecoded {
  bool async = false;
  bool streamed = false;
  bool success = false;
  size_t module_size_in_bytes = 0;
  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 109 110 111 112 113 114 115 116
};

struct WasmModuleCompiled {
  bool async = false;
  bool streamed = false;
  bool cached = false;
  bool deserialized = false;
  bool lazy = false;
  bool success = false;
  size_t code_size_in_bytes = 0;
  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 122 123 124
};

struct WasmModuleInstantiated {
  bool async = false;
  bool success = false;
  size_t imported_function_count = 0;
125
  int64_t wall_clock_duration_in_us = -1;
126 127 128 129 130
};

struct WasmModuleTieredUp {
  bool lazy = false;
  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 136 137 138
};

struct WasmModulesPerIsolate {
  size_t count = 0;
};

139 140 141 142 143 144 145 146 147 148
#define V8_MAIN_THREAD_METRICS_EVENTS(V)                    \
  V(GarbageCollectionFullCycle)                             \
  V(GarbageCollectionFullMainThreadIncrementalMark)         \
  V(GarbageCollectionFullMainThreadBatchedIncrementalMark)  \
  V(GarbageCollectionFullMainThreadIncrementalSweep)        \
  V(GarbageCollectionFullMainThreadBatchedIncrementalSweep) \
  V(GarbageCollectionYoungCycle)                            \
  V(WasmModuleDecoded)                                      \
  V(WasmModuleCompiled)                                     \
  V(WasmModuleInstantiated)                                 \
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
  V(WasmModuleTieredUp)

#define V8_THREAD_SAFE_METRICS_EVENTS(V) V(WasmModulesPerIsolate)

/**
 * This class serves as a base class for recording event-based metrics in V8.
 * There a two kinds of metrics, those which are expected to be thread-safe and
 * whose implementation is required to fulfill this requirement and those whose
 * implementation does not have that requirement and only needs to be
 * executable on the main thread. If such an event is triggered from a
 * background thread, it will be delayed and executed by the foreground task
 * runner.
 *
 * The thread-safe events are listed in the V8_THREAD_SAFE_METRICS_EVENTS
 * macro above while the main thread event are listed in
 * V8_MAIN_THREAD_METRICS_EVENTS above. For the former, a virtual method
 * AddMainThreadEvent(const E& event, v8::Context::Token token) will be
 * generated and for the latter AddThreadSafeEvent(const E& event).
 *
 * Thread-safe events are not allowed to access the context and therefore do
169 170 171 172
 * not carry a context ID with them. These IDs can be generated using
 * Recorder::GetContextId() and the ID will be valid throughout the lifetime
 * of the isolate. It is not guaranteed that the ID will still resolve to
 * a valid context using Recorder::GetContext() at the time the metric is
173 174 175 176 177 178
 * recorded. In this case, an empty handle will be returned.
 *
 * The embedder is expected to call v8::Isolate::SetMetricsRecorder()
 * providing its implementation and have the virtual methods overwritten
 * for the events it cares about.
 */
179
class V8_EXPORT Recorder {
180
 public:
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
  // A unique identifier for a context in this Isolate.
  // It is guaranteed to not be reused throughout the lifetime of the Isolate.
  class ContextId {
   public:
    ContextId() : id_(kEmptyId) {}

    bool IsEmpty() const { return id_ == kEmptyId; }
    static const ContextId Empty() { return ContextId{kEmptyId}; }

    bool operator==(const ContextId& other) const { return id_ == other.id_; }
    bool operator!=(const ContextId& other) const { return id_ != other.id_; }

   private:
    friend class ::v8::Context;
    friend class ::v8::internal::Isolate;

    explicit ContextId(uintptr_t id) : id_(id) {}

    static constexpr uintptr_t kEmptyId = 0;
    uintptr_t id_;
  };

203 204 205
  virtual ~Recorder() = default;

#define ADD_MAIN_THREAD_EVENT(E) \
206
  virtual void AddMainThreadEvent(const E& event, ContextId context_id) {}
207 208 209 210 211 212 213 214 215
  V8_MAIN_THREAD_METRICS_EVENTS(ADD_MAIN_THREAD_EVENT)
#undef ADD_MAIN_THREAD_EVENT

#define ADD_THREAD_SAFE_EVENT(E) \
  virtual void AddThreadSafeEvent(const E& event) {}
  V8_THREAD_SAFE_METRICS_EVENTS(ADD_THREAD_SAFE_EVENT)
#undef ADD_THREAD_SAFE_EVENT

  virtual void NotifyIsolateDisposal() {}
216 217 218 219 220 221

  // Return the context with the given id or an empty handle if the context
  // was already garbage collected.
  static MaybeLocal<Context> GetContext(Isolate* isolate, ContextId id);
  // Return the unique id corresponding to the given context.
  static ContextId GetContextId(Local<Context> context);
222 223
};

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
/**
 * Experimental API intended for the LongTasks UKM (crbug.com/1173527).
 * The Reset() method should be called at the start of a potential
 * long task. The Get() method returns durations of V8 work that
 * happened during the task.
 *
 * This API is experimental and may be removed/changed in the future.
 */
struct V8_EXPORT LongTaskStats {
  /**
   * Resets durations of V8 work for the new task.
   */
  V8_INLINE static void Reset(Isolate* isolate) {
    v8::internal::Internals::IncrementLongTasksStatsCounter(isolate);
  }

  /**
   * Returns durations of V8 work that happened since the last Reset().
   */
  static LongTaskStats Get(Isolate* isolate);

  int64_t gc_full_atomic_wall_clock_duration_us = 0;
  int64_t gc_full_incremental_wall_clock_duration_us = 0;
  int64_t gc_young_wall_clock_duration_us = 0;
248 249
  // Only collected with --slow-histograms
  int64_t v8_execute_us = 0;
250 251
};

252 253 254 255
}  // namespace metrics
}  // namespace v8

#endif  // V8_METRICS_H_