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/allocation-observer.h"
6
7 #include "src/heap/heap.h"
8 #include "src/heap/spaces.h"
9
10 namespace v8 {
11 namespace internal {
12
AddAllocationObserver(AllocationObserver * observer)13 void AllocationCounter::AddAllocationObserver(AllocationObserver* observer) {
14 #if DEBUG
15 auto it = std::find_if(observers_.begin(), observers_.end(),
16 [observer](const AllocationObserverCounter& aoc) {
17 return aoc.observer_ == observer;
18 });
19 DCHECK_EQ(observers_.end(), it);
20 #endif
21
22 if (step_in_progress_) {
23 pending_added_.push_back(AllocationObserverCounter(observer, 0, 0));
24 return;
25 }
26
27 intptr_t step_size = observer->GetNextStepSize();
28 size_t observer_next_counter = current_counter_ + step_size;
29
30 observers_.push_back(AllocationObserverCounter(observer, current_counter_,
31 observer_next_counter));
32
33 if (observers_.size() == 1) {
34 DCHECK_EQ(current_counter_, next_counter_);
35 next_counter_ = observer_next_counter;
36 } else {
37 size_t missing_bytes = next_counter_ - current_counter_;
38 next_counter_ = current_counter_ +
39 std::min(static_cast<intptr_t>(missing_bytes), step_size);
40 }
41 }
42
RemoveAllocationObserver(AllocationObserver * observer)43 void AllocationCounter::RemoveAllocationObserver(AllocationObserver* observer) {
44 auto it = std::find_if(observers_.begin(), observers_.end(),
45 [observer](const AllocationObserverCounter& aoc) {
46 return aoc.observer_ == observer;
47 });
48 DCHECK_NE(observers_.end(), it);
49
50 if (step_in_progress_) {
51 DCHECK_EQ(pending_removed_.count(observer), 0);
52 pending_removed_.insert(observer);
53 return;
54 }
55
56 observers_.erase(it);
57
58 if (observers_.size() == 0) {
59 current_counter_ = next_counter_ = 0;
60 } else {
61 size_t step_size = 0;
62
63 for (AllocationObserverCounter& observer_counter : observers_) {
64 size_t left_in_step = observer_counter.next_counter_ - current_counter_;
65 DCHECK_GT(left_in_step, 0);
66 step_size = step_size ? std::min(step_size, left_in_step) : left_in_step;
67 }
68
69 next_counter_ = current_counter_ + step_size;
70 }
71 }
72
AdvanceAllocationObservers(size_t allocated)73 void AllocationCounter::AdvanceAllocationObservers(size_t allocated) {
74 if (!IsActive()) {
75 return;
76 }
77
78 DCHECK(!step_in_progress_);
79 DCHECK_LT(allocated, next_counter_ - current_counter_);
80 current_counter_ += allocated;
81 }
82
InvokeAllocationObservers(Address soon_object,size_t object_size,size_t aligned_object_size)83 void AllocationCounter::InvokeAllocationObservers(Address soon_object,
84 size_t object_size,
85 size_t aligned_object_size) {
86 if (!IsActive()) {
87 return;
88 }
89
90 DCHECK(!step_in_progress_);
91 DCHECK_GE(aligned_object_size, next_counter_ - current_counter_);
92 DCHECK(soon_object);
93 bool step_run = false;
94 step_in_progress_ = true;
95 size_t step_size = 0;
96
97 DCHECK(pending_added_.empty());
98 DCHECK(pending_removed_.empty());
99
100 for (AllocationObserverCounter& aoc : observers_) {
101 if (aoc.next_counter_ - current_counter_ <= aligned_object_size) {
102 {
103 DisallowGarbageCollection no_gc;
104 aoc.observer_->Step(
105 static_cast<int>(current_counter_ - aoc.prev_counter_), soon_object,
106 object_size);
107 }
108 size_t observer_step_size = aoc.observer_->GetNextStepSize();
109
110 aoc.prev_counter_ = current_counter_;
111 aoc.next_counter_ =
112 current_counter_ + aligned_object_size + observer_step_size;
113 step_run = true;
114 }
115
116 size_t left_in_step = aoc.next_counter_ - current_counter_;
117 step_size = step_size ? std::min(step_size, left_in_step) : left_in_step;
118 }
119
120 CHECK(step_run);
121
122 // Now process newly added allocation observers.
123 for (AllocationObserverCounter& aoc : pending_added_) {
124 size_t observer_step_size = aoc.observer_->GetNextStepSize();
125 aoc.prev_counter_ = current_counter_;
126 aoc.next_counter_ =
127 current_counter_ + aligned_object_size + observer_step_size;
128
129 DCHECK_NE(step_size, 0);
130 step_size = std::min(step_size, aligned_object_size + observer_step_size);
131
132 observers_.push_back(aoc);
133 }
134
135 pending_added_.clear();
136
137 if (!pending_removed_.empty()) {
138 observers_.erase(
139 std::remove_if(observers_.begin(), observers_.end(),
140 [this](const AllocationObserverCounter& aoc) {
141 return pending_removed_.count(aoc.observer_) != 0;
142 }));
143 pending_removed_.clear();
144
145 // Some observers were removed, recalculate step size.
146 step_size = 0;
147 for (AllocationObserverCounter& aoc : observers_) {
148 size_t left_in_step = aoc.next_counter_ - current_counter_;
149 step_size = step_size ? std::min(step_size, left_in_step) : left_in_step;
150 }
151
152 if (observers_.empty()) {
153 next_counter_ = current_counter_ = 0;
154 step_in_progress_ = false;
155 return;
156 }
157 }
158
159 next_counter_ = current_counter_ + step_size;
160 step_in_progress_ = false;
161 }
162
PauseAllocationObserversScope(Heap * heap)163 PauseAllocationObserversScope::PauseAllocationObserversScope(Heap* heap)
164 : heap_(heap) {
165 DCHECK_EQ(heap->gc_state(), Heap::NOT_IN_GC);
166
167 for (SpaceIterator it(heap_); it.HasNext();) {
168 it.Next()->PauseAllocationObservers();
169 }
170 }
171
~PauseAllocationObserversScope()172 PauseAllocationObserversScope::~PauseAllocationObserversScope() {
173 for (SpaceIterator it(heap_); it.HasNext();) {
174 it.Next()->ResumeAllocationObservers();
175 }
176 }
177
178 } // namespace internal
179 } // namespace v8
180