1// Copyright 2021 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/heap/cppgc/heap-statistics-collector.h" 6 7#include <string> 8#include <unordered_map> 9 10#include "include/cppgc/heap-statistics.h" 11#include "include/cppgc/name-provider.h" 12#include "src/heap/cppgc/free-list.h" 13#include "src/heap/cppgc/globals.h" 14#include "src/heap/cppgc/heap-base.h" 15#include "src/heap/cppgc/heap-object-header.h" 16#include "src/heap/cppgc/raw-heap.h" 17#include "src/heap/cppgc/stats-collector.h" 18 19namespace cppgc { 20namespace internal { 21 22namespace { 23 24std::string GetNormalPageSpaceName(size_t index) { 25 // Check that space is not a large object space. 26 DCHECK_NE(RawHeap::kNumberOfRegularSpaces - 1, index); 27 // Handle regular normal page spaces. 28 if (index < RawHeap::kNumberOfRegularSpaces) { 29 return "NormalPageSpace" + std::to_string(index); 30 } 31 // Space is a custom space. 32 return "CustomSpace" + 33 std::to_string(index - RawHeap::kNumberOfRegularSpaces); 34} 35 36HeapStatistics::SpaceStatistics* InitializeSpace(HeapStatistics* stats, 37 std::string name) { 38 stats->space_stats.emplace_back(); 39 HeapStatistics::SpaceStatistics* space_stats = &stats->space_stats.back(); 40 space_stats->name = std::move(name); 41 return space_stats; 42} 43 44HeapStatistics::PageStatistics* InitializePage( 45 HeapStatistics::SpaceStatistics* stats) { 46 stats->page_stats.emplace_back(); 47 HeapStatistics::PageStatistics* page_stats = &stats->page_stats.back(); 48 return page_stats; 49} 50 51void FinalizePage(HeapStatistics::SpaceStatistics* space_stats, 52 HeapStatistics::PageStatistics** page_stats) { 53 if (*page_stats) { 54 DCHECK_NOT_NULL(space_stats); 55 space_stats->committed_size_bytes += (*page_stats)->committed_size_bytes; 56 space_stats->resident_size_bytes += (*page_stats)->resident_size_bytes; 57 space_stats->used_size_bytes += (*page_stats)->used_size_bytes; 58 } 59 *page_stats = nullptr; 60} 61 62void FinalizeSpace(HeapStatistics* stats, 63 HeapStatistics::SpaceStatistics** space_stats, 64 HeapStatistics::PageStatistics** page_stats) { 65 FinalizePage(*space_stats, page_stats); 66 if (*space_stats) { 67 DCHECK_NOT_NULL(stats); 68 stats->committed_size_bytes += (*space_stats)->committed_size_bytes; 69 stats->resident_size_bytes += (*space_stats)->resident_size_bytes; 70 stats->used_size_bytes += (*space_stats)->used_size_bytes; 71 } 72 *space_stats = nullptr; 73} 74 75void RecordObjectType( 76 std::unordered_map<const void*, size_t>& type_map, 77 std::vector<HeapStatistics::ObjectStatsEntry>& object_statistics, 78 HeapObjectHeader* header, size_t object_size) { 79 if (!NameProvider::HideInternalNames()) { 80 // Tries to insert a new entry into the typemap with a running counter. If 81 // the entry is already present, just returns the old one. 82 const auto it = type_map.insert({header->GetName().value, type_map.size()}); 83 const size_t type_index = it.first->second; 84 if (object_statistics.size() <= type_index) { 85 object_statistics.resize(type_index + 1); 86 } 87 object_statistics[type_index].allocated_bytes += object_size; 88 object_statistics[type_index].object_count++; 89 } 90} 91 92} // namespace 93 94HeapStatistics HeapStatisticsCollector::CollectDetailedStatistics( 95 HeapBase* heap) { 96 HeapStatistics stats; 97 stats.detail_level = HeapStatistics::DetailLevel::kDetailed; 98 current_stats_ = &stats; 99 100 if (!NameProvider::HideInternalNames()) { 101 // Add a dummy type so that a type index of zero has a valid mapping but 102 // shows an invalid type. 103 type_name_to_index_map_.insert({"Invalid type", 0}); 104 } 105 106 Traverse(heap->raw_heap()); 107 FinalizeSpace(current_stats_, ¤t_space_stats_, ¤t_page_stats_); 108 109 if (!NameProvider::HideInternalNames()) { 110 stats.type_names.resize(type_name_to_index_map_.size()); 111 for (auto& it : type_name_to_index_map_) { 112 stats.type_names[it.second] = reinterpret_cast<const char*>(it.first); 113 } 114 } 115 116 DCHECK_EQ(heap->stats_collector()->allocated_memory_size(), 117 stats.resident_size_bytes); 118 return stats; 119} 120 121bool HeapStatisticsCollector::VisitNormalPageSpace(NormalPageSpace& space) { 122 DCHECK_EQ(0u, space.linear_allocation_buffer().size()); 123 124 FinalizeSpace(current_stats_, ¤t_space_stats_, ¤t_page_stats_); 125 126 current_space_stats_ = 127 InitializeSpace(current_stats_, GetNormalPageSpaceName(space.index())); 128 129 space.free_list().CollectStatistics(current_space_stats_->free_list_stats); 130 131 return false; 132} 133 134bool HeapStatisticsCollector::VisitLargePageSpace(LargePageSpace& space) { 135 FinalizeSpace(current_stats_, ¤t_space_stats_, ¤t_page_stats_); 136 137 current_space_stats_ = InitializeSpace(current_stats_, "LargePageSpace"); 138 139 return false; 140} 141 142bool HeapStatisticsCollector::VisitNormalPage(NormalPage& page) { 143 DCHECK_NOT_NULL(current_space_stats_); 144 FinalizePage(current_space_stats_, ¤t_page_stats_); 145 146 current_page_stats_ = InitializePage(current_space_stats_); 147 current_page_stats_->committed_size_bytes = kPageSize; 148 current_page_stats_->resident_size_bytes = 149 kPageSize - page.discarded_memory(); 150 return false; 151} 152 153bool HeapStatisticsCollector::VisitLargePage(LargePage& page) { 154 DCHECK_NOT_NULL(current_space_stats_); 155 FinalizePage(current_space_stats_, ¤t_page_stats_); 156 157 const size_t object_size = page.PayloadSize(); 158 const size_t allocated_size = LargePage::AllocationSize(object_size); 159 current_page_stats_ = InitializePage(current_space_stats_); 160 current_page_stats_->committed_size_bytes = allocated_size; 161 current_page_stats_->resident_size_bytes = allocated_size; 162 return false; 163} 164 165bool HeapStatisticsCollector::VisitHeapObjectHeader(HeapObjectHeader& header) { 166 if (header.IsFree()) return true; 167 168 DCHECK_NOT_NULL(current_space_stats_); 169 DCHECK_NOT_NULL(current_page_stats_); 170 // For the purpose of heap statistics, the header counts towards the allocated 171 // object size. 172 const size_t allocated_object_size = 173 header.IsLargeObject() 174 ? LargePage::From( 175 BasePage::FromPayload(const_cast<HeapObjectHeader*>(&header))) 176 ->PayloadSize() 177 : header.AllocatedSize(); 178 RecordObjectType(type_name_to_index_map_, 179 current_page_stats_->object_statistics, &header, 180 allocated_object_size); 181 current_page_stats_->used_size_bytes += allocated_object_size; 182 return true; 183} 184 185} // namespace internal 186} // namespace cppgc 187