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:
parent
1105d7ba5f
commit
daaff7dfe9
3
BUILD.gn
3
BUILD.gn
@ -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",
|
||||
|
@ -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",
|
||||
|
110
include/cppgc/heap-statistics.h
Normal file
110
include/cppgc/heap-statistics.h
Normal 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_
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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]; }
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
158
src/heap/cppgc/heap-statistics-collector.cc
Normal file
158
src/heap/cppgc/heap-statistics-collector.cc
Normal 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_, ¤t_space_stats_, ¤t_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_, ¤t_space_stats_, ¤t_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_, ¤t_space_stats_, ¤t_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_, ¤t_page_stats_);
|
||||
current_space_stats_->page_stats.emplace_back(
|
||||
HeapStatistics::PageStatistics{kPageSize, 0});
|
||||
current_page_stats_ = ¤t_space_stats_->page_stats.back();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HeapStatisticsCollector::VisitLargePage(LargePage* page) {
|
||||
DCHECK_NOT_NULL(current_space_stats_);
|
||||
FinalizePage(current_space_stats_, ¤t_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
|
35
src/heap/cppgc/heap-statistics-collector.h
Normal file
35
src/heap/cppgc/heap-statistics-collector.h
Normal 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_
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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) {
|
||||
|
131
test/unittests/heap/cppgc/heap-statistics-collector-unittest.cc
Normal file
131
test/unittests/heap/cppgc/heap-statistics-collector-unittest.cc
Normal 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
|
Loading…
Reference in New Issue
Block a user