[cppgc] Add API to query custom space sizes at last GC

The function will be used to implement the web memory measurement API.

This adds an |allocated_bytes_at_last_gc| counter to each normal page.
The counter is updated by the sweeper.

Bug: chromium:1181269
Change-Id: If6612de06f373a839fce986c71ba3dfde4d9c9c2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2880534
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74446}
This commit is contained in:
Ulan Degenbaev 2021-05-07 18:25:58 +02:00 committed by V8 LUCI CQ
parent be09ba1f12
commit 65aafbfad8
5 changed files with 73 additions and 0 deletions

View File

@ -28,6 +28,8 @@ namespace internal {
class CppHeap;
} // namespace internal
class CustomSpaceStatisticsReceiver;
/**
* Describes how V8 wrapper objects maintain references to garbage-collected C++
* objects.
@ -119,6 +121,16 @@ class V8_EXPORT CppHeap {
cppgc::HeapStatistics CollectStatistics(
cppgc::HeapStatistics::DetailLevel detail_level);
/**
* Collects statistics for the given spaces and reports them to the receiver.
*
* \param custom_spaces a collection of custom space indicies.
* \param receiver an object that gets the results.
*/
void CollectCustomSpaceStatisticsAtLastGC(
std::vector<cppgc::CustomSpaceIndex> custom_spaces,
std::unique_ptr<CustomSpaceStatisticsReceiver> receiver);
/**
* Enables a detached mode that allows testing garbage collection using
* `cppgc::testing` APIs. Once used, the heap cannot be attached to an
@ -277,6 +289,26 @@ class V8_EXPORT JSHeapConsistency final {
const TracedReferenceBase& ref);
};
/**
* Provided as input to `CppHeap::CollectCustomSpaceStatisticsAtLastGC()`.
*
* Its method is invoked with the results of the statistic collection.
*/
class CustomSpaceStatisticsReceiver {
public:
virtual ~CustomSpaceStatisticsReceiver() = default;
/**
* Reports the size of a space at the last GC. It is called for each space
* that was requested in `CollectCustomSpaceStatisticsAtLastGC()`.
*
* \param space_index The index of the space.
* \param bytes The total size of live objects in the space at the last GC.
* It is zero if there was no GC yet.
*/
virtual void AllocatedBytes(cppgc::CustomSpaceIndex space_index,
size_t bytes) = 0;
};
} // namespace v8
namespace cppgc {

View File

@ -5,6 +5,7 @@
#include "src/heap/cppgc-js/cpp-heap.h"
#include <cstdint>
#include <numeric>
#include "include/cppgc/heap-consistency.h"
#include "include/cppgc/platform.h"
@ -65,6 +66,25 @@ cppgc::HeapStatistics CppHeap::CollectStatistics(
detail_level);
}
void CppHeap::CollectCustomSpaceStatisticsAtLastGC(
std::vector<cppgc::CustomSpaceIndex> custom_spaces,
std::unique_ptr<CustomSpaceStatisticsReceiver> receiver) {
cppgc::internal::HeapBase& heap_base =
internal::CppHeap::From(this)->AsBase();
// TODO(1181269): Use tasks to help the sweeper incrementally instead of
// finalizing atomically.
heap_base.sweeper().FinishIfRunning();
for (auto custom_space_index : custom_spaces) {
const cppgc::internal::BaseSpace* space =
heap_base.raw_heap().CustomSpace(custom_space_index);
size_t allocated_bytes = std::accumulate(
space->begin(), space->end(), 0, [](size_t sum, auto* page) {
return sum + page->AllocatedBytesAtLastGC();
});
receiver->AllocatedBytes(custom_space_index, allocated_bytes);
}
}
void CppHeap::EnableDetachedGarbageCollectionsForTesting() {
return internal::CppHeap::From(this)
->EnableDetachedGarbageCollectionsForTesting();

View File

@ -69,6 +69,11 @@ ConstAddress BasePage::PayloadEnd() const {
return const_cast<BasePage*>(this)->PayloadEnd();
}
size_t BasePage::AllocatedBytesAtLastGC() const {
return is_large() ? LargePage::From(this)->AllocatedBytesAtLastGC()
: NormalPage::From(this)->AllocatedBytesAtLastGC();
}
HeapObjectHeader* BasePage::TryObjectHeaderFromInnerAddress(
void* address) const {
return const_cast<HeapObjectHeader*>(

View File

@ -46,6 +46,10 @@ class V8_EXPORT_PRIVATE BasePage {
Address PayloadEnd();
ConstAddress PayloadEnd() const;
// Returns the size of live objects on the page at the last GC.
// The counter is update after sweeping.
size_t AllocatedBytesAtLastGC() const;
// |address| must refer to real object.
template <AccessMode = AccessMode::kNonAtomic>
HeapObjectHeader& ObjectHeaderFromInnerAddress(void* address) const;
@ -169,6 +173,12 @@ class V8_EXPORT_PRIVATE NormalPage final : public BasePage {
return (PayloadStart() <= address) && (address < PayloadEnd());
}
size_t AllocatedBytesAtLastGC() const { return allocated_bytes_at_last_gc_; }
void SetAllocatedBytesAtLastGC(size_t bytes) {
allocated_bytes_at_last_gc_ = bytes;
}
PlatformAwareObjectStartBitmap& object_start_bitmap() {
return object_start_bitmap_;
}
@ -180,6 +190,7 @@ class V8_EXPORT_PRIVATE NormalPage final : public BasePage {
NormalPage(HeapBase* heap, BaseSpace* space);
~NormalPage();
size_t allocated_bytes_at_last_gc_ = 0;
PlatformAwareObjectStartBitmap object_start_bitmap_;
};
@ -215,6 +226,8 @@ class V8_EXPORT_PRIVATE LargePage final : public BasePage {
return payload_size_ - sizeof(HeapObjectHeader);
}
size_t AllocatedBytesAtLastGC() const { return ObjectSize(); }
bool PayloadContains(ConstAddress address) const {
return (PayloadStart() <= address) && (address < PayloadEnd());
}

View File

@ -194,6 +194,7 @@ typename FinalizationBuilder::ResultType SweepNormalPage(NormalPage* page) {
bitmap.Clear();
size_t largest_new_free_list_entry = 0;
size_t live_bytes = 0;
Address start_of_gap = page->PayloadStart();
for (Address begin = page->PayloadStart(), end = page->PayloadEnd();
@ -226,6 +227,7 @@ typename FinalizationBuilder::ResultType SweepNormalPage(NormalPage* page) {
bitmap.SetBit(begin);
begin += size;
start_of_gap = begin;
live_bytes += size;
}
if (start_of_gap != page->PayloadStart() &&
@ -234,6 +236,7 @@ typename FinalizationBuilder::ResultType SweepNormalPage(NormalPage* page) {
start_of_gap, static_cast<size_t>(page->PayloadEnd() - start_of_gap));
bitmap.SetBit(start_of_gap);
}
page->SetAllocatedBytesAtLastGC(live_bytes);
const bool is_empty = (start_of_gap == page->PayloadStart());
return builder.GetResult(is_empty, largest_new_free_list_entry);