1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/heap/cppgc/stats-collector.h"
6
7 #include <algorithm>
8 #include <cmath>
9
10 #include "src/base/logging.h"
11
12 namespace cppgc {
13 namespace internal {
14
15 // static
16 constexpr size_t StatsCollector::kAllocationThresholdBytes;
17
RegisterObserver(AllocationObserver * observer)18 void StatsCollector::RegisterObserver(AllocationObserver* observer) {
19 DCHECK_EQ(allocation_observers_.end(),
20 std::find(allocation_observers_.begin(),
21 allocation_observers_.end(), observer));
22 allocation_observers_.push_back(observer);
23 }
24
UnregisterObserver(AllocationObserver * observer)25 void StatsCollector::UnregisterObserver(AllocationObserver* observer) {
26 auto it = std::find(allocation_observers_.begin(),
27 allocation_observers_.end(), observer);
28 DCHECK_NE(allocation_observers_.end(), it);
29 allocation_observers_.erase(it);
30 }
31
NotifyAllocation(size_t bytes)32 void StatsCollector::NotifyAllocation(size_t bytes) {
33 // The current GC may not have been started. This is ok as recording considers
34 // the whole time range between garbage collections.
35 allocated_bytes_since_safepoint_ += bytes;
36 }
37
NotifyExplicitFree(size_t bytes)38 void StatsCollector::NotifyExplicitFree(size_t bytes) {
39 // See IncreaseAllocatedObjectSize for lifetime of the counter.
40 explicitly_freed_bytes_since_safepoint_ += bytes;
41 }
42
NotifySafePointForConservativeCollection()43 void StatsCollector::NotifySafePointForConservativeCollection() {
44 if (std::abs(allocated_bytes_since_safepoint_ -
45 explicitly_freed_bytes_since_safepoint_) >=
46 static_cast<int64_t>(kAllocationThresholdBytes)) {
47 AllocatedObjectSizeSafepointImpl();
48 }
49 }
50
AllocatedObjectSizeSafepointImpl()51 void StatsCollector::AllocatedObjectSizeSafepointImpl() {
52 allocated_bytes_since_end_of_marking_ +=
53 static_cast<int64_t>(allocated_bytes_since_safepoint_) -
54 static_cast<int64_t>(explicitly_freed_bytes_since_safepoint_);
55
56 // These observer methods may start or finalize GC. In case they trigger a
57 // final GC pause, the delta counters are reset there and the following
58 // observer calls are called with '0' updates.
59 ForAllAllocationObservers([this](AllocationObserver* observer) {
60 // Recompute delta here so that a GC finalization is able to clear the
61 // delta for other observer calls.
62 int64_t delta = allocated_bytes_since_safepoint_ -
63 explicitly_freed_bytes_since_safepoint_;
64 if (delta < 0) {
65 observer->AllocatedObjectSizeDecreased(static_cast<size_t>(-delta));
66 } else {
67 observer->AllocatedObjectSizeIncreased(static_cast<size_t>(delta));
68 }
69 });
70 allocated_bytes_since_safepoint_ = 0;
71 explicitly_freed_bytes_since_safepoint_ = 0;
72 }
73
NotifyMarkingStarted()74 void StatsCollector::NotifyMarkingStarted() {
75 DCHECK_EQ(GarbageCollectionState::kNotRunning, gc_state_);
76 gc_state_ = GarbageCollectionState::kMarking;
77 }
78
NotifyMarkingCompleted(size_t marked_bytes)79 void StatsCollector::NotifyMarkingCompleted(size_t marked_bytes) {
80 DCHECK_EQ(GarbageCollectionState::kMarking, gc_state_);
81 gc_state_ = GarbageCollectionState::kSweeping;
82 current_.marked_bytes = marked_bytes;
83 allocated_bytes_since_safepoint_ = 0;
84 explicitly_freed_bytes_since_safepoint_ = 0;
85
86 ForAllAllocationObservers([marked_bytes](AllocationObserver* observer) {
87 observer->ResetAllocatedObjectSize(marked_bytes);
88 });
89
90 // HeapGrowing would use the below fields to estimate allocation rate during
91 // execution of ResetAllocatedObjectSize.
92 allocated_bytes_since_end_of_marking_ = 0;
93 time_of_last_end_of_marking_ = v8::base::TimeTicks::Now();
94 }
95
GetRecentAllocationSpeedInBytesPerMs() const96 double StatsCollector::GetRecentAllocationSpeedInBytesPerMs() const {
97 v8::base::TimeTicks current_time = v8::base::TimeTicks::Now();
98 DCHECK_LE(time_of_last_end_of_marking_, current_time);
99 if (time_of_last_end_of_marking_ == current_time) return 0;
100 return allocated_bytes_since_end_of_marking_ /
101 (current_time - time_of_last_end_of_marking_).InMillisecondsF();
102 }
103
NotifySweepingCompleted()104 const StatsCollector::Event& StatsCollector::NotifySweepingCompleted() {
105 DCHECK_EQ(GarbageCollectionState::kSweeping, gc_state_);
106 gc_state_ = GarbageCollectionState::kNotRunning;
107 previous_ = std::move(current_);
108 current_ = Event();
109 return previous_;
110 }
111
allocated_object_size() const112 size_t StatsCollector::allocated_object_size() const {
113 // During sweeping we refer to the current Event as that already holds the
114 // correct marking information. In all other phases, the previous event holds
115 // the most up-to-date marking information.
116 const Event& event =
117 gc_state_ == GarbageCollectionState::kSweeping ? current_ : previous_;
118 DCHECK_GE(static_cast<int64_t>(event.marked_bytes) +
119 allocated_bytes_since_end_of_marking_,
120 0);
121 return static_cast<size_t>(static_cast<int64_t>(event.marked_bytes) +
122 allocated_bytes_since_end_of_marking_);
123 }
124
125 } // namespace internal
126 } // namespace cppgc
127