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