• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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