Skip to content

Commit

Permalink
Reland "[profiler] proper observation of old space inline allocations"
Browse files Browse the repository at this point in the history
This is a reland of 672a41c
Original change's description:
> [profiler] proper observation of old space inline allocations
> 
> Bug: chromium:633920
> Change-Id: I9a2f4a89f6b9c0f63cb3b166b06a88a12f0a203c
> Reviewed-on: https://chromium-review.googlesource.com/631696
> Commit-Queue: Ali Ijaz Sheikh <ofrobots@google.com>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#48043}

Bug: chromium:633920
Change-Id: I6fe743d31b8ff26f3858488d4c014c62d3c85add
Reviewed-on: https://chromium-review.googlesource.com/671127
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Commit-Queue: Ali Ijaz Sheikh <ofrobots@google.com>
Cr-Commit-Position: refs/heads/master@{#48085}
  • Loading branch information
ofrobots authored and Commit Bot committed Sep 20, 2017
1 parent f2cd10d commit ec952aa
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 69 deletions.
11 changes: 9 additions & 2 deletions src/heap/spaces-inl.h
Expand Up @@ -369,6 +369,11 @@ AllocationResult PagedSpace::AllocateRawAligned(int size_in_bytes,

AllocationResult PagedSpace::AllocateRaw(int size_in_bytes,
AllocationAlignment alignment) {
DCHECK(top() >= top_on_previous_step_);
size_t bytes_since_last =
top_on_previous_step_ ? top() - top_on_previous_step_ : 0;

DCHECK_IMPLIES(!SupportsInlineAllocation(), bytes_since_last == 0);
#ifdef V8_HOST_ARCH_32_BIT
AllocationResult result =
alignment == kDoubleAligned
Expand All @@ -378,11 +383,13 @@ AllocationResult PagedSpace::AllocateRaw(int size_in_bytes,
AllocationResult result = AllocateRawUnaligned(size_in_bytes);
#endif
HeapObject* heap_obj = nullptr;
if (!result.IsRetry() && result.To(&heap_obj)) {
AllocationStep(heap_obj->address(), size_in_bytes);
if (!result.IsRetry() && result.To(&heap_obj) && !is_local()) {
AllocationStep(static_cast<int>(size_in_bytes + bytes_since_last),
heap_obj->address(), size_in_bytes);
DCHECK_IMPLIES(
heap()->incremental_marking()->black_allocation(),
heap()->incremental_marking()->marking_state()->IsBlack(heap_obj));
StartNextInlineAllocationStep();
}
return result;
}
Expand Down
122 changes: 81 additions & 41 deletions src/heap/spaces.cc
Expand Up @@ -1328,13 +1328,15 @@ STATIC_ASSERT(static_cast<ObjectSpace>(1 << AllocationSpace::MAP_SPACE) ==

void Space::AddAllocationObserver(AllocationObserver* observer) {
allocation_observers_.push_back(observer);
StartNextInlineAllocationStep();
}

void Space::RemoveAllocationObserver(AllocationObserver* observer) {
auto it = std::find(allocation_observers_.begin(),
allocation_observers_.end(), observer);
DCHECK(allocation_observers_.end() != it);
allocation_observers_.erase(it);
StartNextInlineAllocationStep();
}

void Space::PauseAllocationObservers() { allocation_observers_paused_ = true; }
Expand All @@ -1343,11 +1345,12 @@ void Space::ResumeAllocationObservers() {
allocation_observers_paused_ = false;
}

void Space::AllocationStep(Address soon_object, int size) {
void Space::AllocationStep(int bytes_since_last, Address soon_object,
int size) {
if (!allocation_observers_paused_) {
heap()->CreateFillerObjectAt(soon_object, size, ClearRecordedSlots::kNo);
for (AllocationObserver* observer : allocation_observers_) {
observer->AllocationStep(size, soon_object, size);
observer->AllocationStep(bytes_since_last, soon_object, size);
}
}
}
Expand All @@ -1367,7 +1370,8 @@ PagedSpace::PagedSpace(Heap* heap, AllocationSpace space,
: Space(heap, space, executable),
anchor_(this),
free_list_(this),
locked_page_(nullptr) {
locked_page_(nullptr),
top_on_previous_step_(0) {
area_size_ = MemoryAllocator::PageAreaSize(space);
accounting_stats_.Clear();

Expand Down Expand Up @@ -1596,6 +1600,48 @@ void PagedSpace::SetAllocationInfo(Address top, Address limit) {
}
}

void PagedSpace::DecreaseLimit(Address new_limit) {
Address old_limit = limit();
DCHECK_LE(top(), new_limit);
DCHECK_GE(old_limit, new_limit);
if (new_limit != old_limit) {
SetTopAndLimit(top(), new_limit);
Free(new_limit, old_limit - new_limit);
if (heap()->incremental_marking()->black_allocation()) {
Page::FromAllocationAreaAddress(new_limit)->DestroyBlackArea(new_limit,
old_limit);
}
}
}

Address PagedSpace::ComputeLimit(Address start, Address end,
size_t size_in_bytes) {
DCHECK_GE(end - start, size_in_bytes);

if (heap()->inline_allocation_disabled()) {
// Keep the linear allocation area to fit exactly the requested size.
return start + size_in_bytes;
} else if (!allocation_observers_paused_ && !allocation_observers_.empty() &&
identity() == OLD_SPACE && !is_local()) {
// Generated code may allocate inline from the linear allocation area for
// Old Space. To make sure we can observe these allocations, we use a lower
// limit.
size_t step = RoundSizeDownToObjectAlignment(
static_cast<int>(GetNextInlineAllocationStepSize()));
return Max(start + size_in_bytes, Min(start + step, end));
} else {
// The entire node can be used as the linear allocation area.
return end;
}
}

void PagedSpace::StartNextInlineAllocationStep() {
if (!allocation_observers_paused_ && SupportsInlineAllocation()) {
top_on_previous_step_ = allocation_observers_.empty() ? 0 : top();
DecreaseLimit(ComputeLimit(top(), limit(), 0));
}
}

void PagedSpace::MarkAllocationInfoBlack() {
DCHECK(heap()->incremental_marking()->black_allocation());
Address current_top = top();
Expand Down Expand Up @@ -1641,6 +1687,12 @@ void PagedSpace::EmptyAllocationInfo() {
}
}

if (top_on_previous_step_) {
DCHECK(current_top >= top_on_previous_step_);
AllocationStep(static_cast<int>(current_top - top_on_previous_step_),
nullptr, 0);
top_on_previous_step_ = 0;
}
SetTopAndLimit(NULL, NULL);
DCHECK_GE(current_limit, current_top);
Free(current_top, current_limit - current_top);
Expand Down Expand Up @@ -2083,16 +2135,6 @@ void NewSpace::StartNextInlineAllocationStep() {
}
}

void NewSpace::AddAllocationObserver(AllocationObserver* observer) {
Space::AddAllocationObserver(observer);
StartNextInlineAllocationStep();
}

void NewSpace::RemoveAllocationObserver(AllocationObserver* observer) {
Space::RemoveAllocationObserver(observer);
StartNextInlineAllocationStep();
}

void NewSpace::PauseAllocationObservers() {
// Do a step to account for memory allocated so far.
InlineAllocationStep(top(), top(), nullptr, 0);
Expand All @@ -2101,12 +2143,28 @@ void NewSpace::PauseAllocationObservers() {
UpdateInlineAllocationLimit(0);
}

void PagedSpace::PauseAllocationObservers() {
// Do a step to account for memory allocated so far.
if (top_on_previous_step_) {
int bytes_allocated = static_cast<int>(top() - top_on_previous_step_);
AllocationStep(bytes_allocated, nullptr, 0);
}
Space::PauseAllocationObservers();
top_on_previous_step_ = 0;
}

void NewSpace::ResumeAllocationObservers() {
DCHECK(top_on_previous_step_ == 0);
Space::ResumeAllocationObservers();
StartNextInlineAllocationStep();
}

// TODO(ofrobots): refactor into SpaceWithLinearArea
void PagedSpace::ResumeAllocationObservers() {
DCHECK(top_on_previous_step_ == 0);
Space::ResumeAllocationObservers();
StartNextInlineAllocationStep();
}

void NewSpace::InlineAllocationStep(Address top, Address new_top,
Address soon_object, size_t size) {
Expand Down Expand Up @@ -2881,7 +2939,6 @@ bool FreeList::Allocate(size_t size_in_bytes) {
if (new_node == nullptr) return false;

DCHECK_GE(new_node_size, size_in_bytes);
size_t bytes_left = new_node_size - size_in_bytes;

#ifdef DEBUG
for (size_t i = 0; i < size_in_bytes / kPointerSize; i++) {
Expand All @@ -2895,38 +2952,21 @@ bool FreeList::Allocate(size_t size_in_bytes) {
// candidate.
DCHECK(!MarkCompactCollector::IsOnEvacuationCandidate(new_node));

const size_t kThreshold = IncrementalMarking::kAllocatedThreshold;

// Memory in the linear allocation area is counted as allocated. We may free
// a little of this again immediately - see below.
owner_->IncreaseAllocatedBytes(new_node_size,
Page::FromAddress(new_node->address()));

if (owner_->heap()->inline_allocation_disabled()) {
// Keep the linear allocation area to fit exactly the requested size.
// Return the rest to the free list.
owner_->Free(new_node->address() + size_in_bytes, bytes_left);
owner_->SetAllocationInfo(new_node->address(),
new_node->address() + size_in_bytes);
} else if (bytes_left > kThreshold &&
owner_->heap()->incremental_marking()->IsMarkingIncomplete() &&
FLAG_incremental_marking &&
!owner_->is_local()) { // Not needed on CompactionSpaces.
size_t linear_size = owner_->RoundSizeDownToObjectAlignment(kThreshold);
// We don't want to give too large linear areas to the allocator while
// incremental marking is going on, because we won't check again whether
// we want to do another increment until the linear area is used up.
DCHECK_GE(new_node_size, size_in_bytes + linear_size);
owner_->Free(new_node->address() + size_in_bytes + linear_size,
new_node_size - size_in_bytes - linear_size);
owner_->SetAllocationInfo(
new_node->address(), new_node->address() + size_in_bytes + linear_size);
} else {
// Normally we give the rest of the node to the allocator as its new
// linear allocation area.
owner_->SetAllocationInfo(new_node->address(),
new_node->address() + new_node_size);
Address start = new_node->address();
Address end = new_node->address() + new_node_size;
Address limit = owner_->ComputeLimit(start, end, size_in_bytes);
DCHECK_LE(limit, end);
DCHECK_LE(size_in_bytes, limit - start);
if (limit != end) {
owner_->Free(limit, end - limit);
}
owner_->SetAllocationInfo(start, limit);

return true;
}

Expand Down Expand Up @@ -3314,7 +3354,7 @@ AllocationResult LargeObjectSpace::AllocateRaw(int object_size,
if (heap()->incremental_marking()->black_allocation()) {
heap()->incremental_marking()->marking_state()->WhiteToBlack(object);
}
AllocationStep(object->address(), object_size);
AllocationStep(object_size, object->address(), object_size);
DCHECK_IMPLIES(
heap()->incremental_marking()->black_allocation(),
heap()->incremental_marking()->marking_state()->IsBlack(object));
Expand Down
47 changes: 24 additions & 23 deletions src/heap/spaces.h
Expand Up @@ -903,17 +903,17 @@ class Space : public Malloced {
// Identity used in error reporting.
AllocationSpace identity() { return id_; }

V8_EXPORT_PRIVATE virtual void AddAllocationObserver(
AllocationObserver* observer);
void AddAllocationObserver(AllocationObserver* observer);

V8_EXPORT_PRIVATE virtual void RemoveAllocationObserver(
AllocationObserver* observer);
void RemoveAllocationObserver(AllocationObserver* observer);

V8_EXPORT_PRIVATE virtual void PauseAllocationObservers();

V8_EXPORT_PRIVATE virtual void ResumeAllocationObservers();

void AllocationStep(Address soon_object, int size);
V8_EXPORT_PRIVATE virtual void StartNextInlineAllocationStep() {}

void AllocationStep(int bytes_since_last, Address soon_object, int size);

// Return the total amount committed memory for this space, i.e., allocatable
// memory and page headers.
Expand Down Expand Up @@ -2071,15 +2071,8 @@ class V8_EXPORT_PRIVATE PagedSpace : NON_EXPORTED_BASE(public Space) {

void ResetFreeList() { free_list_.Reset(); }

// Set space allocation info.
void SetTopAndLimit(Address top, Address limit) {
DCHECK(top == limit ||
Page::FromAddress(top) == Page::FromAddress(limit - 1));
MemoryChunk::UpdateHighWaterMark(allocation_info_.top());
allocation_info_.Reset(top, limit);
}

void SetAllocationInfo(Address top, Address limit);
void PauseAllocationObservers() override;
void ResumeAllocationObservers() override;

// Empty space allocation info, returning unused area to free list.
void EmptyAllocationInfo();
Expand Down Expand Up @@ -2184,6 +2177,21 @@ class V8_EXPORT_PRIVATE PagedSpace : NON_EXPORTED_BASE(public Space) {
// multiple tasks hold locks on pages while trying to sweep each others pages.
void AnnounceLockedPage(Page* page) { locked_page_ = page; }

Address ComputeLimit(Address start, Address end, size_t size_in_bytes);
void SetAllocationInfo(Address top, Address limit);

private:
// Set space allocation info.
void SetTopAndLimit(Address top, Address limit) {
DCHECK(top == limit ||
Page::FromAddress(top) == Page::FromAddress(limit - 1));
MemoryChunk::UpdateHighWaterMark(allocation_info_.top());
allocation_info_.Reset(top, limit);
}
void DecreaseLimit(Address new_limit);
void StartNextInlineAllocationStep() override;
bool SupportsInlineAllocation() { return identity() == OLD_SPACE; }

protected:
// PagedSpaces that should be included in snapshots have different, i.e.,
// smaller, initial pages.
Expand Down Expand Up @@ -2246,6 +2254,7 @@ class V8_EXPORT_PRIVATE PagedSpace : NON_EXPORTED_BASE(public Space) {
base::Mutex space_mutex_;

Page* locked_page_;
Address top_on_previous_step_;

friend class IncrementalMarking;
friend class MarkCompactCollector;
Expand Down Expand Up @@ -2647,14 +2656,6 @@ class NewSpace : public Space {
UpdateInlineAllocationLimit(0);
}

// Allows observation of inline allocation. The observer->Step() method gets
// called after every step_size bytes have been allocated (approximately).
// This works by adjusting the allocation limit to a lower value and adjusting
// it after each step.
void AddAllocationObserver(AllocationObserver* observer) override;

void RemoveAllocationObserver(AllocationObserver* observer) override;

// Get the extent of the inactive semispace (for use as a marking stack,
// or to zap it). Notice: space-addresses are not necessarily on the
// same page, so FromSpaceStart() might be above FromSpaceEnd().
Expand Down Expand Up @@ -2761,7 +2762,7 @@ class NewSpace : public Space {
// different when we cross a page boundary or reset the space.
void InlineAllocationStep(Address top, Address new_top, Address soon_object,
size_t size);
void StartNextInlineAllocationStep();
void StartNextInlineAllocationStep() override;

friend class SemiSpaceIterator;
};
Expand Down
7 changes: 5 additions & 2 deletions src/profiler/sampling-heap-profiler.h
Expand Up @@ -172,8 +172,11 @@ class SamplingAllocationObserver : public AllocationObserver {
void Step(int bytes_allocated, Address soon_object, size_t size) override {
USE(heap_);
DCHECK(heap_->gc_state() == Heap::NOT_IN_GC);
DCHECK(soon_object);
profiler_->SampleObject(soon_object, size);
if (soon_object) {
// TODO(ofrobots): it would be better to sample the next object rather
// than skipping this sample epoch if soon_object happens to be null.
profiler_->SampleObject(soon_object, size);
}
}

intptr_t GetNextStepSize() override { return GetNextSampleInterval(rate_); }
Expand Down
3 changes: 2 additions & 1 deletion test/cctest/heap/heap-utils.cc
Expand Up @@ -32,6 +32,7 @@ int FixedArrayLenFromSize(int size) {

std::vector<Handle<FixedArray>> FillOldSpacePageWithFixedArrays(Heap* heap,
int remainder) {
PauseAllocationObserversScope pause_observers(heap);
std::vector<Handle<FixedArray>> handles;
Isolate* isolate = heap->isolate();
const int kArraySize = 128;
Expand Down Expand Up @@ -203,7 +204,7 @@ void ForceEvacuationCandidate(Page* page) {
int remaining = static_cast<int>(limit - top);
space->heap()->CreateFillerObjectAt(top, remaining,
ClearRecordedSlots::kNo);
space->SetTopAndLimit(nullptr, nullptr);
space->EmptyAllocationInfo();
}
}

Expand Down
1 change: 1 addition & 0 deletions test/cctest/heap/test-invalidated-slots.cc
Expand Up @@ -20,6 +20,7 @@ namespace heap {

Page* HeapTester::AllocateByteArraysOnPage(
Heap* heap, std::vector<ByteArray*>* byte_arrays) {
PauseAllocationObserversScope pause_observers(heap);
const int kLength = 256 - ByteArray::kHeaderSize;
const int kSize = ByteArray::SizeFor(kLength);
CHECK_EQ(kSize, 256);
Expand Down

0 comments on commit ec952aa

Please sign in to comment.