| // 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. |
| |
| #include "src/heap/allocation-observer.h" |
| |
| #include "src/heap/heap.h" |
| #include "src/heap/spaces.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| void AllocationCounter::AddAllocationObserver(AllocationObserver* observer) { |
| #if DEBUG |
| auto it = std::find_if(observers_.begin(), observers_.end(), |
| [observer](const AllocationObserverCounter& aoc) { |
| return aoc.observer_ == observer; |
| }); |
| DCHECK_EQ(observers_.end(), it); |
| #endif |
| |
| if (step_in_progress_) { |
| pending_added_.push_back(AllocationObserverCounter(observer, 0, 0)); |
| return; |
| } |
| |
| intptr_t step_size = observer->GetNextStepSize(); |
| size_t observer_next_counter = current_counter_ + step_size; |
| |
| observers_.push_back(AllocationObserverCounter(observer, current_counter_, |
| observer_next_counter)); |
| |
| if (observers_.size() == 1) { |
| DCHECK_EQ(current_counter_, next_counter_); |
| next_counter_ = observer_next_counter; |
| } else { |
| size_t missing_bytes = next_counter_ - current_counter_; |
| next_counter_ = |
| current_counter_ + Min(static_cast<intptr_t>(missing_bytes), step_size); |
| } |
| } |
| |
| void AllocationCounter::RemoveAllocationObserver(AllocationObserver* observer) { |
| auto it = std::find_if(observers_.begin(), observers_.end(), |
| [observer](const AllocationObserverCounter& aoc) { |
| return aoc.observer_ == observer; |
| }); |
| DCHECK_NE(observers_.end(), it); |
| |
| if (step_in_progress_) { |
| DCHECK_EQ(pending_removed_.count(observer), 0); |
| pending_removed_.insert(observer); |
| return; |
| } |
| |
| observers_.erase(it); |
| |
| if (observers_.size() == 0) { |
| current_counter_ = next_counter_ = 0; |
| } else { |
| size_t step_size = 0; |
| |
| for (AllocationObserverCounter& observer : observers_) { |
| size_t left_in_step = observer.next_counter_ - current_counter_; |
| DCHECK_GT(left_in_step, 0); |
| step_size = step_size ? Min(step_size, left_in_step) : left_in_step; |
| } |
| |
| next_counter_ = current_counter_ + step_size; |
| } |
| } |
| |
| void AllocationCounter::AdvanceAllocationObservers(size_t allocated) { |
| if (!IsActive()) { |
| return; |
| } |
| |
| DCHECK(!step_in_progress_); |
| DCHECK_LT(allocated, next_counter_ - current_counter_); |
| current_counter_ += allocated; |
| } |
| |
| void AllocationCounter::InvokeAllocationObservers(Address soon_object, |
| size_t object_size, |
| size_t aligned_object_size) { |
| if (!IsActive()) { |
| return; |
| } |
| |
| DCHECK(!step_in_progress_); |
| DCHECK_GE(aligned_object_size, next_counter_ - current_counter_); |
| DCHECK(soon_object); |
| bool step_run = false; |
| step_in_progress_ = true; |
| size_t step_size = 0; |
| |
| DCHECK(pending_added_.empty()); |
| DCHECK(pending_removed_.empty()); |
| |
| for (AllocationObserverCounter& aoc : observers_) { |
| if (aoc.next_counter_ - current_counter_ <= aligned_object_size) { |
| { |
| DisallowHeapAllocation disallow_heap_allocation; |
| aoc.observer_->Step( |
| static_cast<int>(current_counter_ - aoc.prev_counter_), soon_object, |
| object_size); |
| } |
| size_t observer_step_size = aoc.observer_->GetNextStepSize(); |
| |
| aoc.prev_counter_ = current_counter_; |
| aoc.next_counter_ = |
| current_counter_ + aligned_object_size + observer_step_size; |
| step_run = true; |
| } |
| |
| size_t left_in_step = aoc.next_counter_ - current_counter_; |
| step_size = step_size ? Min(step_size, left_in_step) : left_in_step; |
| } |
| |
| CHECK(step_run); |
| |
| // Now process newly added allocation observers. |
| for (AllocationObserverCounter& aoc : pending_added_) { |
| size_t observer_step_size = aoc.observer_->GetNextStepSize(); |
| aoc.prev_counter_ = current_counter_; |
| aoc.next_counter_ = |
| current_counter_ + aligned_object_size + observer_step_size; |
| |
| DCHECK_NE(step_size, 0); |
| step_size = Min(step_size, aligned_object_size + observer_step_size); |
| |
| observers_.push_back(aoc); |
| } |
| |
| pending_added_.clear(); |
| |
| if (!pending_removed_.empty()) { |
| observers_.erase( |
| std::remove_if(observers_.begin(), observers_.end(), |
| [this](const AllocationObserverCounter& aoc) { |
| return pending_removed_.count(aoc.observer_) != 0; |
| })); |
| pending_removed_.clear(); |
| |
| // Some observers were removed, recalculate step size. |
| step_size = 0; |
| for (AllocationObserverCounter& aoc : observers_) { |
| size_t left_in_step = aoc.next_counter_ - current_counter_; |
| step_size = step_size ? Min(step_size, left_in_step) : left_in_step; |
| } |
| |
| if (observers_.empty()) { |
| next_counter_ = current_counter_ = 0; |
| step_in_progress_ = false; |
| return; |
| } |
| } |
| |
| next_counter_ = current_counter_ + step_size; |
| step_in_progress_ = false; |
| } |
| |
| PauseAllocationObserversScope::PauseAllocationObserversScope(Heap* heap) |
| : heap_(heap) { |
| DCHECK_EQ(heap->gc_state(), Heap::NOT_IN_GC); |
| |
| for (SpaceIterator it(heap_); it.HasNext();) { |
| it.Next()->PauseAllocationObservers(); |
| } |
| } |
| |
| PauseAllocationObserversScope::~PauseAllocationObserversScope() { |
| for (SpaceIterator it(heap_); it.HasNext();) { |
| it.Next()->ResumeAllocationObservers(); |
| } |
| } |
| |
| } // namespace internal |
| } // namespace v8 |