cppgc: Collect heap statistics

HeapBase::CollectStatistics returns a HeapStatistics struct that can be
used by blink to populate a memory dump.

Bug: chromium:1056170
Change-Id: Ic147a02ba6b4aa77bf92cfca067da70b7e1af55b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2689181
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72660}
This commit is contained in:
Omer Katz 2021-02-11 14:57:15 +01:00 committed by Commit Bot
parent 1105d7ba5f
commit daaff7dfe9
19 changed files with 512 additions and 8 deletions

View File

@ -4666,6 +4666,7 @@ v8_source_set("cppgc_base") {
"include/cppgc/garbage-collected.h",
"include/cppgc/heap-consistency.h",
"include/cppgc/heap-state.h",
"include/cppgc/heap-statistics.h",
"include/cppgc/heap.h",
"include/cppgc/internal/api-constants.h",
"include/cppgc/internal/atomic-entry-flag.h",
@ -4719,6 +4720,8 @@ v8_source_set("cppgc_base") {
"src/heap/cppgc/heap-space.cc",
"src/heap/cppgc/heap-space.h",
"src/heap/cppgc/heap-state.cc",
"src/heap/cppgc/heap-statistics-collector.cc",
"src/heap/cppgc/heap-statistics-collector.h",
"src/heap/cppgc/heap-visitor.h",
"src/heap/cppgc/heap.cc",
"src/heap/cppgc/heap.h",

View File

@ -4,6 +4,7 @@ include_rules = [
"+cppgc/common.h",
# Used by v8-cppgc.h to bridge to cppgc.
"+cppgc/custom-space.h",
"+cppgc/heap-statistics.h",
"+cppgc/internal/process-heap.h",
"+cppgc/internal/write-barrier.h",
"+cppgc/visitor.h",

View File

@ -0,0 +1,110 @@
// Copyright 2021 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.
#ifndef INCLUDE_CPPGC_HEAP_STATISTICS_H_
#define INCLUDE_CPPGC_HEAP_STATISTICS_H_
#include <memory>
#include <string>
#include <vector>
namespace cppgc {
/**
* `HeapStatistics` contains memory consumption and utilization statistics for a
* cppgc heap.
*/
struct HeapStatistics final {
/**
* Specifies the detail level of the heap statistics. Brief statistics contain
* only the top-level allocated and used memory statistics for the entire
* heap. Detailed statistics also contain a break down per space and page, as
* well as freelist statistics and object type histograms. Note that used
* memory reported by brief statistics and detailed statistics might differ
* slightly.
*/
enum DetailLevel : uint8_t {
kBrief,
kDetailed,
};
/**
* Statistics of object types. For each type the statistics record its name,
* how many objects of that type were allocated, and the overall size used by
* these objects.
*/
struct ObjectStatistics {
/** Number of distinct types in the heap. */
size_t num_types = 0;
/** Name of each type in the heap. */
std::vector<std::string> type_name;
/** Number of allocated objects per each type. */
std::vector<size_t> type_count;
/** Overall size of allocated objects per each type. */
std::vector<size_t> type_bytes;
};
/**
* Page granularity statistics. For each page the statistics record the
* allocated memory size and overall used memory size for the page.
*/
struct PageStatistics {
/** Overall amount of memory allocated for the page. */
size_t allocated_size_bytes = 0;
/** Amount of memory actually used on the page. */
size_t used_size_bytes = 0;
};
/**
* Stastistics of the freelist (used only in non-large object spaces). For
* each bucket in the freelist the statistics record the bucket size, the
* number of freelist entries in the bucket, and the overall allocated memory
* consumed by these freelist entries.
*/
struct FreeListStatistics {
/** bucket sizes in the freelist. */
std::vector<size_t> bucket_size;
/** number of freelist entries per bucket. */
std::vector<size_t> free_count;
/** memory size concumed by freelist entries per size. */
std::vector<size_t> free_size;
};
/**
* Space granularity statistics. For each space the statistics record the
* space name, the amount of allocated memory and overall used memory for the
* space. The statistics also contain statistics for each of the space's
* pages, its freelist and the objects allocated on the space.
*/
struct SpaceStatistics {
/** The space name */
std::string name;
/** Overall amount of memory allocated for the space. */
size_t allocated_size_bytes = 0;
/** Amount of memory actually used on the space. */
size_t used_size_bytes = 0;
/** Statistics for each of the pages in the space. */
std::vector<PageStatistics> page_stats;
/** Statistics for the freelist of the space. */
FreeListStatistics free_list_stats;
/** Statistics for object allocated on the space. Filled only when
* NameProvider::HideInternalNames() is false. */
ObjectStatistics object_stats;
};
/** Overall amount of memory allocated for the heap. */
size_t allocated_size_bytes = 0;
/** Amount of memory actually used on the heap. */
size_t used_size_bytes = 0;
/** Detail level of this HeapStatistics. */
DetailLevel detail_level;
/** Statistics for each of the spaces in the heap. Filled only when
* detail_level is kDetailed. */
std::vector<SpaceStatistics> space_stats;
};
} // namespace cppgc
#endif // INCLUDE_CPPGC_HEAP_STATISTICS_H_

View File

@ -10,6 +10,7 @@
#include <vector>
#include "cppgc/custom-space.h"
#include "cppgc/heap-statistics.h"
#include "cppgc/internal/process-heap.h"
#include "cppgc/internal/write-barrier.h"
#include "cppgc/visitor.h"
@ -106,6 +107,15 @@ class V8_EXPORT CppHeap {
*/
void Terminate();
/**
* \param detail_level specifies whether should return detailed
* statistics or only brief summary statistics.
* \returns current CppHeap statistics regarding memory consumption
* and utilization.
*/
cppgc::HeapStatistics CollectStatistics(
cppgc::HeapStatistics::DetailLevel detail_level);
private:
CppHeap() = default;

View File

@ -58,6 +58,12 @@ cppgc::HeapHandle& CppHeap::GetHeapHandle() {
void CppHeap::Terminate() { internal::CppHeap::From(this)->Terminate(); }
cppgc::HeapStatistics CppHeap::CollectStatistics(
cppgc::HeapStatistics::DetailLevel detail_level) {
return internal::CppHeap::From(this)->AsBase().CollectStatistics(
detail_level);
}
void JSHeapConsistency::DijkstraMarkingBarrierSlow(
cppgc::HeapHandle& heap_handle, const TracedReferenceBase& ref) {
auto& heap_base = cppgc::internal::HeapBase::From(heap_handle);

View File

@ -191,5 +191,26 @@ bool FreeList::IsConsistent(size_t index) const {
!free_list_tails_[index]->Next());
}
void FreeList::CollectStatistics(
HeapStatistics::FreeListStatistics& free_list_stats) {
std::vector<size_t>& bucket_size = free_list_stats.bucket_size;
std::vector<size_t>& free_count = free_list_stats.free_count;
std::vector<size_t>& free_size = free_list_stats.free_size;
DCHECK(bucket_size.empty());
DCHECK(free_count.empty());
DCHECK(free_size.empty());
for (size_t i = 0; i < kPageSizeLog2; ++i) {
size_t entry_count = 0;
size_t entry_size = 0;
for (Entry* entry = free_list_heads_[i]; entry; entry = entry->Next()) {
++entry_count;
entry_size += entry->GetSize();
}
bucket_size.push_back(static_cast<size_t>(1) << i);
free_count.push_back(entry_count);
free_size.push_back(entry_size);
}
}
} // namespace internal
} // namespace cppgc

View File

@ -7,6 +7,7 @@
#include <array>
#include "include/cppgc/heap-statistics.h"
#include "src/base/macros.h"
#include "src/heap/cppgc/globals.h"
#include "src/heap/cppgc/heap-object-header.h"
@ -45,6 +46,8 @@ class V8_EXPORT_PRIVATE FreeList {
bool Contains(Block) const;
void CollectStatistics(HeapStatistics::FreeListStatistics&);
private:
class Entry;

View File

@ -63,7 +63,8 @@ class V8_EXPORT GCInfoTable final {
return table_[index];
}
GCInfoIndex NumberOfGCInfosForTesting() const { return current_index_; }
GCInfoIndex NumberOfGCInfos() const { return current_index_; }
GCInfoIndex LimitForTesting() const { return limit_; }
GCInfo& TableSlotForTesting(GCInfoIndex index) { return table_[index]; }

View File

@ -10,6 +10,7 @@
#include "src/heap/cppgc/globals.h"
#include "src/heap/cppgc/heap-object-header.h"
#include "src/heap/cppgc/heap-page.h"
#include "src/heap/cppgc/heap-statistics-collector.h"
#include "src/heap/cppgc/heap-visitor.h"
#include "src/heap/cppgc/marker.h"
#include "src/heap/cppgc/marking-verifier.h"
@ -133,5 +134,19 @@ void HeapBase::Terminate() {
disallow_gc_scope_++;
}
HeapStatistics HeapBase::CollectStatistics(
HeapStatistics::DetailLevel detail_level) {
if (detail_level == HeapStatistics::DetailLevel::kBrief) {
return {stats_collector_->allocated_memory_size(),
stats_collector_->allocated_object_size(),
HeapStatistics::DetailLevel::kBrief,
{}};
}
sweeper_.FinishIfRunning();
object_allocator_.ResetLinearAllocationBuffers();
return HeapStatisticsCollector().CollectStatistics(this);
}
} // namespace internal
} // namespace cppgc

View File

@ -8,6 +8,7 @@
#include <memory>
#include <set>
#include "include/cppgc/heap-statistics.h"
#include "include/cppgc/heap.h"
#include "include/cppgc/internal/persistent-node.h"
#include "include/cppgc/macros.h"
@ -163,6 +164,8 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
void EnableTestingAPIsForTesting() { testing_enabled_ = true; }
bool TestingEnabled() const { return testing_enabled_; }
HeapStatistics CollectStatistics(HeapStatistics::DetailLevel);
protected:
virtual void FinalizeIncrementalGarbageCollectionIfNeeded(
cppgc::Heap::StackState) = 0;

View File

@ -179,13 +179,12 @@ LargePage::LargePage(HeapBase* heap, BaseSpace* space, size_t size)
LargePage::~LargePage() = default;
namespace {
size_t LargePageAllocationSize(size_t payload_size) {
// static
size_t LargePage::AllocationSize(size_t payload_size) {
const size_t page_header_size =
RoundUp(sizeof(LargePage), kAllocationGranularity);
return page_header_size + payload_size;
}
} // namespace
// static
LargePage* LargePage::Create(PageBackend* page_backend, LargePageSpace* space,
@ -194,14 +193,14 @@ LargePage* LargePage::Create(PageBackend* page_backend, LargePageSpace* space,
DCHECK_NOT_NULL(space);
DCHECK_LE(kLargeObjectSizeThreshold, size);
const size_t allocation_size = LargePageAllocationSize(size);
const size_t allocation_size = AllocationSize(size);
auto* heap = space->raw_heap()->heap();
void* memory = page_backend->AllocateLargePageMemory(allocation_size);
LargePage* page = new (memory) LargePage(heap, space, size);
page->SynchronizedStore();
page->heap()->stats_collector()->NotifyAllocatedMemory(
LargePageAllocationSize(page->PayloadSize()));
AllocationSize(page->PayloadSize()));
return page;
}
@ -215,7 +214,7 @@ void LargePage::Destroy(LargePage* page) {
page->~LargePage();
PageBackend* backend = page->heap()->page_backend();
page->heap()->stats_collector()->NotifyFreedMemory(
LargePageAllocationSize(page->PayloadSize()));
AllocationSize(page->PayloadSize()));
backend->FreeLargePageMemory(reinterpret_cast<Address>(page));
}

View File

@ -185,6 +185,8 @@ class V8_EXPORT_PRIVATE NormalPage final : public BasePage {
class V8_EXPORT_PRIVATE LargePage final : public BasePage {
public:
// Returns the allocation size required for a payload of size |size|.
static size_t AllocationSize(size_t size);
// Allocates a new page in the detached state.
static LargePage* Create(PageBackend*, LargePageSpace*, size_t);
// Destroys and frees the page. The page must be detached from the

View File

@ -0,0 +1,158 @@
// Copyright 2021 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/cppgc/heap-statistics-collector.h"
#include <string>
#include "include/cppgc/name-provider.h"
#include "src/heap/cppgc/free-list.h"
#include "src/heap/cppgc/heap-base.h"
#include "src/heap/cppgc/heap-object-header.h"
#include "src/heap/cppgc/raw-heap.h"
#include "src/heap/cppgc/stats-collector.h"
namespace cppgc {
namespace internal {
namespace {
std::string GetNormalPageSpaceName(size_t index) {
// Check that space is not a large object space.
DCHECK_NE(RawHeap::kNumberOfRegularSpaces - 1, index);
// Handle regular normal page spaces.
if (index < RawHeap::kNumberOfRegularSpaces) {
return "NormalPageSpace" + std::to_string(index);
}
// Space is a custom space.
return "CustomSpace" +
std::to_string(index - RawHeap::kNumberOfRegularSpaces);
}
HeapStatistics::SpaceStatistics* InitializeSpace(HeapStatistics* stats,
std::string name) {
stats->space_stats.emplace_back();
HeapStatistics::SpaceStatistics* space_stats = &stats->space_stats.back();
space_stats->name = std::move(name);
if (!NameProvider::HideInternalNames()) {
const size_t num_types = GlobalGCInfoTable::Get().NumberOfGCInfos();
space_stats->object_stats.num_types = num_types;
space_stats->object_stats.type_name.resize(num_types);
space_stats->object_stats.type_count.resize(num_types);
space_stats->object_stats.type_bytes.resize(num_types);
}
return space_stats;
}
void FinalizePage(HeapStatistics::SpaceStatistics* space_stats,
HeapStatistics::PageStatistics** page_stats) {
if (*page_stats) {
DCHECK_NOT_NULL(space_stats);
space_stats->allocated_size_bytes += (*page_stats)->allocated_size_bytes;
space_stats->used_size_bytes += (*page_stats)->used_size_bytes;
}
*page_stats = nullptr;
}
void FinalizeSpace(HeapStatistics* stats,
HeapStatistics::SpaceStatistics** space_stats,
HeapStatistics::PageStatistics** page_stats) {
FinalizePage(*space_stats, page_stats);
if (*space_stats) {
DCHECK_NOT_NULL(stats);
stats->allocated_size_bytes += (*space_stats)->allocated_size_bytes;
stats->used_size_bytes += (*space_stats)->used_size_bytes;
}
*space_stats = nullptr;
}
void RecordObjectType(HeapStatistics::SpaceStatistics* space_stats,
HeapObjectHeader* header, size_t object_size) {
if (!NameProvider::HideInternalNames()) {
// Detailed names available.
GCInfoIndex gc_info_index = header->GetGCInfoIndex();
space_stats->object_stats.type_count[gc_info_index]++;
space_stats->object_stats.type_bytes[gc_info_index] += object_size;
if (space_stats->object_stats.type_name[gc_info_index].empty()) {
space_stats->object_stats.type_name[gc_info_index] =
header->GetName().value;
}
}
}
} // namespace
HeapStatistics HeapStatisticsCollector::CollectStatistics(HeapBase* heap) {
HeapStatistics stats;
stats.detail_level = HeapStatistics::DetailLevel::kDetailed;
current_stats_ = &stats;
Traverse(&heap->raw_heap());
FinalizeSpace(current_stats_, &current_space_stats_, &current_page_stats_);
DCHECK_EQ(heap->stats_collector()->allocated_memory_size(),
stats.allocated_size_bytes);
return stats;
}
bool HeapStatisticsCollector::VisitNormalPageSpace(NormalPageSpace* space) {
DCHECK_EQ(0u, space->linear_allocation_buffer().size());
FinalizeSpace(current_stats_, &current_space_stats_, &current_page_stats_);
current_space_stats_ =
InitializeSpace(current_stats_, GetNormalPageSpaceName(space->index()));
space->free_list().CollectStatistics(current_space_stats_->free_list_stats);
return false;
}
bool HeapStatisticsCollector::VisitLargePageSpace(LargePageSpace* space) {
FinalizeSpace(current_stats_, &current_space_stats_, &current_page_stats_);
current_space_stats_ = InitializeSpace(current_stats_, "LargePageSpace");
return false;
}
bool HeapStatisticsCollector::VisitNormalPage(NormalPage* page) {
DCHECK_NOT_NULL(current_space_stats_);
FinalizePage(current_space_stats_, &current_page_stats_);
current_space_stats_->page_stats.emplace_back(
HeapStatistics::PageStatistics{kPageSize, 0});
current_page_stats_ = &current_space_stats_->page_stats.back();
return false;
}
bool HeapStatisticsCollector::VisitLargePage(LargePage* page) {
DCHECK_NOT_NULL(current_space_stats_);
FinalizePage(current_space_stats_, &current_page_stats_);
HeapObjectHeader* object_header = page->ObjectHeader();
size_t object_size = page->PayloadSize();
RecordObjectType(current_space_stats_, object_header, object_size);
size_t allocated_size = LargePage::AllocationSize(object_size);
current_space_stats_->allocated_size_bytes += allocated_size;
current_space_stats_->used_size_bytes += object_size;
current_space_stats_->page_stats.emplace_back(
HeapStatistics::PageStatistics{allocated_size, object_size});
return true;
}
bool HeapStatisticsCollector::VisitHeapObjectHeader(HeapObjectHeader* header) {
DCHECK(!header->IsLargeObject());
DCHECK_NOT_NULL(current_space_stats_);
DCHECK_NOT_NULL(current_page_stats_);
if (header->IsFree()) return true;
size_t object_size = header->GetSize();
RecordObjectType(current_space_stats_, header, object_size);
current_page_stats_->used_size_bytes += object_size;
return true;
}
} // namespace internal
} // namespace cppgc

View File

@ -0,0 +1,35 @@
// Copyright 2021 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.
#ifndef V8_HEAP_CPPGC_HEAP_STATISTICS_COLLECTOR_H_
#define V8_HEAP_CPPGC_HEAP_STATISTICS_COLLECTOR_H_
#include "include/cppgc/heap-statistics.h"
#include "src/heap/cppgc/heap-visitor.h"
namespace cppgc {
namespace internal {
class HeapStatisticsCollector : private HeapVisitor<HeapStatisticsCollector> {
friend class HeapVisitor<HeapStatisticsCollector>;
public:
HeapStatistics CollectStatistics(HeapBase*);
private:
bool VisitNormalPageSpace(NormalPageSpace*);
bool VisitLargePageSpace(LargePageSpace*);
bool VisitNormalPage(NormalPage*);
bool VisitLargePage(LargePage*);
bool VisitHeapObjectHeader(HeapObjectHeader*);
HeapStatistics* current_stats_;
HeapStatistics::SpaceStatistics* current_space_stats_ = nullptr;
HeapStatistics::PageStatistics* current_page_stats_ = nullptr;
};
} // namespace internal
} // namespace cppgc
#endif // V8_HEAP_CPPGC_HEAP_STATISTICS_COLLECTOR_H_

View File

@ -215,6 +215,10 @@ void StatsCollector::NotifySweepingCompleted() {
}
}
size_t StatsCollector::allocated_memory_size() const {
return memory_allocated_bytes_;
}
size_t StatsCollector::allocated_object_size() const {
// During sweeping we refer to the current Event as that already holds the
// correct marking information. In all other phases, the previous event holds

View File

@ -263,6 +263,7 @@ class V8_EXPORT_PRIVATE StatsCollector final {
// is finished at this point.
void NotifySweepingCompleted();
size_t allocated_memory_size() const;
// Size of live objects in bytes on the heap. Based on the most recent marked
// bytes and the bytes allocated since last marking.
size_t allocated_object_size() const;

View File

@ -100,6 +100,7 @@ v8_source_set("cppgc_unittests_sources") {
"heap/cppgc/heap-growing-unittest.cc",
"heap/cppgc/heap-object-header-unittest.cc",
"heap/cppgc/heap-page-unittest.cc",
"heap/cppgc/heap-statistics-collector-unittest.cc",
"heap/cppgc/heap-unittest.cc",
"heap/cppgc/incremental-marking-schedule-unittest.cc",
"heap/cppgc/logging-unittest.cc",

View File

@ -23,7 +23,7 @@ constexpr GCInfo GetEmptyGCInfo() { return {nullptr, nullptr, nullptr, false}; }
TEST(GCInfoTableTest, InitialEmpty) {
v8::base::PageAllocator page_allocator;
GCInfoTable table(&page_allocator);
EXPECT_EQ(GCInfoTable::kMinIndex, table.NumberOfGCInfosForTesting());
EXPECT_EQ(GCInfoTable::kMinIndex, table.NumberOfGCInfos());
}
TEST(GCInfoTableTest, ResizeToMaxIndex) {

View File

@ -0,0 +1,131 @@
// Copyright 2021 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/cppgc/heap-statistics-collector.h"
#include "include/cppgc/heap-statistics.h"
#include "src/base/macros.h"
#include "src/heap/cppgc/globals.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc {
namespace internal {
class HeapStatisticsCollectorTest : public testing::TestWithHeap {};
TEST_F(HeapStatisticsCollectorTest, EmptyHeapBriefStatisitcs) {
HeapStatistics brief_stats = Heap::From(GetHeap())->CollectStatistics(
HeapStatistics::DetailLevel::kBrief);
EXPECT_EQ(HeapStatistics::DetailLevel::kBrief, brief_stats.detail_level);
EXPECT_EQ(0u, brief_stats.used_size_bytes);
EXPECT_EQ(0u, brief_stats.used_size_bytes);
EXPECT_TRUE(brief_stats.space_stats.empty());
}
TEST_F(HeapStatisticsCollectorTest, EmptyHeapDetailedStatisitcs) {
HeapStatistics detailed_stats = Heap::From(GetHeap())->CollectStatistics(
HeapStatistics::DetailLevel::kDetailed);
EXPECT_EQ(HeapStatistics::DetailLevel::kDetailed,
detailed_stats.detail_level);
EXPECT_EQ(0u, detailed_stats.used_size_bytes);
EXPECT_EQ(0u, detailed_stats.used_size_bytes);
EXPECT_EQ(RawHeap::kNumberOfRegularSpaces, detailed_stats.space_stats.size());
for (HeapStatistics::SpaceStatistics& space_stats :
detailed_stats.space_stats) {
EXPECT_EQ(0u, space_stats.used_size_bytes);
EXPECT_EQ(0u, space_stats.used_size_bytes);
EXPECT_TRUE(space_stats.page_stats.empty());
if (space_stats.name == "LargePageSpace") {
// Large page space has no free list.
EXPECT_TRUE(space_stats.free_list_stats.bucket_size.empty());
EXPECT_TRUE(space_stats.free_list_stats.free_count.empty());
EXPECT_TRUE(space_stats.free_list_stats.free_size.empty());
} else {
EXPECT_EQ(kPageSizeLog2, space_stats.free_list_stats.bucket_size.size());
EXPECT_EQ(kPageSizeLog2, space_stats.free_list_stats.free_count.size());
EXPECT_EQ(kPageSizeLog2, space_stats.free_list_stats.free_size.size());
}
}
}
namespace {
template <size_t Size>
class GCed : public GarbageCollected<GCed<Size>> {
public:
void Trace(Visitor*) const {}
private:
char array_[Size];
};
} // namespace
TEST_F(HeapStatisticsCollectorTest, NonEmptyNormalPage) {
MakeGarbageCollected<GCed<1>>(GetHeap()->GetAllocationHandle());
static constexpr size_t used_size =
RoundUp<kAllocationGranularity>(1 + sizeof(HeapObjectHeader));
HeapStatistics detailed_stats = Heap::From(GetHeap())->CollectStatistics(
HeapStatistics::DetailLevel::kDetailed);
EXPECT_EQ(HeapStatistics::DetailLevel::kDetailed,
detailed_stats.detail_level);
EXPECT_EQ(kPageSize, detailed_stats.allocated_size_bytes);
EXPECT_EQ(used_size, detailed_stats.used_size_bytes);
EXPECT_EQ(RawHeap::kNumberOfRegularSpaces, detailed_stats.space_stats.size());
bool found_non_empty_space = false;
for (const HeapStatistics::SpaceStatistics& space_stats :
detailed_stats.space_stats) {
if (space_stats.page_stats.empty()) {
EXPECT_EQ(0u, space_stats.allocated_size_bytes);
EXPECT_EQ(0u, space_stats.used_size_bytes);
continue;
}
EXPECT_NE("LargePageSpace", space_stats.name);
EXPECT_FALSE(found_non_empty_space);
found_non_empty_space = true;
EXPECT_EQ(kPageSize, space_stats.allocated_size_bytes);
EXPECT_EQ(used_size, space_stats.used_size_bytes);
EXPECT_EQ(1u, space_stats.page_stats.size());
EXPECT_EQ(kPageSize, space_stats.page_stats.back().allocated_size_bytes);
EXPECT_EQ(used_size, space_stats.page_stats.back().used_size_bytes);
}
EXPECT_TRUE(found_non_empty_space);
}
TEST_F(HeapStatisticsCollectorTest, NonEmptyLargePage) {
MakeGarbageCollected<GCed<kLargeObjectSizeThreshold>>(
GetHeap()->GetAllocationHandle());
static constexpr size_t used_size = RoundUp<kAllocationGranularity>(
kLargeObjectSizeThreshold + sizeof(HeapObjectHeader));
static constexpr size_t allocated_size =
RoundUp<kAllocationGranularity>(used_size + sizeof(LargePage));
HeapStatistics detailed_stats = Heap::From(GetHeap())->CollectStatistics(
HeapStatistics::DetailLevel::kDetailed);
EXPECT_EQ(HeapStatistics::DetailLevel::kDetailed,
detailed_stats.detail_level);
EXPECT_EQ(allocated_size, detailed_stats.allocated_size_bytes);
EXPECT_EQ(used_size, detailed_stats.used_size_bytes);
EXPECT_EQ(RawHeap::kNumberOfRegularSpaces, detailed_stats.space_stats.size());
bool found_non_empty_space = false;
for (const HeapStatistics::SpaceStatistics& space_stats :
detailed_stats.space_stats) {
if (space_stats.page_stats.empty()) {
EXPECT_EQ(0u, space_stats.allocated_size_bytes);
EXPECT_EQ(0u, space_stats.used_size_bytes);
continue;
}
EXPECT_EQ("LargePageSpace", space_stats.name);
EXPECT_FALSE(found_non_empty_space);
found_non_empty_space = true;
EXPECT_EQ(allocated_size, space_stats.allocated_size_bytes);
EXPECT_EQ(used_size, space_stats.used_size_bytes);
EXPECT_EQ(1u, space_stats.page_stats.size());
EXPECT_EQ(allocated_size,
space_stats.page_stats.back().allocated_size_bytes);
EXPECT_EQ(used_size, space_stats.page_stats.back().used_size_bytes);
}
EXPECT_TRUE(found_non_empty_space);
}
} // namespace internal
} // namespace cppgc