blob: 24f85c136e96c5565ad0a5b09f6f68d9fba0b609 [file] [log] [blame]
/*
* Copyright (c) 2019-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "pas_config.h"
#if LIBPAS_ENABLED
#include "pas_status_reporter.h"
#include "pas_all_heaps.h"
#include "pas_all_shared_page_directories.h"
#include "pas_baseline_allocator_table.h"
#include "pas_bitfit_directory.h"
#include "pas_bitfit_heap.h"
#include "pas_bitfit_size_class.h"
#include "pas_bitfit_view.h"
#include "pas_compact_bootstrap_free_heap.h"
#include "pas_compact_large_utility_free_heap.h"
#include "pas_fd_stream.h"
#include "pas_heap.h"
#include "pas_heap_lock.h"
#include "pas_heap_summary.h"
#include "pas_heap_table.h"
#include "pas_large_heap.h"
#include "pas_large_map.h"
#include "pas_large_sharing_pool.h"
#include "pas_large_utility_free_heap.h"
#include "pas_log.h"
#include "pas_page_sharing_pool.h"
#include "pas_segregated_size_directory_inlines.h"
#include "pas_segregated_heap.h"
#include "pas_segregated_shared_page_directory.h"
#include "pas_segregated_size_directory.h"
#include "pas_simple_type.h"
#include "pas_stream.h"
#include "pas_thread_local_cache.h"
#include "pas_thread_local_cache_layout.h"
#include "pas_thread_local_cache_node.h"
#include "pas_utility_heap.h"
#include <pthread.h>
#include <unistd.h>
unsigned pas_status_reporter_enabled = 0;
unsigned pas_status_reporter_period_in_microseconds = 10 * 1000 * 1000;
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
static void dump_ratio_initial(
pas_stream* stream, const char* full, uintptr_t numerator, uintptr_t denominator)
{
uintptr_t value;
if (!denominator) {
pas_stream_printf(stream, " ");
return;
}
if (numerator == denominator) {
pas_stream_printf(stream, "%s", full);
return;
}
value = 10 * numerator / denominator;
PAS_ASSERT(value <= 9);
if (!value && numerator) {
pas_stream_printf(stream, "e");
return;
}
pas_stream_printf(stream, "%lu", value);
}
static void dump_occupancy_initial(pas_stream* stream, pas_heap_summary summary)
{
dump_ratio_initial(stream, "F", summary.allocated, summary.allocated + summary.free);
}
static void dump_arrow(pas_stream* stream, uintptr_t index_of_arrow)
{
uintptr_t index;
for (index = 0; index < index_of_arrow; ++index)
pas_stream_printf(stream, " ");
pas_stream_printf(stream, "^");
}
static void report_bitfit_directory_contents(
pas_stream* stream, pas_bitfit_directory* directory, const char* prefix)
{
size_t index;
pas_bitfit_size_class* size_class;
if (!pas_bitfit_directory_size(directory))
return;
pas_stream_printf(stream, "%s Occupancy: ", prefix);
for (index = 0; index < pas_bitfit_directory_size(directory); ++index) {
pas_bitfit_view* view;
view = pas_bitfit_directory_get_view(directory, index);
if (!view) {
pas_stream_printf(stream, " ");
continue;
}
dump_occupancy_initial(stream, pas_bitfit_view_compute_summary(view));
}
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s Max Free: ", prefix);
for (index = 0; index < pas_bitfit_directory_size(directory); ++index) {
pas_bitfit_max_free max_free;
max_free = pas_bitfit_directory_get_max_free(directory, index);
if (max_free == PAS_BITFIT_MAX_FREE_EMPTY)
pas_stream_printf(stream, "E");
else
dump_ratio_initial(stream, "U", max_free, PAS_BITFIT_MAX_FREE_UNPROCESSED);
}
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s Empty (bit): ", prefix);
for (index = 0; index < pas_bitfit_directory_size(directory); ++index) {
if (pas_bitfit_directory_get_empty_bit_at_index(directory, index))
pas_stream_printf(stream, "x");
else
pas_stream_printf(stream, " ");
}
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s Last Empty+1: ", prefix);
dump_arrow(stream, (uintptr_t)directory->last_empty_plus_one.value);
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s Committed: ", prefix);
for (index = 0; index < pas_bitfit_directory_size(directory); ++index) {
pas_bitfit_view* view;
view = pas_bitfit_directory_get_view(directory, index);
if (!view) {
pas_stream_printf(stream, " ");
continue;
}
if (view->is_owned)
pas_stream_printf(stream, "x");
else
pas_stream_printf(stream, " ");
}
pas_stream_printf(stream, "\n");
for (size_class = pas_compact_atomic_bitfit_size_class_ptr_load(&directory->largest_size_class);
size_class;
size_class = pas_compact_atomic_bitfit_size_class_ptr_load(&size_class->next_smaller)) {
uintptr_t index_of_first_free;
index_of_first_free = size_class->first_free.value;
pas_stream_printf(stream, "%s%7u Bytes: ", prefix, size_class->size);
dump_arrow(stream, index_of_first_free);
pas_stream_printf(stream, "\n");
}
pas_stream_printf(stream, "%s Unprocessed: ", prefix);
dump_arrow(stream, directory->first_unprocessed_free.value);
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s Empty (free): ", prefix);
dump_arrow(stream, directory->first_empty.value);
pas_stream_printf(stream, "\n");
}
void pas_status_reporter_dump_bitfit_directory(
pas_stream* stream, pas_bitfit_directory* directory)
{
if (directory->config_kind == pas_bitfit_page_config_kind_null)
return;
pas_stream_printf(stream, " %s Global Dir (%p): ",
pas_bitfit_page_config_variant_get_capitalized_string(
pas_bitfit_page_config_kind_get_config(directory->config_kind)->variant),
directory);
pas_heap_summary_dump(pas_bitfit_directory_compute_summary(directory), stream);
pas_stream_printf(stream, "\n");
report_bitfit_directory_contents(stream, directory, " ");
}
static void report_segregated_directory_contents(
pas_stream* stream, pas_segregated_directory* directory, const char* prefix)
{
size_t index;
if (!pas_segregated_directory_size(directory))
return;
pas_stream_printf(stream, "%s Kind: ", prefix);
for (index = 0; index < pas_segregated_directory_size(directory); ++index) {
pas_segregated_view view;
view = pas_segregated_directory_get(directory, index);
pas_stream_printf(
stream, "%c",
pas_segregated_view_kind_get_character_code(
pas_segregated_view_get_kind(pas_segregated_directory_get(directory, index))));
}
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s Occupancy: ", prefix);
for (index = 0; index < pas_segregated_directory_size(directory); ++index) {
pas_segregated_view view;
pas_heap_summary summary;
view = pas_segregated_directory_get(directory, index);
summary = pas_segregated_view_compute_summary(
view, pas_segregated_page_config_kind_get_config(directory->page_config_kind));
dump_occupancy_initial(stream, summary);
}
pas_stream_printf(stream, "\n");
if (directory->directory_kind == pas_segregated_shared_page_directory_kind) {
pas_segregated_page_config* page_config;
uintptr_t payload_begin;
uintptr_t payload_end;
page_config = pas_segregated_page_config_kind_get_config(directory->page_config_kind);
payload_begin = pas_round_up_to_power_of_2(
page_config->base.page_object_payload_offset,
pas_segregated_page_config_min_align(*page_config));
payload_end = pas_segregated_page_config_object_payload_end_offset_from_boundary(*page_config);
pas_stream_printf(stream, "%s Bump: ", prefix);
for (index = 0; index < pas_segregated_directory_size(directory); ++index) {
pas_segregated_view view;
pas_segregated_shared_view* shared_view;
unsigned bump_offset;
view = pas_segregated_directory_get(directory, index);
shared_view = pas_segregated_view_get_shared(view);
bump_offset = shared_view->bump_offset;
if (!bump_offset) {
pas_stream_printf(stream, "0");
continue;
}
PAS_ASSERT(bump_offset >= payload_begin);
PAS_ASSERT(bump_offset <= payload_end);
dump_ratio_initial(stream, "F", bump_offset - payload_begin, payload_end - payload_begin);
}
pas_stream_printf(stream, "\n");
}
pas_stream_printf(stream, "%s Eligible: ", prefix);
for (index = 0; index < pas_segregated_directory_size(directory); ++index) {
if (pas_segregated_directory_is_eligible(directory, index))
pas_stream_printf(stream, "x");
else
pas_stream_printf(stream, " ");
}
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s First Elgbl: ", prefix);
dump_arrow(stream, (uintptr_t)pas_segregated_directory_get_first_eligible(directory).value);
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s Empty: ", prefix);
for (index = 0; index < pas_segregated_directory_size(directory); ++index) {
if (pas_segregated_directory_is_empty(directory, index))
pas_stream_printf(stream, "x");
else
pas_stream_printf(stream, " ");
}
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s Last Empt+1: ", prefix);
dump_arrow(stream, (uintptr_t)pas_segregated_directory_get_last_empty_plus_one(directory).value);
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, "%s Committed: ", prefix);
for (index = 0; index < pas_segregated_directory_size(directory); ++index) {
if (pas_segregated_directory_is_committed(directory, index))
pas_stream_printf(stream, "x");
else
pas_stream_printf(stream, " ");
}
pas_stream_printf(stream, "\n");
}
void pas_status_reporter_dump_segregated_size_directory(
pas_stream* stream, pas_segregated_size_directory* directory)
{
pas_heap_summary partial_summary;
pas_heap_summary exclusive_summary;
size_t index;
pas_segregated_size_directory_data* data;
pas_stream_printf(
stream,
" Global Size Dir %p(%u/%s): Num Views: %zu",
directory,
directory->object_size,
pas_segregated_page_config_kind_get_string(directory->base.page_config_kind),
pas_segregated_directory_size(&directory->base));
if (pas_segregated_directory_data_ptr_load(&directory->base.data))
pas_stream_printf(stream, ", Has Base Data");
data = pas_segregated_size_directory_data_ptr_load(&directory->data);
if (data)
pas_stream_printf(stream, ", Has Data");
if (pas_segregated_size_directory_has_tlc_allocator(directory))
pas_stream_printf(stream, ", Has TLA");
if (pas_segregated_size_directory_are_exclusive_views_enabled(directory))
pas_stream_printf(stream, ", Enabled Exclusives");
pas_stream_printf(stream, "\n");
partial_summary = pas_heap_summary_create_empty();
exclusive_summary = pas_heap_summary_create_empty();
for (index = 0; index < pas_segregated_directory_size(&directory->base); ++index) {
pas_segregated_view view;
pas_heap_summary view_summary;
view = pas_segregated_directory_get(&directory->base, index);
view_summary = pas_segregated_view_compute_summary(
view, pas_segregated_page_config_kind_get_config(directory->base.page_config_kind));
if (pas_segregated_view_is_partial(view))
partial_summary = pas_heap_summary_add(partial_summary, view_summary);
else {
PAS_ASSERT(pas_segregated_view_is_some_exclusive(view));
exclusive_summary = pas_heap_summary_add(exclusive_summary, view_summary);
}
}
if (!pas_heap_summary_is_empty(partial_summary)) {
pas_stream_printf(
stream,
" Partials: ");
pas_heap_summary_dump(partial_summary, stream);
pas_stream_printf(stream, "\n");
}
if (!pas_heap_summary_is_empty(exclusive_summary)) {
pas_stream_printf(
stream,
" Exclusives: ");
pas_heap_summary_dump(exclusive_summary, stream);
pas_stream_printf(stream, "\n");
}
report_segregated_directory_contents(stream, &directory->base, " ");
}
void pas_status_reporter_dump_segregated_shared_page_directory(
pas_stream* stream, pas_segregated_shared_page_directory* directory)
{
pas_heap_summary summary;
pas_stream_printf(
stream,
" Shared Page Dir %p(%s): Num Views: %zu, ",
directory,
pas_segregated_page_config_kind_get_string(directory->base.page_config_kind),
pas_segregated_directory_size(&directory->base));
summary = pas_segregated_directory_compute_summary(&directory->base);
pas_heap_summary_dump(summary, stream);
pas_stream_printf(stream, "\n");
if (pas_status_reporter_enabled >= 3)
report_segregated_directory_contents(stream, &directory->base, " ");
}
void pas_status_reporter_dump_large_heap(pas_stream* stream, pas_large_heap* heap)
{
pas_heap_summary summary;
pas_stream_printf(
stream,
"Large %p: ",
heap);
summary = pas_large_heap_compute_summary(heap);
pas_heap_summary_dump(summary, stream);
}
void pas_status_reporter_dump_large_map(pas_stream* stream)
{
pas_stream_printf(stream, " Large Map:\n");
pas_stream_printf(
stream,
" Tiny Map: Num Entries: %zu, Num Deleted: %zu, Table Size: %zu\n",
pas_tiny_large_map_hashtable_instance.key_count,
pas_tiny_large_map_hashtable_instance.deleted_count,
pas_tiny_large_map_hashtable_instance.table_size);
pas_stream_printf(
stream,
" Small Fallback Map: Num Entries: %zu, Num Deleted: %zu, Table Size: %zu\n",
pas_small_large_map_hashtable_instance.key_count,
pas_small_large_map_hashtable_instance.deleted_count,
pas_small_large_map_hashtable_instance.table_size);
pas_stream_printf(
stream,
" Fallback Map: Num Entries: %zu, Num Deleted: %zu, Table Size: %zu\n",
pas_large_map_hashtable_instance.key_count,
pas_large_map_hashtable_instance.deleted_count,
pas_large_map_hashtable_instance.table_size);
}
void pas_status_reporter_dump_heap_table(pas_stream* stream)
{
pas_stream_printf(
stream,
"Heap Table Size: %u",
pas_heap_table_bump_index);
}
void pas_status_reporter_dump_immortal_heap(pas_stream* stream)
{
pas_stream_printf(
stream,
"Alloc Internal: %zu, External: %zu",
pas_immortal_heap_allocated_internal,
pas_immortal_heap_allocated_external);
}
void pas_status_reporter_dump_compact_large_utility_free_heap(pas_stream* stream)
{
pas_heap_summary summary;
summary = pas_compact_large_utility_free_heap_compute_summary();
pas_heap_summary_dump(summary, stream);
}
void pas_status_reporter_dump_large_utility_free_heap(pas_stream* stream)
{
pas_heap_summary summary;
summary = pas_large_utility_free_heap_compute_summary();
pas_heap_summary_dump(summary, stream);
}
void pas_status_reporter_dump_compact_bootstrap_free_heap(pas_stream* stream)
{
pas_stream_printf(
stream,
"Alloc: %zu, Peak Alloc: %zu, Mapped: %zu, Free: %zu",
pas_compact_bootstrap_free_heap_num_allocated_object_bytes,
pas_compact_bootstrap_free_heap_num_allocated_object_bytes_peak,
pas_compact_bootstrap_free_heap.num_mapped_bytes,
pas_compact_bootstrap_free_heap_get_num_free_bytes());
}
void pas_status_reporter_dump_bootstrap_free_heap(pas_stream* stream)
{
pas_stream_printf(
stream,
"Alloc: %zu, Peak Alloc: %zu, Mapped: %zu, Free: %zu",
pas_bootstrap_free_heap_num_allocated_object_bytes,
pas_bootstrap_free_heap_num_allocated_object_bytes_peak,
pas_bootstrap_free_heap.num_mapped_bytes,
pas_bootstrap_free_heap_get_num_free_bytes());
}
static bool dump_segregated_heap_directory_callback(
pas_segregated_heap* heap,
pas_segregated_size_directory* directory,
void* arg)
{
pas_stream* stream;
PAS_UNUSED_PARAM(heap);
stream = arg;
pas_status_reporter_dump_segregated_size_directory(stream, directory);
return true;
}
void pas_status_reporter_dump_bitfit_heap(pas_stream* stream, pas_bitfit_heap* heap)
{
pas_stream_printf(stream, " Bitfit Heap %p: ", heap);
pas_heap_summary_dump(pas_bitfit_heap_compute_summary(heap), stream);
pas_stream_printf(stream, "\n");
if (pas_status_reporter_enabled >= 3) {
pas_bitfit_page_config_variant variant;
for (PAS_EACH_BITFIT_PAGE_CONFIG_VARIANT_ASCENDING(variant)) {
pas_status_reporter_dump_bitfit_directory(
stream,
pas_bitfit_heap_get_directory(heap, variant));
}
}
}
void pas_status_reporter_dump_segregated_heap(pas_stream* stream, pas_segregated_heap* heap)
{
bool comma;
pas_heap_summary summary;
pas_bitfit_heap* bitfit_heap;
pas_stream_printf(stream, " Segregated Heap %p: ", heap);
comma = false;
if (pas_segregated_heap_rare_data_ptr_load(&heap->rare_data)) {
pas_stream_print_comma(stream, &comma, ", ");
pas_stream_printf(stream, "Has Rare Data");
}
if (heap->index_to_small_size_directory) {
pas_stream_print_comma(stream, &comma, ", ");
pas_stream_printf(stream, "Has Index Lookup");
}
pas_stream_printf(stream, ": ");
summary = pas_segregated_heap_compute_summary(heap);
pas_heap_summary_dump(summary, stream);
pas_stream_printf(stream, "\n");
if (pas_status_reporter_enabled >= 3) {
pas_segregated_heap_for_each_size_directory(
heap, dump_segregated_heap_directory_callback, stream);
}
bitfit_heap = pas_compact_atomic_bitfit_heap_ptr_load(&heap->bitfit_heap);
if (bitfit_heap)
pas_status_reporter_dump_bitfit_heap(stream, bitfit_heap);
}
void pas_status_reporter_dump_heap(pas_stream* stream, pas_heap* heap)
{
pas_heap_config* config;
pas_heap_summary summary;
config = pas_heap_config_kind_get_config(heap->config_kind);
pas_stream_printf(stream, " Heap %p:\n", heap);
pas_stream_printf(stream, " Size = %zu, Alignment = %zu\n",
config->get_type_size(heap->type),
config->get_type_alignment(heap->type));
#if PAS_ENABLE_ISO
if (config->kind == pas_heap_config_kind_iso) {
pas_simple_type type;
type = (pas_simple_type)heap->type;
if (pas_simple_type_has_key(type))
pas_stream_printf(stream, " Key = %p\n", pas_simple_type_key(type));
}
#endif
summary = pas_heap_compute_summary(heap, pas_lock_is_held);
pas_stream_printf(stream, " Total Summary: ");
pas_heap_summary_dump(summary, stream);
pas_stream_printf(stream, "\n");
pas_status_reporter_dump_segregated_heap(stream, &heap->segregated_heap);
pas_stream_printf(stream, " ");
pas_status_reporter_dump_large_heap(stream, &heap->large_heap);
pas_stream_printf(stream, "\n");
}
typedef struct {
pas_stream* stream;
size_t count;
} dump_all_heaps_data;
static bool dump_all_heaps_heap_callback(pas_heap* heap, void* arg)
{
dump_all_heaps_data* data;
data = arg;
pas_status_reporter_dump_heap(data->stream, heap);
data->count++;
return true;
}
void pas_status_reporter_dump_all_heaps(pas_stream* stream)
{
dump_all_heaps_data data;
data.stream = stream;
data.count = 0;
pas_all_heaps_for_each_heap(dump_all_heaps_heap_callback, &data);
pas_stream_printf(stream, " Num Heaps: %zu\n", data.count);
}
static bool dump_all_shared_page_directories_directory_callback(
pas_segregated_shared_page_directory* directory,
void* arg)
{
pas_stream* stream;
stream = arg;
pas_status_reporter_dump_segregated_shared_page_directory(stream, directory);
return true;
}
void pas_status_reporter_dump_all_shared_page_directories(pas_stream* stream)
{
pas_stream_printf(stream, " Shared Page Directories:\n");
pas_all_shared_page_directories_for_each(
dump_all_shared_page_directories_directory_callback,
stream);
}
void pas_status_reporter_dump_all_heaps_non_utility_summaries(pas_stream* stream)
{
pas_stream_printf(stream, " All Heaps Non-Utility Segregated Summary: ");
pas_heap_summary_dump(pas_all_heaps_compute_total_non_utility_segregated_summary(), stream);
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, " All Heaps Non-Utility Bitfit Summary: ");
pas_heap_summary_dump(pas_all_heaps_compute_total_non_utility_bitfit_summary(), stream);
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, " All Heaps Non-Utility Large Summary: ");
pas_heap_summary_dump(pas_all_heaps_compute_total_non_utility_large_summary(), stream);
pas_stream_printf(stream, "\n");
}
static bool dump_large_sharing_pool_node_callback(pas_large_sharing_node* node,
void* arg)
{
pas_stream* stream;
stream = arg;
pas_stream_printf(stream, " %p...%p: %s, %zu/%zu live (%.0lf%%), %llu",
(void*)node->range.begin,
(void*)node->range.end,
pas_commit_mode_get_string(node->is_committed),
node->num_live_bytes,
pas_range_size(node->range),
100. * (double)node->num_live_bytes / (double)pas_range_size(node->range),
node->use_epoch);
if (node->synchronization_style != pas_physical_memory_is_locked_by_virtual_range_common_lock) {
pas_stream_printf(
stream, ", %s",
pas_physical_memory_synchronization_style_get_string(node->synchronization_style));
}
pas_stream_printf(stream, "\n");
return true;
}
void pas_status_reporter_dump_large_sharing_pool(pas_stream* stream)
{
pas_stream_printf(stream, " Large sharing pool contents:\n");
pas_large_sharing_pool_for_each(dump_large_sharing_pool_node_callback, stream, pas_lock_is_held);
}
void pas_status_reporter_dump_utility_heap(pas_stream* stream)
{
pas_stream_printf(stream, " Utility Heap:\n");
pas_status_reporter_dump_segregated_heap(stream, &pas_utility_segregated_heap);
}
#define SIZE_HISTOGRAM_MAX_SIZE 65536
#define SIZE_HISTOGRAM_BUCKET_SIZE 256
#define SIZE_HISTOGRAM_NUM_BUCKETS (SIZE_HISTOGRAM_MAX_SIZE / SIZE_HISTOGRAM_BUCKET_SIZE + 1)
typedef struct {
size_t segregated_exclusive_fragmentation_size_histogram[SIZE_HISTOGRAM_NUM_BUCKETS];
size_t segregated_partial_fragmentation_size_histogram[SIZE_HISTOGRAM_NUM_BUCKETS];
size_t segregated_exclusive_fragmentation;
size_t segregated_shared_fragmentation;
size_t large_fragmentation;
} total_fragmentation_data;
static void add_to_size_histogram(size_t* histogram,
size_t size,
size_t value)
{
PAS_ASSERT(size <= SIZE_HISTOGRAM_MAX_SIZE);
histogram[size / SIZE_HISTOGRAM_BUCKET_SIZE] += value;
}
static void dump_histogram(pas_stream* stream, size_t* histogram)
{
size_t size;
for (size = 0; size <= SIZE_HISTOGRAM_MAX_SIZE; size += SIZE_HISTOGRAM_BUCKET_SIZE) {
size_t value;
value = histogram[size / SIZE_HISTOGRAM_BUCKET_SIZE];
if (!value)
continue;
pas_stream_printf(stream,
" %zu..%zu: %zu\n",
size, size + SIZE_HISTOGRAM_BUCKET_SIZE - 1,
value);
}
}
static bool total_fragmentation_size_directory_callback(
pas_segregated_heap* heap,
pas_segregated_size_directory* directory,
void* arg)
{
total_fragmentation_data* data;
size_t index;
PAS_UNUSED_PARAM(heap);
data = arg;
for (index = 0; index < pas_segregated_directory_size(&directory->base); ++index) {
pas_segregated_view view;
size_t fragmentation;
view = pas_segregated_directory_get(&directory->base, index);
fragmentation = pas_heap_summary_fragmentation(
pas_segregated_view_compute_summary(
view,
pas_segregated_page_config_kind_get_config(directory->base.page_config_kind)));
if (!pas_segregated_view_is_some_exclusive(view)) {
PAS_ASSERT(pas_segregated_view_is_partial(view));
add_to_size_histogram(data->segregated_partial_fragmentation_size_histogram,
directory->object_size,
fragmentation);
continue;
}
data->segregated_exclusive_fragmentation += fragmentation;
add_to_size_histogram(data->segregated_exclusive_fragmentation_size_histogram,
directory->object_size,
fragmentation);
}
return true;
}
static bool total_fragmentation_heap_callback(pas_heap* heap, void* arg)
{
total_fragmentation_data* data;
data = arg;
pas_segregated_heap_for_each_size_directory(
&heap->segregated_heap, total_fragmentation_size_directory_callback, data);
data->large_fragmentation += pas_heap_summary_fragmentation(
pas_large_heap_compute_summary(&heap->large_heap));
return true;
}
static bool total_fragmentation_shared_page_directory_callback(
pas_segregated_shared_page_directory* directory,
void* arg)
{
total_fragmentation_data* data;
data = arg;
data->segregated_shared_fragmentation += pas_heap_summary_fragmentation(
pas_segregated_directory_compute_summary(&directory->base));
return true;
}
void pas_status_reporter_dump_total_fragmentation(pas_stream* stream)
{
total_fragmentation_data data;
pas_zero_memory(&data, sizeof(data));
pas_all_heaps_for_each_heap(total_fragmentation_heap_callback, &data);
pas_all_shared_page_directories_for_each(
total_fragmentation_shared_page_directory_callback, &data);
pas_segregated_heap_for_each_size_directory(
&pas_utility_segregated_heap, total_fragmentation_size_directory_callback, &data);
data.large_fragmentation += pas_heap_summary_fragmentation(
pas_large_utility_free_heap_compute_summary());
pas_stream_printf(stream, " Segregated Exclusive Fragmentation Histogram:\n");
dump_histogram(stream, data.segregated_exclusive_fragmentation_size_histogram);
pas_stream_printf(stream, " Segregated Partial Fragmentation Histogram:\n");
dump_histogram(stream, data.segregated_partial_fragmentation_size_histogram);
pas_stream_printf(stream, " Segregated Exclusive Fragmentation: %zu\n",
data.segregated_exclusive_fragmentation);
pas_stream_printf(stream, " Segregated Shared Fragmentation: %zu\n",
data.segregated_shared_fragmentation);
pas_stream_printf(stream, " Total Segregated Fragmentation: %zu\n",
data.segregated_exclusive_fragmentation +
data.segregated_shared_fragmentation);
pas_stream_printf(stream, " Large Fragmentation: %zu\n", data.large_fragmentation);
pas_stream_printf(stream, " Total Fragmentation: %zu\n",
data.segregated_exclusive_fragmentation +
data.segregated_shared_fragmentation +
data.large_fragmentation);
}
typedef struct {
size_t num_directories_with_data;
size_t num_directories_with_tlas;
size_t num_directories_with_exclusives;
size_t num_directories;
size_t num_heaps;
} tier_up_rate_data;
static bool tier_up_rate_size_directory_callback(
pas_segregated_heap* heap,
pas_segregated_size_directory* directory,
void* arg)
{
tier_up_rate_data* data;
PAS_UNUSED_PARAM(heap);
data = arg;
if (pas_segregated_size_directory_data_ptr_load(&directory->data))
data->num_directories_with_data++;
if (pas_segregated_size_directory_has_tlc_allocator(directory))
data->num_directories_with_tlas++;
if (pas_segregated_size_directory_are_exclusive_views_enabled(directory))
data->num_directories_with_exclusives++;
data->num_directories++;
return true;
}
static bool tier_up_rate_heap_callback(pas_heap* heap, void* arg)
{
tier_up_rate_data* data;
data = arg;
pas_segregated_heap_for_each_size_directory(
&heap->segregated_heap, tier_up_rate_size_directory_callback, data);
data->num_heaps++;
return true;
}
static void dump_directory_tier_up_rate(pas_stream* stream,
const char* name,
size_t count,
tier_up_rate_data* data)
{
pas_stream_printf(stream,
" %s: %zu/%zu (%.0lf%%)\n",
name,
count,
data->num_directories,
100. * count / data->num_directories);
}
void pas_status_reporter_dump_tier_up_rates(pas_stream* stream)
{
tier_up_rate_data data;
pas_zero_memory(&data, sizeof(data));
pas_all_heaps_for_each_heap(tier_up_rate_heap_callback, &data);
dump_directory_tier_up_rate(
stream, "Num Size Directories With Data", data.num_directories_with_data, &data);
dump_directory_tier_up_rate(
stream, "Num Size Directories With TLAs", data.num_directories_with_tlas, &data);
dump_directory_tier_up_rate(
stream, "Num Size Directories With Exclusives", data.num_directories_with_exclusives,
&data);
}
static const char* allocator_state(pas_local_allocator* allocator)
{
if (!pas_local_allocator_is_active(allocator))
return "inactive";
if (pas_segregated_view_is_partial(allocator->view))
return "partial";
return "exclusive";
}
void pas_status_reporter_dump_baseline_allocators(pas_stream* stream)
{
size_t index;
pas_stream_printf(stream, " Baseline Allocators:\n");
if (!pas_baseline_allocator_table) {
pas_stream_printf(stream, " N/A\n");
return;
}
for (index = 0; index < PAS_NUM_BASELINE_ALLOCATORS; ++index) {
pas_local_allocator* allocator;
pas_segregated_view view;
allocator = &pas_baseline_allocator_table[index].u.allocator;
view = allocator->view;
pas_stream_printf(stream,
" %zu: directory = %p, %s\n",
index,
view ? pas_segregated_view_get_size_directory(view) : NULL,
allocator_state(allocator));
}
}
void pas_status_reporter_dump_thread_local_caches(pas_stream* stream)
{
pas_thread_local_cache_node* node;
pas_thread_local_cache_layout_node layout_node;
size_t index;
pas_stream_printf(stream, " Thread Local Cache Layout:\n");
for (PAS_THREAD_LOCAL_CACHE_LAYOUT_EACH_ALLOCATOR(layout_node)) {
pas_stream_printf(stream, " %u: %s, directory = %p\n",
pas_thread_local_cache_layout_node_get_allocator_index_generic(layout_node),
pas_thread_local_cache_layout_node_kind_get_string(
pas_thread_local_cache_layout_node_get_kind(layout_node)),
pas_thread_local_cache_layout_node_get_directory(layout_node));
}
pas_stream_printf(stream, " Thread Local Caches:\n");
for (index = 0, node = pas_thread_local_cache_node_first; node; node = node->next, ++index) {
pas_thread_local_cache* cache;
unsigned num_logged_objects;
unsigned index_in_deallocation_log;
cache = node->cache;
pas_stream_printf(stream, " %p(%zu): node = %p\n", cache, index, node);
if (!cache)
continue;
num_logged_objects = 0;
for (index_in_deallocation_log = cache->deallocation_log_index; index_in_deallocation_log--;) {
if (cache->deallocation_log[index_in_deallocation_log])
num_logged_objects++;
}
pas_stream_printf(stream,
" Deallocation logged objects = %u\n",
num_logged_objects);
for (PAS_THREAD_LOCAL_CACHE_LAYOUT_EACH_ALLOCATOR(layout_node)) {
pas_allocator_index allocator_index;
pas_local_allocator* allocator;
if (!pas_thread_local_cache_layout_node_represents_allocator(layout_node))
continue;
allocator_index =
pas_thread_local_cache_layout_node_get_allocator_index_for_allocator(layout_node);
if (allocator_index >= cache->allocator_index_upper_bound)
break;
allocator = pas_thread_local_cache_get_local_allocator_impl(cache, allocator_index);
pas_stream_printf(stream,
" %u: directory = %p, %s\n",
allocator_index,
pas_segregated_view_get_size_directory(allocator->view),
allocator_state(allocator));
}
}
}
void pas_status_reporter_dump_configuration(pas_stream* stream)
{
pas_stream_printf(stream, " Mprotect Decommitted: %s\n",
pas_page_malloc_mprotect_decommitted ? "yes" : "no");
}
void pas_status_reporter_dump_everything(pas_stream* stream)
{
pas_stream_printf(stream, "%d: Heap Status:\n", getpid());
pas_status_reporter_dump_all_heaps(stream);
pas_status_reporter_dump_all_shared_page_directories(stream);
pas_status_reporter_dump_all_heaps_non_utility_summaries(stream);
if (pas_status_reporter_enabled >= 3)
pas_status_reporter_dump_large_sharing_pool(stream);
pas_status_reporter_dump_utility_heap(stream);
if (pas_status_reporter_enabled >= 3) {
pas_status_reporter_dump_large_map(stream);
pas_status_reporter_dump_baseline_allocators(stream);
pas_status_reporter_dump_thread_local_caches(stream);
pas_stream_printf(stream, " Heap Table: ");
pas_status_reporter_dump_heap_table(stream);
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, " Immortal Heap: ");
pas_status_reporter_dump_immortal_heap(stream);
pas_stream_printf(stream, "\n");
}
pas_stream_printf(stream, " Compact Large Utility Free Heap: ");
pas_status_reporter_dump_compact_large_utility_free_heap(stream);
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, " Large Utility Free Heap: ");
pas_status_reporter_dump_large_utility_free_heap(stream);
pas_stream_printf(stream, "\n");
if (pas_status_reporter_enabled >= 3) {
pas_status_reporter_dump_total_fragmentation(stream);
pas_status_reporter_dump_tier_up_rates(stream);
}
pas_stream_printf(stream, " Compact Bootstrap Free Heap: ");
pas_status_reporter_dump_compact_bootstrap_free_heap(stream);
pas_stream_printf(stream, "\n");
pas_stream_printf(stream, " Bootstrap Free Heap: ");
pas_status_reporter_dump_bootstrap_free_heap(stream);
pas_stream_printf(stream, "\n");
pas_status_reporter_dump_configuration(stream);
}
static void* status_reporter_thread_main(void* arg)
{
pas_fd_stream fd_stream;
PAS_UNUSED_PARAM(arg);
pas_fd_stream_construct(&fd_stream, PAS_LOG_DEFAULT_FD);
for (;;) {
usleep(pas_status_reporter_period_in_microseconds);
PAS_ASSERT(pas_status_reporter_enabled);
if (pas_status_reporter_enabled == 1) {
pas_fd_stream_printf(&fd_stream, "%d: Num Heaps: %zu\n", getpid(), pas_all_heaps_count);
continue;
}
pas_heap_lock_lock();
pas_status_reporter_dump_everything((pas_stream*)&fd_stream);
pas_heap_lock_unlock();
}
PAS_ASSERT(!"Should not be reached");
return NULL;
}
static void start_reporter(void)
{
int result;
pthread_t thread;
result = pthread_create(&thread, NULL, status_reporter_thread_main, NULL);
PAS_ASSERT(!result);
pthread_detach(thread);
}
void pas_status_reporter_start_if_necessary(void)
{
if (pas_status_reporter_enabled)
pthread_once(&once_control, start_reporter);
}
#endif /* LIBPAS_ENABLED */