// 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_HEAP_CPPGC_STATS_COLLECTOR_H_ #define V8_HEAP_CPPGC_STATS_COLLECTOR_H_ #include #include #include #include "src/base/macros.h" #include "src/base/platform/time.h" namespace cppgc { namespace internal { // Sink for various time and memory statistics. class V8_EXPORT_PRIVATE StatsCollector final { public: // POD to hold interesting data accumulated during a garbage collection cycle. // // The event is always fully populated when looking at previous events but // may only be partially populated when looking at the current event. struct Event final { // Marked bytes collected during marking. size_t marked_bytes = 0; }; // Observer for allocated object size. May be used to implement heap growing // heuristics. class AllocationObserver { public: // Called after observing at least // StatsCollector::kAllocationThresholdBytes changed bytes through // allocation or explicit free. Reports both, negative and positive // increments, to allow observer to decide whether absolute values or only // the deltas is interesting. // // May trigger GC. virtual void AllocatedObjectSizeIncreased(size_t) = 0; virtual void AllocatedObjectSizeDecreased(size_t) = 0; // Called when the exact size of allocated object size is known. In // practice, this is after marking when marked bytes == allocated bytes. // // Must not trigger GC synchronously. virtual void ResetAllocatedObjectSize(size_t) = 0; }; // Observers are implemented using virtual calls. Avoid notifications below // reasonably interesting sizes. static constexpr size_t kAllocationThresholdBytes = 1024; StatsCollector() = default; StatsCollector(const StatsCollector&) = delete; StatsCollector& operator=(const StatsCollector&) = delete; void RegisterObserver(AllocationObserver*); void UnregisterObserver(AllocationObserver*); void NotifyAllocation(size_t); void NotifyExplicitFree(size_t); // Safepoints should only be invoked when garabge collections are possible. // This is necessary as increments and decrements are reported as close to // their actual allocation/reclamation as possible. void NotifySafePointForConservativeCollection(); // Indicates a new garbage collection cycle. void NotifyMarkingStarted(); // Indicates that marking of the current garbage collection cycle is // completed. void NotifyMarkingCompleted(size_t marked_bytes); // Indicates the end of a garbage collection cycle. This means that sweeping // is finished at this point. const Event& NotifySweepingCompleted(); // Size of live objects in bytes on the heap. Based on the most recent marked // bytes and the bytes allocated since last marking. size_t allocated_object_size() const; double GetRecentAllocationSpeedInBytesPerMs() const; private: enum class GarbageCollectionState : uint8_t { kNotRunning, kMarking, kSweeping }; // Invokes |callback| for all registered observers. template void ForAllAllocationObservers(Callback callback); void AllocatedObjectSizeSafepointImpl(); // Allocated bytes since the end of marking. These bytes are reset after // marking as they are accounted in marked_bytes then. May be negative in case // an object was explicitly freed that was marked as live in the previous // cycle. int64_t allocated_bytes_since_end_of_marking_ = 0; v8::base::TimeTicks time_of_last_end_of_marking_ = v8::base::TimeTicks::Now(); // Counters for allocation and free. The individual values are never negative // but their delta may be because of the same reason the overall // allocated_bytes_since_end_of_marking_ may be negative. Keep integer // arithmetic for simplicity. int64_t allocated_bytes_since_safepoint_ = 0; int64_t explicitly_freed_bytes_since_safepoint_ = 0; // vector to allow fast iteration of observers. Register/Unregisters only // happens on startup/teardown. std::vector allocation_observers_; GarbageCollectionState gc_state_ = GarbageCollectionState::kNotRunning; // The event being filled by the current GC cycle between NotifyMarkingStarted // and NotifySweepingFinished. Event current_; // The previous GC event which is populated at NotifySweepingFinished. Event previous_; }; template void StatsCollector::ForAllAllocationObservers(Callback callback) { for (AllocationObserver* observer : allocation_observers_) { callback(observer); } } } // namespace internal } // namespace cppgc #endif // V8_HEAP_CPPGC_STATS_COLLECTOR_H_