|  | // 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 <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #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 <typename Callback> | 
|  | 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<AllocationObserver*> 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 <typename Callback> | 
|  | void StatsCollector::ForAllAllocationObservers(Callback callback) { | 
|  | for (AllocationObserver* observer : allocation_observers_) { | 
|  | callback(observer); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace cppgc | 
|  |  | 
|  | #endif  // V8_HEAP_CPPGC_STATS_COLLECTOR_H_ |