11cb0ef41Sopenharmony_ci// Copyright 2020 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#ifndef V8_HEAP_CPPGC_STATS_COLLECTOR_H_
61cb0ef41Sopenharmony_ci#define V8_HEAP_CPPGC_STATS_COLLECTOR_H_
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ci#include <stddef.h>
91cb0ef41Sopenharmony_ci#include <stdint.h>
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_ci#include <atomic>
121cb0ef41Sopenharmony_ci#include <vector>
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ci#include "include/cppgc/platform.h"
151cb0ef41Sopenharmony_ci#include "src/base/logging.h"
161cb0ef41Sopenharmony_ci#include "src/base/macros.h"
171cb0ef41Sopenharmony_ci#include "src/base/platform/time.h"
181cb0ef41Sopenharmony_ci#include "src/heap/cppgc/garbage-collector.h"
191cb0ef41Sopenharmony_ci#include "src/heap/cppgc/metric-recorder.h"
201cb0ef41Sopenharmony_ci#include "src/heap/cppgc/trace-event.h"
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_cinamespace cppgc {
231cb0ef41Sopenharmony_cinamespace internal {
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ci// Histogram scopes contribute to histogram as well as to traces and metrics.
261cb0ef41Sopenharmony_ci// Other scopes contribute only to traces and metrics.
271cb0ef41Sopenharmony_ci#define CPPGC_FOR_ALL_HISTOGRAM_SCOPES(V) \
281cb0ef41Sopenharmony_ci  V(AtomicMark)                           \
291cb0ef41Sopenharmony_ci  V(AtomicWeak)                           \
301cb0ef41Sopenharmony_ci  V(AtomicCompact)                        \
311cb0ef41Sopenharmony_ci  V(AtomicSweep)                          \
321cb0ef41Sopenharmony_ci  V(IncrementalMark)                      \
331cb0ef41Sopenharmony_ci  V(IncrementalSweep)
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci#define CPPGC_FOR_ALL_SCOPES(V)             \
361cb0ef41Sopenharmony_ci  V(MarkIncrementalStart)                   \
371cb0ef41Sopenharmony_ci  V(MarkIncrementalFinalize)                \
381cb0ef41Sopenharmony_ci  V(MarkAtomicPrologue)                     \
391cb0ef41Sopenharmony_ci  V(MarkAtomicEpilogue)                     \
401cb0ef41Sopenharmony_ci  V(MarkTransitiveClosure)                  \
411cb0ef41Sopenharmony_ci  V(MarkTransitiveClosureWithDeadline)      \
421cb0ef41Sopenharmony_ci  V(MarkFlushEphemerons)                    \
431cb0ef41Sopenharmony_ci  V(MarkOnAllocation)                       \
441cb0ef41Sopenharmony_ci  V(MarkProcessBailOutObjects)              \
451cb0ef41Sopenharmony_ci  V(MarkProcessMarkingWorklist)             \
461cb0ef41Sopenharmony_ci  V(MarkProcessWriteBarrierWorklist)        \
471cb0ef41Sopenharmony_ci  V(MarkProcessNotFullyconstructedWorklist) \
481cb0ef41Sopenharmony_ci  V(MarkProcessEphemerons)                  \
491cb0ef41Sopenharmony_ci  V(MarkVisitRoots)                         \
501cb0ef41Sopenharmony_ci  V(MarkVisitNotFullyConstructedObjects)    \
511cb0ef41Sopenharmony_ci  V(MarkVisitPersistents)                   \
521cb0ef41Sopenharmony_ci  V(MarkVisitCrossThreadPersistents)        \
531cb0ef41Sopenharmony_ci  V(MarkVisitStack)                         \
541cb0ef41Sopenharmony_ci  V(MarkVisitRememberedSets)                \
551cb0ef41Sopenharmony_ci  V(SweepInvokePreFinalizers)               \
561cb0ef41Sopenharmony_ci  V(SweepIdleStep)                          \
571cb0ef41Sopenharmony_ci  V(SweepInTask)                            \
581cb0ef41Sopenharmony_ci  V(SweepOnAllocation)                      \
591cb0ef41Sopenharmony_ci  V(SweepFinalize)
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci#define CPPGC_FOR_ALL_HISTOGRAM_CONCURRENT_SCOPES(V) \
621cb0ef41Sopenharmony_ci  V(ConcurrentMark)                                  \
631cb0ef41Sopenharmony_ci  V(ConcurrentSweep)
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ci#define CPPGC_FOR_ALL_CONCURRENT_SCOPES(V) V(ConcurrentMarkProcessEphemerons)
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci// Sink for various time and memory statistics.
681cb0ef41Sopenharmony_ciclass V8_EXPORT_PRIVATE StatsCollector final {
691cb0ef41Sopenharmony_ci  using IsForcedGC = GarbageCollector::Config::IsForcedGC;
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci public:
721cb0ef41Sopenharmony_ci  using CollectionType = GarbageCollector::Config::CollectionType;
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci#if defined(CPPGC_DECLARE_ENUM)
751cb0ef41Sopenharmony_ci  static_assert(false, "CPPGC_DECLARE_ENUM macro is already defined");
761cb0ef41Sopenharmony_ci#endif
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ci  enum ScopeId {
791cb0ef41Sopenharmony_ci#define CPPGC_DECLARE_ENUM(name) k##name,
801cb0ef41Sopenharmony_ci    CPPGC_FOR_ALL_HISTOGRAM_SCOPES(CPPGC_DECLARE_ENUM)
811cb0ef41Sopenharmony_ci        kNumHistogramScopeIds,
821cb0ef41Sopenharmony_ci    CPPGC_FOR_ALL_SCOPES(CPPGC_DECLARE_ENUM)
831cb0ef41Sopenharmony_ci#undef CPPGC_DECLARE_ENUM
841cb0ef41Sopenharmony_ci        kNumScopeIds,
851cb0ef41Sopenharmony_ci  };
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci  enum ConcurrentScopeId {
881cb0ef41Sopenharmony_ci#define CPPGC_DECLARE_ENUM(name) k##name,
891cb0ef41Sopenharmony_ci    CPPGC_FOR_ALL_HISTOGRAM_CONCURRENT_SCOPES(CPPGC_DECLARE_ENUM)
901cb0ef41Sopenharmony_ci        kNumHistogramConcurrentScopeIds,
911cb0ef41Sopenharmony_ci    CPPGC_FOR_ALL_CONCURRENT_SCOPES(CPPGC_DECLARE_ENUM)
921cb0ef41Sopenharmony_ci#undef CPPGC_DECLARE_ENUM
931cb0ef41Sopenharmony_ci        kNumConcurrentScopeIds
941cb0ef41Sopenharmony_ci  };
951cb0ef41Sopenharmony_ci
961cb0ef41Sopenharmony_ci  // POD to hold interesting data accumulated during a garbage collection cycle.
971cb0ef41Sopenharmony_ci  //
981cb0ef41Sopenharmony_ci  // The event is always fully populated when looking at previous events but
991cb0ef41Sopenharmony_ci  // may only be partially populated when looking at the current event.
1001cb0ef41Sopenharmony_ci  struct Event final {
1011cb0ef41Sopenharmony_ci    V8_EXPORT_PRIVATE explicit Event();
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_ci    v8::base::TimeDelta scope_data[kNumHistogramScopeIds];
1041cb0ef41Sopenharmony_ci    v8::base::Atomic32 concurrent_scope_data[kNumHistogramConcurrentScopeIds]{
1051cb0ef41Sopenharmony_ci        0};
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci    size_t epoch = -1;
1081cb0ef41Sopenharmony_ci    CollectionType collection_type = CollectionType::kMajor;
1091cb0ef41Sopenharmony_ci    IsForcedGC is_forced_gc = IsForcedGC::kNotForced;
1101cb0ef41Sopenharmony_ci    // Marked bytes collected during marking.
1111cb0ef41Sopenharmony_ci    size_t marked_bytes = 0;
1121cb0ef41Sopenharmony_ci    size_t object_size_before_sweep_bytes = -1;
1131cb0ef41Sopenharmony_ci    size_t memory_size_before_sweep_bytes = -1;
1141cb0ef41Sopenharmony_ci  };
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci private:
1171cb0ef41Sopenharmony_ci#if defined(CPPGC_CASE)
1181cb0ef41Sopenharmony_ci  static_assert(false, "CPPGC_CASE macro is already defined");
1191cb0ef41Sopenharmony_ci#endif
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci  constexpr static const char* GetScopeName(ScopeId id, CollectionType type) {
1221cb0ef41Sopenharmony_ci    switch (id) {
1231cb0ef41Sopenharmony_ci#define CPPGC_CASE(name)                                   \
1241cb0ef41Sopenharmony_ci  case k##name:                                            \
1251cb0ef41Sopenharmony_ci    return type == CollectionType::kMajor ? "CppGC." #name \
1261cb0ef41Sopenharmony_ci                                          : "CppGC." #name ".Minor";
1271cb0ef41Sopenharmony_ci      CPPGC_FOR_ALL_HISTOGRAM_SCOPES(CPPGC_CASE)
1281cb0ef41Sopenharmony_ci      CPPGC_FOR_ALL_SCOPES(CPPGC_CASE)
1291cb0ef41Sopenharmony_ci#undef CPPGC_CASE
1301cb0ef41Sopenharmony_ci      default:
1311cb0ef41Sopenharmony_ci        return nullptr;
1321cb0ef41Sopenharmony_ci    }
1331cb0ef41Sopenharmony_ci  }
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci  constexpr static const char* GetScopeName(ConcurrentScopeId id,
1361cb0ef41Sopenharmony_ci                                            CollectionType type) {
1371cb0ef41Sopenharmony_ci    switch (id) {
1381cb0ef41Sopenharmony_ci#define CPPGC_CASE(name)                                   \
1391cb0ef41Sopenharmony_ci  case k##name:                                            \
1401cb0ef41Sopenharmony_ci    return type == CollectionType::kMajor ? "CppGC." #name \
1411cb0ef41Sopenharmony_ci                                          : "CppGC." #name ".Minor";
1421cb0ef41Sopenharmony_ci      CPPGC_FOR_ALL_HISTOGRAM_CONCURRENT_SCOPES(CPPGC_CASE)
1431cb0ef41Sopenharmony_ci      CPPGC_FOR_ALL_CONCURRENT_SCOPES(CPPGC_CASE)
1441cb0ef41Sopenharmony_ci#undef CPPGC_CASE
1451cb0ef41Sopenharmony_ci      default:
1461cb0ef41Sopenharmony_ci        return nullptr;
1471cb0ef41Sopenharmony_ci    }
1481cb0ef41Sopenharmony_ci  }
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci  enum TraceCategory { kEnabled, kDisabled };
1511cb0ef41Sopenharmony_ci  enum ScopeContext { kMutatorThread, kConcurrentThread };
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci  // Trace a particular scope. Will emit a trace event and record the time in
1541cb0ef41Sopenharmony_ci  // the corresponding StatsCollector.
1551cb0ef41Sopenharmony_ci  template <TraceCategory trace_category, ScopeContext scope_category>
1561cb0ef41Sopenharmony_ci  class V8_NODISCARD InternalScope {
1571cb0ef41Sopenharmony_ci    using ScopeIdType = std::conditional_t<scope_category == kMutatorThread,
1581cb0ef41Sopenharmony_ci                                           ScopeId, ConcurrentScopeId>;
1591cb0ef41Sopenharmony_ci
1601cb0ef41Sopenharmony_ci   public:
1611cb0ef41Sopenharmony_ci    template <typename... Args>
1621cb0ef41Sopenharmony_ci    InternalScope(StatsCollector* stats_collector, ScopeIdType scope_id,
1631cb0ef41Sopenharmony_ci                  Args... args)
1641cb0ef41Sopenharmony_ci        : stats_collector_(stats_collector),
1651cb0ef41Sopenharmony_ci          start_time_(v8::base::TimeTicks::Now()),
1661cb0ef41Sopenharmony_ci          scope_id_(scope_id) {
1671cb0ef41Sopenharmony_ci      DCHECK_LE(0, scope_id_);
1681cb0ef41Sopenharmony_ci      DCHECK_LT(static_cast<int>(scope_id_),
1691cb0ef41Sopenharmony_ci                scope_category == kMutatorThread
1701cb0ef41Sopenharmony_ci                    ? static_cast<int>(kNumScopeIds)
1711cb0ef41Sopenharmony_ci                    : static_cast<int>(kNumConcurrentScopeIds));
1721cb0ef41Sopenharmony_ci      DCHECK_NE(static_cast<int>(scope_id_),
1731cb0ef41Sopenharmony_ci                scope_category == kMutatorThread
1741cb0ef41Sopenharmony_ci                    ? static_cast<int>(kNumHistogramScopeIds)
1751cb0ef41Sopenharmony_ci                    : static_cast<int>(kNumHistogramConcurrentScopeIds));
1761cb0ef41Sopenharmony_ci      StartTrace(args...);
1771cb0ef41Sopenharmony_ci    }
1781cb0ef41Sopenharmony_ci
1791cb0ef41Sopenharmony_ci    ~InternalScope() {
1801cb0ef41Sopenharmony_ci      StopTrace();
1811cb0ef41Sopenharmony_ci      IncreaseScopeTime();
1821cb0ef41Sopenharmony_ci    }
1831cb0ef41Sopenharmony_ci
1841cb0ef41Sopenharmony_ci    InternalScope(const InternalScope&) = delete;
1851cb0ef41Sopenharmony_ci    InternalScope& operator=(const InternalScope&) = delete;
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci    void DecreaseStartTimeForTesting(v8::base::TimeDelta delta) {
1881cb0ef41Sopenharmony_ci      start_time_ -= delta;
1891cb0ef41Sopenharmony_ci    }
1901cb0ef41Sopenharmony_ci
1911cb0ef41Sopenharmony_ci   private:
1921cb0ef41Sopenharmony_ci    void* operator new(size_t, void*) = delete;
1931cb0ef41Sopenharmony_ci    void* operator new(size_t) = delete;
1941cb0ef41Sopenharmony_ci
1951cb0ef41Sopenharmony_ci    inline constexpr static const char* TraceCategory();
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci    template <typename... Args>
1981cb0ef41Sopenharmony_ci    inline void StartTrace(Args... args);
1991cb0ef41Sopenharmony_ci    inline void StopTrace();
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_ci    inline void StartTraceImpl();
2021cb0ef41Sopenharmony_ci    template <typename Value1>
2031cb0ef41Sopenharmony_ci    inline void StartTraceImpl(const char* k1, Value1 v1);
2041cb0ef41Sopenharmony_ci    template <typename Value1, typename Value2>
2051cb0ef41Sopenharmony_ci    inline void StartTraceImpl(const char* k1, Value1 v1, const char* k2,
2061cb0ef41Sopenharmony_ci                               Value2 v2);
2071cb0ef41Sopenharmony_ci    inline void StopTraceImpl();
2081cb0ef41Sopenharmony_ci
2091cb0ef41Sopenharmony_ci    inline void IncreaseScopeTime();
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci    StatsCollector* const stats_collector_;
2121cb0ef41Sopenharmony_ci    v8::base::TimeTicks start_time_;
2131cb0ef41Sopenharmony_ci    const ScopeIdType scope_id_;
2141cb0ef41Sopenharmony_ci  };
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ci public:
2171cb0ef41Sopenharmony_ci  using DisabledScope = InternalScope<kDisabled, kMutatorThread>;
2181cb0ef41Sopenharmony_ci  using EnabledScope = InternalScope<kEnabled, kMutatorThread>;
2191cb0ef41Sopenharmony_ci  using DisabledConcurrentScope = InternalScope<kDisabled, kConcurrentThread>;
2201cb0ef41Sopenharmony_ci  using EnabledConcurrentScope = InternalScope<kEnabled, kConcurrentThread>;
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_ci  // Observer for allocated object size. May e.g. be used to implement heap
2231cb0ef41Sopenharmony_ci  // growing heuristics. Observers may register/unregister observers at any
2241cb0ef41Sopenharmony_ci  // time when being invoked.
2251cb0ef41Sopenharmony_ci  class AllocationObserver {
2261cb0ef41Sopenharmony_ci   public:
2271cb0ef41Sopenharmony_ci    // Called after observing at least
2281cb0ef41Sopenharmony_ci    // StatsCollector::kAllocationThresholdBytes changed bytes through
2291cb0ef41Sopenharmony_ci    // allocation or explicit free. Reports both, negative and positive
2301cb0ef41Sopenharmony_ci    // increments, to allow observer to decide whether absolute values or only
2311cb0ef41Sopenharmony_ci    // the deltas is interesting.
2321cb0ef41Sopenharmony_ci    //
2331cb0ef41Sopenharmony_ci    // May trigger GC.
2341cb0ef41Sopenharmony_ci    virtual void AllocatedObjectSizeIncreased(size_t) {}
2351cb0ef41Sopenharmony_ci    virtual void AllocatedObjectSizeDecreased(size_t) {}
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci    // Called when the exact size of allocated object size is known. In
2381cb0ef41Sopenharmony_ci    // practice, this is after marking when marked bytes == allocated bytes.
2391cb0ef41Sopenharmony_ci    //
2401cb0ef41Sopenharmony_ci    // Must not trigger GC synchronously.
2411cb0ef41Sopenharmony_ci    virtual void ResetAllocatedObjectSize(size_t) {}
2421cb0ef41Sopenharmony_ci
2431cb0ef41Sopenharmony_ci    // Called upon allocating/releasing chunks of memory (e.g. pages) that can
2441cb0ef41Sopenharmony_ci    // contain objects.
2451cb0ef41Sopenharmony_ci    //
2461cb0ef41Sopenharmony_ci    // Must not trigger GC.
2471cb0ef41Sopenharmony_ci    virtual void AllocatedSizeIncreased(size_t) {}
2481cb0ef41Sopenharmony_ci    virtual void AllocatedSizeDecreased(size_t) {}
2491cb0ef41Sopenharmony_ci  };
2501cb0ef41Sopenharmony_ci
2511cb0ef41Sopenharmony_ci  // Observers are implemented using virtual calls. Avoid notifications below
2521cb0ef41Sopenharmony_ci  // reasonably interesting sizes.
2531cb0ef41Sopenharmony_ci  static constexpr size_t kAllocationThresholdBytes = 1024;
2541cb0ef41Sopenharmony_ci
2551cb0ef41Sopenharmony_ci  explicit StatsCollector(Platform*);
2561cb0ef41Sopenharmony_ci  StatsCollector(const StatsCollector&) = delete;
2571cb0ef41Sopenharmony_ci  StatsCollector& operator=(const StatsCollector&) = delete;
2581cb0ef41Sopenharmony_ci
2591cb0ef41Sopenharmony_ci  void RegisterObserver(AllocationObserver*);
2601cb0ef41Sopenharmony_ci  void UnregisterObserver(AllocationObserver*);
2611cb0ef41Sopenharmony_ci
2621cb0ef41Sopenharmony_ci  void NotifyAllocation(size_t);
2631cb0ef41Sopenharmony_ci  void NotifyExplicitFree(size_t);
2641cb0ef41Sopenharmony_ci  // Safepoints should only be invoked when garbage collections are possible.
2651cb0ef41Sopenharmony_ci  // This is necessary as increments and decrements are reported as close to
2661cb0ef41Sopenharmony_ci  // their actual allocation/reclamation as possible.
2671cb0ef41Sopenharmony_ci  void NotifySafePointForConservativeCollection();
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_ci  void NotifySafePointForTesting();
2701cb0ef41Sopenharmony_ci
2711cb0ef41Sopenharmony_ci  // Indicates a new garbage collection cycle.
2721cb0ef41Sopenharmony_ci  void NotifyMarkingStarted(CollectionType, IsForcedGC);
2731cb0ef41Sopenharmony_ci  // Indicates that marking of the current garbage collection cycle is
2741cb0ef41Sopenharmony_ci  // completed.
2751cb0ef41Sopenharmony_ci  void NotifyMarkingCompleted(size_t marked_bytes);
2761cb0ef41Sopenharmony_ci  // Indicates the end of a garbage collection cycle. This means that sweeping
2771cb0ef41Sopenharmony_ci  // is finished at this point.
2781cb0ef41Sopenharmony_ci  void NotifySweepingCompleted();
2791cb0ef41Sopenharmony_ci
2801cb0ef41Sopenharmony_ci  size_t allocated_memory_size() const;
2811cb0ef41Sopenharmony_ci  // Size of live objects in bytes  on the heap. Based on the most recent marked
2821cb0ef41Sopenharmony_ci  // bytes and the bytes allocated since last marking.
2831cb0ef41Sopenharmony_ci  size_t allocated_object_size() const;
2841cb0ef41Sopenharmony_ci
2851cb0ef41Sopenharmony_ci  // Returns the overall marked bytes count, i.e. if young generation is
2861cb0ef41Sopenharmony_ci  // enabled, it returns the accumulated number. Should not be called during
2871cb0ef41Sopenharmony_ci  // marking.
2881cb0ef41Sopenharmony_ci  size_t marked_bytes() const;
2891cb0ef41Sopenharmony_ci
2901cb0ef41Sopenharmony_ci  // Returns the marked bytes for the current cycle. Should only be called
2911cb0ef41Sopenharmony_ci  // within GC cycle.
2921cb0ef41Sopenharmony_ci  size_t marked_bytes_on_current_cycle() const;
2931cb0ef41Sopenharmony_ci
2941cb0ef41Sopenharmony_ci  // Returns the overall duration of the most recent marking phase. Should not
2951cb0ef41Sopenharmony_ci  // be called during marking.
2961cb0ef41Sopenharmony_ci  v8::base::TimeDelta marking_time() const;
2971cb0ef41Sopenharmony_ci
2981cb0ef41Sopenharmony_ci  double GetRecentAllocationSpeedInBytesPerMs() const;
2991cb0ef41Sopenharmony_ci
3001cb0ef41Sopenharmony_ci  const Event& GetPreviousEventForTesting() const { return previous_; }
3011cb0ef41Sopenharmony_ci
3021cb0ef41Sopenharmony_ci  void NotifyAllocatedMemory(int64_t);
3031cb0ef41Sopenharmony_ci  void NotifyFreedMemory(int64_t);
3041cb0ef41Sopenharmony_ci
3051cb0ef41Sopenharmony_ci  void IncrementDiscardedMemory(size_t);
3061cb0ef41Sopenharmony_ci  void DecrementDiscardedMemory(size_t);
3071cb0ef41Sopenharmony_ci  void ResetDiscardedMemory();
3081cb0ef41Sopenharmony_ci  size_t discarded_memory_size() const;
3091cb0ef41Sopenharmony_ci  size_t resident_memory_size() const;
3101cb0ef41Sopenharmony_ci
3111cb0ef41Sopenharmony_ci  void SetMetricRecorder(std::unique_ptr<MetricRecorder> histogram_recorder) {
3121cb0ef41Sopenharmony_ci    metric_recorder_ = std::move(histogram_recorder);
3131cb0ef41Sopenharmony_ci  }
3141cb0ef41Sopenharmony_ci
3151cb0ef41Sopenharmony_ci  MetricRecorder* GetMetricRecorder() const { return metric_recorder_.get(); }
3161cb0ef41Sopenharmony_ci
3171cb0ef41Sopenharmony_ci private:
3181cb0ef41Sopenharmony_ci  enum class GarbageCollectionState : uint8_t {
3191cb0ef41Sopenharmony_ci    kNotRunning,
3201cb0ef41Sopenharmony_ci    kMarking,
3211cb0ef41Sopenharmony_ci    kSweeping
3221cb0ef41Sopenharmony_ci  };
3231cb0ef41Sopenharmony_ci
3241cb0ef41Sopenharmony_ci  void RecordHistogramSample(ScopeId, v8::base::TimeDelta);
3251cb0ef41Sopenharmony_ci  void RecordHistogramSample(ConcurrentScopeId, v8::base::TimeDelta) {}
3261cb0ef41Sopenharmony_ci
3271cb0ef41Sopenharmony_ci  // Invokes |callback| for all registered observers.
3281cb0ef41Sopenharmony_ci  template <typename Callback>
3291cb0ef41Sopenharmony_ci  void ForAllAllocationObservers(Callback callback);
3301cb0ef41Sopenharmony_ci
3311cb0ef41Sopenharmony_ci  void AllocatedObjectSizeSafepointImpl();
3321cb0ef41Sopenharmony_ci
3331cb0ef41Sopenharmony_ci  // Allocated bytes since the end of marking. These bytes are reset after
3341cb0ef41Sopenharmony_ci  // marking as they are accounted in marked_bytes then. May be negative in case
3351cb0ef41Sopenharmony_ci  // an object was explicitly freed that was marked as live in the previous
3361cb0ef41Sopenharmony_ci  // cycle.
3371cb0ef41Sopenharmony_ci  int64_t allocated_bytes_since_end_of_marking_ = 0;
3381cb0ef41Sopenharmony_ci  v8::base::TimeTicks time_of_last_end_of_marking_ = v8::base::TimeTicks::Now();
3391cb0ef41Sopenharmony_ci  // Counters for allocation and free. The individual values are never negative
3401cb0ef41Sopenharmony_ci  // but their delta may be because of the same reason the overall
3411cb0ef41Sopenharmony_ci  // allocated_bytes_since_end_of_marking_ may be negative. Keep integer
3421cb0ef41Sopenharmony_ci  // arithmetic for simplicity.
3431cb0ef41Sopenharmony_ci  int64_t allocated_bytes_since_safepoint_ = 0;
3441cb0ef41Sopenharmony_ci  int64_t explicitly_freed_bytes_since_safepoint_ = 0;
3451cb0ef41Sopenharmony_ci#ifdef CPPGC_VERIFY_HEAP
3461cb0ef41Sopenharmony_ci  // Tracks live bytes for overflows.
3471cb0ef41Sopenharmony_ci  size_t tracked_live_bytes_ = 0;
3481cb0ef41Sopenharmony_ci#endif  // CPPGC_VERIFY_HEAP
3491cb0ef41Sopenharmony_ci
3501cb0ef41Sopenharmony_ci  // The number of bytes marked so far. For young generation (with sticky bits)
3511cb0ef41Sopenharmony_ci  // keeps track of marked bytes across multiple GC cycles.
3521cb0ef41Sopenharmony_ci  size_t marked_bytes_so_far_ = 0;
3531cb0ef41Sopenharmony_ci
3541cb0ef41Sopenharmony_ci  int64_t memory_allocated_bytes_ = 0;
3551cb0ef41Sopenharmony_ci  int64_t memory_freed_bytes_since_end_of_marking_ = 0;
3561cb0ef41Sopenharmony_ci  std::atomic<size_t> discarded_bytes_{0};
3571cb0ef41Sopenharmony_ci
3581cb0ef41Sopenharmony_ci  // vector to allow fast iteration of observers. Register/Unregisters only
3591cb0ef41Sopenharmony_ci  // happens on startup/teardown.
3601cb0ef41Sopenharmony_ci  std::vector<AllocationObserver*> allocation_observers_;
3611cb0ef41Sopenharmony_ci  bool allocation_observer_deleted_ = false;
3621cb0ef41Sopenharmony_ci
3631cb0ef41Sopenharmony_ci  GarbageCollectionState gc_state_ = GarbageCollectionState::kNotRunning;
3641cb0ef41Sopenharmony_ci
3651cb0ef41Sopenharmony_ci  // The event being filled by the current GC cycle between NotifyMarkingStarted
3661cb0ef41Sopenharmony_ci  // and NotifySweepingFinished.
3671cb0ef41Sopenharmony_ci  Event current_;
3681cb0ef41Sopenharmony_ci  // The previous GC event which is populated at NotifySweepingFinished.
3691cb0ef41Sopenharmony_ci  Event previous_;
3701cb0ef41Sopenharmony_ci
3711cb0ef41Sopenharmony_ci  std::unique_ptr<MetricRecorder> metric_recorder_;
3721cb0ef41Sopenharmony_ci
3731cb0ef41Sopenharmony_ci  // |platform_| is used by the TRACE_EVENT_* macros.
3741cb0ef41Sopenharmony_ci  Platform* platform_;
3751cb0ef41Sopenharmony_ci};
3761cb0ef41Sopenharmony_ci
3771cb0ef41Sopenharmony_citemplate <typename Callback>
3781cb0ef41Sopenharmony_civoid StatsCollector::ForAllAllocationObservers(Callback callback) {
3791cb0ef41Sopenharmony_ci  // Iterate using indices to allow push_back() of new observers.
3801cb0ef41Sopenharmony_ci  for (size_t i = 0; i < allocation_observers_.size(); ++i) {
3811cb0ef41Sopenharmony_ci    auto* observer = allocation_observers_[i];
3821cb0ef41Sopenharmony_ci    if (observer) {
3831cb0ef41Sopenharmony_ci      callback(observer);
3841cb0ef41Sopenharmony_ci    }
3851cb0ef41Sopenharmony_ci  }
3861cb0ef41Sopenharmony_ci  if (allocation_observer_deleted_) {
3871cb0ef41Sopenharmony_ci    allocation_observers_.erase(
3881cb0ef41Sopenharmony_ci        std::remove(allocation_observers_.begin(), allocation_observers_.end(),
3891cb0ef41Sopenharmony_ci                    nullptr),
3901cb0ef41Sopenharmony_ci        allocation_observers_.end());
3911cb0ef41Sopenharmony_ci    allocation_observer_deleted_ = false;
3921cb0ef41Sopenharmony_ci  }
3931cb0ef41Sopenharmony_ci}
3941cb0ef41Sopenharmony_ci
3951cb0ef41Sopenharmony_citemplate <StatsCollector::TraceCategory trace_category,
3961cb0ef41Sopenharmony_ci          StatsCollector::ScopeContext scope_category>
3971cb0ef41Sopenharmony_ciconstexpr const char*
3981cb0ef41Sopenharmony_ciStatsCollector::InternalScope<trace_category, scope_category>::TraceCategory() {
3991cb0ef41Sopenharmony_ci  switch (trace_category) {
4001cb0ef41Sopenharmony_ci    case kEnabled:
4011cb0ef41Sopenharmony_ci      return "cppgc";
4021cb0ef41Sopenharmony_ci    case kDisabled:
4031cb0ef41Sopenharmony_ci      return TRACE_DISABLED_BY_DEFAULT("cppgc");
4041cb0ef41Sopenharmony_ci  }
4051cb0ef41Sopenharmony_ci}
4061cb0ef41Sopenharmony_ci
4071cb0ef41Sopenharmony_citemplate <StatsCollector::TraceCategory trace_category,
4081cb0ef41Sopenharmony_ci          StatsCollector::ScopeContext scope_category>
4091cb0ef41Sopenharmony_citemplate <typename... Args>
4101cb0ef41Sopenharmony_civoid StatsCollector::InternalScope<trace_category, scope_category>::StartTrace(
4111cb0ef41Sopenharmony_ci    Args... args) {
4121cb0ef41Sopenharmony_ci  // Top level scopes that contribute to histogram should always be enabled.
4131cb0ef41Sopenharmony_ci  DCHECK_IMPLIES(static_cast<int>(scope_id_) <
4141cb0ef41Sopenharmony_ci                     (scope_category == kMutatorThread
4151cb0ef41Sopenharmony_ci                          ? static_cast<int>(kNumHistogramScopeIds)
4161cb0ef41Sopenharmony_ci                          : static_cast<int>(kNumHistogramConcurrentScopeIds)),
4171cb0ef41Sopenharmony_ci                 trace_category == StatsCollector::TraceCategory::kEnabled);
4181cb0ef41Sopenharmony_ci  if (trace_category == StatsCollector::TraceCategory::kEnabled)
4191cb0ef41Sopenharmony_ci    StartTraceImpl(args...);
4201cb0ef41Sopenharmony_ci}
4211cb0ef41Sopenharmony_ci
4221cb0ef41Sopenharmony_citemplate <StatsCollector::TraceCategory trace_category,
4231cb0ef41Sopenharmony_ci          StatsCollector::ScopeContext scope_category>
4241cb0ef41Sopenharmony_civoid StatsCollector::InternalScope<trace_category,
4251cb0ef41Sopenharmony_ci                                   scope_category>::StopTrace() {
4261cb0ef41Sopenharmony_ci  if (trace_category == StatsCollector::TraceCategory::kEnabled)
4271cb0ef41Sopenharmony_ci    StopTraceImpl();
4281cb0ef41Sopenharmony_ci}
4291cb0ef41Sopenharmony_ci
4301cb0ef41Sopenharmony_citemplate <StatsCollector::TraceCategory trace_category,
4311cb0ef41Sopenharmony_ci          StatsCollector::ScopeContext scope_category>
4321cb0ef41Sopenharmony_civoid StatsCollector::InternalScope<trace_category,
4331cb0ef41Sopenharmony_ci                                   scope_category>::StartTraceImpl() {
4341cb0ef41Sopenharmony_ci  TRACE_EVENT_BEGIN0(
4351cb0ef41Sopenharmony_ci      TraceCategory(),
4361cb0ef41Sopenharmony_ci      GetScopeName(scope_id_, stats_collector_->current_.collection_type));
4371cb0ef41Sopenharmony_ci}
4381cb0ef41Sopenharmony_ci
4391cb0ef41Sopenharmony_citemplate <StatsCollector::TraceCategory trace_category,
4401cb0ef41Sopenharmony_ci          StatsCollector::ScopeContext scope_category>
4411cb0ef41Sopenharmony_citemplate <typename Value1>
4421cb0ef41Sopenharmony_civoid StatsCollector::InternalScope<
4431cb0ef41Sopenharmony_ci    trace_category, scope_category>::StartTraceImpl(const char* k1, Value1 v1) {
4441cb0ef41Sopenharmony_ci  TRACE_EVENT_BEGIN1(
4451cb0ef41Sopenharmony_ci      TraceCategory(),
4461cb0ef41Sopenharmony_ci      GetScopeName(scope_id_, stats_collector_->current_.collection_type), k1,
4471cb0ef41Sopenharmony_ci      v1);
4481cb0ef41Sopenharmony_ci}
4491cb0ef41Sopenharmony_ci
4501cb0ef41Sopenharmony_citemplate <StatsCollector::TraceCategory trace_category,
4511cb0ef41Sopenharmony_ci          StatsCollector::ScopeContext scope_category>
4521cb0ef41Sopenharmony_citemplate <typename Value1, typename Value2>
4531cb0ef41Sopenharmony_civoid StatsCollector::InternalScope<
4541cb0ef41Sopenharmony_ci    trace_category, scope_category>::StartTraceImpl(const char* k1, Value1 v1,
4551cb0ef41Sopenharmony_ci                                                    const char* k2, Value2 v2) {
4561cb0ef41Sopenharmony_ci  TRACE_EVENT_BEGIN2(
4571cb0ef41Sopenharmony_ci      TraceCategory(),
4581cb0ef41Sopenharmony_ci      GetScopeName(scope_id_, stats_collector_->current_.collection_type), k1,
4591cb0ef41Sopenharmony_ci      v1, k2, v2);
4601cb0ef41Sopenharmony_ci}
4611cb0ef41Sopenharmony_ci
4621cb0ef41Sopenharmony_citemplate <StatsCollector::TraceCategory trace_category,
4631cb0ef41Sopenharmony_ci          StatsCollector::ScopeContext scope_category>
4641cb0ef41Sopenharmony_civoid StatsCollector::InternalScope<trace_category,
4651cb0ef41Sopenharmony_ci                                   scope_category>::StopTraceImpl() {
4661cb0ef41Sopenharmony_ci  TRACE_EVENT_END2(
4671cb0ef41Sopenharmony_ci      TraceCategory(),
4681cb0ef41Sopenharmony_ci      GetScopeName(scope_id_, stats_collector_->current_.collection_type),
4691cb0ef41Sopenharmony_ci      "epoch", stats_collector_->current_.epoch, "forced",
4701cb0ef41Sopenharmony_ci      stats_collector_->current_.is_forced_gc == IsForcedGC::kForced);
4711cb0ef41Sopenharmony_ci}
4721cb0ef41Sopenharmony_ci
4731cb0ef41Sopenharmony_citemplate <StatsCollector::TraceCategory trace_category,
4741cb0ef41Sopenharmony_ci          StatsCollector::ScopeContext scope_category>
4751cb0ef41Sopenharmony_civoid StatsCollector::InternalScope<trace_category,
4761cb0ef41Sopenharmony_ci                                   scope_category>::IncreaseScopeTime() {
4771cb0ef41Sopenharmony_ci  DCHECK_NE(GarbageCollectionState::kNotRunning, stats_collector_->gc_state_);
4781cb0ef41Sopenharmony_ci  // Only record top level scopes.
4791cb0ef41Sopenharmony_ci  if (static_cast<int>(scope_id_) >=
4801cb0ef41Sopenharmony_ci      (scope_category == kMutatorThread
4811cb0ef41Sopenharmony_ci           ? static_cast<int>(kNumHistogramScopeIds)
4821cb0ef41Sopenharmony_ci           : static_cast<int>(kNumHistogramConcurrentScopeIds)))
4831cb0ef41Sopenharmony_ci    return;
4841cb0ef41Sopenharmony_ci  v8::base::TimeDelta time = v8::base::TimeTicks::Now() - start_time_;
4851cb0ef41Sopenharmony_ci  if (scope_category == StatsCollector::ScopeContext::kMutatorThread) {
4861cb0ef41Sopenharmony_ci    stats_collector_->current_.scope_data[scope_id_] += time;
4871cb0ef41Sopenharmony_ci    if (stats_collector_->metric_recorder_)
4881cb0ef41Sopenharmony_ci      stats_collector_->RecordHistogramSample(scope_id_, time);
4891cb0ef41Sopenharmony_ci    return;
4901cb0ef41Sopenharmony_ci  }
4911cb0ef41Sopenharmony_ci  // scope_category == StatsCollector::ScopeContext::kConcurrentThread
4921cb0ef41Sopenharmony_ci  using Atomic32 = v8::base::Atomic32;
4931cb0ef41Sopenharmony_ci  const int64_t us = time.InMicroseconds();
4941cb0ef41Sopenharmony_ci  DCHECK_LE(us, std::numeric_limits<Atomic32>::max());
4951cb0ef41Sopenharmony_ci  v8::base::Relaxed_AtomicIncrement(
4961cb0ef41Sopenharmony_ci      &stats_collector_->current_.concurrent_scope_data[scope_id_],
4971cb0ef41Sopenharmony_ci      static_cast<Atomic32>(us));
4981cb0ef41Sopenharmony_ci}
4991cb0ef41Sopenharmony_ci
5001cb0ef41Sopenharmony_ci}  // namespace internal
5011cb0ef41Sopenharmony_ci}  // namespace cppgc
5021cb0ef41Sopenharmony_ci
5031cb0ef41Sopenharmony_ci#endif  // V8_HEAP_CPPGC_STATS_COLLECTOR_H_
504