From dbd50f23db3304be4509f91b68e6ed2be752247d Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Wed, 26 May 2021 23:47:11 -0400 Subject: [PATCH] gmain: Add debug metrics for gnome-shell --- glib/ghash.c | 37 ++-- glib/ghook.c | 4 +- glib/gmain.c | 368 ++++++++++++++++++++++++++++++++++++++++ glib/gslice.c | 207 +++++++++++++++++++++- glib/gslice.h | 47 ++++- glib/gvarianttypeinfo.c | 6 +- gobject/gatomicarray.c | 2 +- gobject/gtype.c | 4 +- 8 files changed, 641 insertions(+), 34 deletions(-) diff --git a/glib/ghash.c b/glib/ghash.c index 0a9e73956..2491f3ca7 100644 --- a/glib/ghash.c +++ b/glib/ghash.c @@ -528,9 +528,9 @@ g_hash_table_remove_all_nodes (GHashTable *hash_table, g_hash_table_set_shift (hash_table, HASH_TABLE_MIN_SHIFT); if (!destruction) { - hash_table->keys = g_new0 (gpointer, hash_table->size); + hash_table->keys = g_slice_alloc0_with_name (sizeof (gpointer) * hash_table->size, "GHashTable::keys"); hash_table->values = hash_table->keys; - hash_table->hashes = g_new0 (guint, hash_table->size); + hash_table->hashes = g_slice_alloc0_with_name (sizeof (guint) * hash_table->size, "GHashTable::hashes"); } else { @@ -560,10 +560,10 @@ g_hash_table_remove_all_nodes (GHashTable *hash_table, /* Destroy old storage space. */ if (old_keys != old_values) - g_free (old_values); + g_slice_free1_with_name (sizeof (gpointer) * old_size, old_values, "GHashTable::values"); - g_free (old_keys); - g_free (old_hashes); + g_slice_free1_with_name (sizeof (gpointer) * old_size, old_keys, "GHashTable::keys"); + g_slice_free1_with_name (sizeof (guint) * old_size, old_hashes, "GHashTable::hashes"); } /* @@ -591,12 +591,12 @@ g_hash_table_resize (GHashTable *hash_table) old_size = hash_table->size; g_hash_table_set_shift_from_size (hash_table, hash_table->nnodes * 2); - new_keys = g_new0 (gpointer, hash_table->size); + new_keys = g_slice_alloc0_with_name (sizeof (gpointer) * hash_table->size, "GHashTable::keys"); if (hash_table->keys == hash_table->values) new_values = new_keys; else - new_values = g_new0 (gpointer, hash_table->size); - new_hashes = g_new0 (guint, hash_table->size); + new_values = g_slice_alloc0_with_name (sizeof (gpointer) * hash_table->size, "GHashTable::values"); + new_hashes = g_slice_alloc0_with_name (sizeof (guint) * hash_table->size, "GHashTable::hashes"); for (i = 0; i < old_size; i++) { @@ -622,10 +622,10 @@ g_hash_table_resize (GHashTable *hash_table) } if (hash_table->keys != hash_table->values) - g_free (hash_table->values); + g_slice_free1_with_name (sizeof (gpointer) * old_size, hash_table->values, "GHashTable::values"); - g_free (hash_table->keys); - g_free (hash_table->hashes); + g_slice_free1_with_name (sizeof (gpointer) * old_size, hash_table->keys, "GHashTable::keys"); + g_slice_free1_with_name (sizeof (guint) * old_size, hash_table->hashes, "GHashTable::hashes"); hash_table->keys = new_keys; hash_table->values = new_values; @@ -790,7 +790,7 @@ g_hash_table_new_full (GHashFunc hash_func, GHashTable *hash_table; gboolean needs_hash_table_metrics = FALSE, needs_hash_table_totals = FALSE; HASH_TABLE_MIN_SHIFT = atoi(getenv ("G_HASH_TABLE_MIN_SHIFT")? : "3"); - hash_table = g_slice_new (GHashTable); + hash_table = g_slice_new0 (GHashTable); g_hash_table_set_shift (hash_table, HASH_TABLE_MIN_SHIFT); hash_table->nnodes = 0; hash_table->last_sweep = 0; @@ -803,9 +803,9 @@ g_hash_table_new_full (GHashFunc hash_func, #endif hash_table->key_destroy_func = key_destroy_func; hash_table->value_destroy_func = value_destroy_func; - hash_table->keys = g_new0 (gpointer, hash_table->size); + hash_table->keys = g_slice_alloc0_with_name (sizeof (gpointer) * hash_table->size, "GHashTable::keys"); hash_table->values = hash_table->keys; - hash_table->hashes = g_new0 (guint, hash_table->size); + hash_table->hashes = g_slice_alloc0_with_name (sizeof (guint) * hash_table->size, "GHashTable::hashes"); G_LOCK (hash_tables); if (g_metrics_enabled ()) @@ -1080,7 +1080,7 @@ g_hash_table_insert_node (GHashTable *hash_table, * split the table. */ if (G_UNLIKELY (hash_table->keys == hash_table->values && hash_table->keys[node_index] != new_value)) - hash_table->values = g_memdup2 (hash_table->keys, sizeof (gpointer) * hash_table->size); + hash_table->values = g_slice_copy_with_name (sizeof (gpointer) * hash_table->size, hash_table->keys, "GHashTable::values"); /* Step 3: Actually do the write */ hash_table->values[node_index] = new_value; @@ -1215,9 +1215,10 @@ g_hash_table_unref (GHashTable *hash_table) { g_hash_table_remove_all_nodes (hash_table, TRUE, TRUE); if (hash_table->keys != hash_table->values) - g_free (hash_table->values); - g_free (hash_table->keys); - g_free (hash_table->hashes); + g_slice_free1_with_name (sizeof (gpointer) * hash_table->size, hash_table->values, "GHashTable::values"); + + g_slice_free1_with_name (sizeof (gpointer) * hash_table->size, hash_table->keys, "GHashTable::keys"); + g_slice_free1_with_name (sizeof (guint) * hash_table->size, hash_table->hashes, "GHashTable::hashes"); G_LOCK (hash_tables); if (hash_tables_list != NULL) g_metrics_list_remove_item (hash_tables_list, hash_table); diff --git a/glib/ghook.c b/glib/ghook.c index 00187bf79..bd3eb27ae 100644 --- a/glib/ghook.c +++ b/glib/ghook.c @@ -268,7 +268,7 @@ g_hook_alloc (GHookList *hook_list) g_return_val_if_fail (hook_list != NULL, NULL); g_return_val_if_fail (hook_list->is_setup, NULL); - hook = g_slice_alloc0 (hook_list->hook_size); + hook = g_slice_alloc0_with_name (hook_list->hook_size, "GHook"); hook->data = NULL; hook->next = NULL; hook->prev = NULL; @@ -300,7 +300,7 @@ g_hook_free (GHookList *hook_list, if(hook_list->finalize_hook != NULL) hook_list->finalize_hook (hook_list, hook); - g_slice_free1 (hook_list->hook_size, hook); + g_slice_free1_with_name (hook_list->hook_size, hook, "GHook"); } /** diff --git a/glib/gmain.c b/glib/gmain.c index 4de30fcd2..9e9ec3bc3 100644 --- a/glib/gmain.c +++ b/glib/gmain.c @@ -606,6 +606,306 @@ g_main_context_new_with_next_id (guint next_id) return ret; } +static GMetricsInstanceCounter *sources_counter = NULL; +static GMetricsFile *total_sources_metrics_file; +static GMetricsFile *sources_metrics_file; +static GMetricsFile *memory_metrics_file; +static GMetricsFile *slice_metrics_file; +static GMetricsFile *slice_metrics_traces_file; +static long old_total_sources = 0; + +static gboolean +fetch_current_memory_stats (char **vm_rss, + char **vm_data, + char **rss_anon, + char **vm_hwm, + char **vm_size, + char **vm_peak, + char **rss_file, + char **rss_shmem, + char **threads, + char **fd_size) +{ + char *status_contents; + gsize length; + gboolean got_contents; + GString *line_buffer; + gsize i; + + got_contents = g_file_get_contents ("/proc/self/status", + &status_contents, + &length, + NULL); + + if (!got_contents) + return FALSE; + + line_buffer = g_string_new (""); + for (i = 0; i <= length; i++) + { + char **key_and_value; + const char *key; + char *value, *end; + if (i != length && status_contents[i] != '\n') + { + g_string_append_c (line_buffer, status_contents[i]); + continue; + } + + key_and_value = g_strsplit (line_buffer->str, ":", 2); + g_string_set_size (line_buffer, 0); + + key = key_and_value[0]; + + if (key == NULL) + { + g_strfreev (key_and_value); + continue; + } + + value = g_strdup (g_strchug (key_and_value[1])); + end = strstr (value, " "); if (end) *end = '\0'; + if (strcmp (key, "VmRSS") == 0) + *vm_rss = value; + else if (strcmp (key, "VmData") == 0) + *vm_data = value; + else if (strcmp (key, "RssAnon") == 0) + *rss_anon = value; + else if (strcmp (key, "VmHWM") == 0) + *vm_hwm = value; + else if (strcmp (key, "VmSize") == 0) + *vm_size = value; + else if (strcmp (key, "VmPeak") == 0) + *vm_peak = value; + else if (strcmp (key, "RssFile") == 0) + *rss_file = value; + else if (strcmp (key, "RssShmem") == 0) + *rss_shmem = value; + else if (strcmp (key, "Threads") == 0) + *threads = value; + else if (strcmp (key, "FDSize") == 0) + *fd_size = value; + else + g_free (value); + + g_strfreev (key_and_value); + } + g_string_free (line_buffer, TRUE); + + g_free (status_contents); + + return TRUE; +} + +static void +on_metrics_timeout (void) +{ + GSList *context_node; + long new_total_sources = 0; + long change; + + if (memory_metrics_file) + { + char *vm_rss = NULL; + char *vm_data = NULL; + char *rss_anon = NULL; + char *vm_hwm = NULL; + char *vm_size = NULL; + char *vm_peak = NULL; + char *rss_file = NULL; + char *rss_shmem = NULL; + char *threads = NULL; + char *fd_size = NULL; + gsize slice_allocated = 0; + + slice_allocated = g_slice_get_total_allocated_memory (); + + g_metrics_file_start_record (memory_metrics_file); + if (fetch_current_memory_stats (&vm_rss, + &vm_data, + &rss_anon, + &vm_hwm, + &vm_size, + &vm_peak, + &rss_file, + &rss_shmem, + &threads, + &fd_size)) + { + g_metrics_file_add_row (memory_metrics_file, + vm_rss? : "", + vm_data? : "", + rss_anon? : "", + vm_hwm? : "", + slice_allocated, + vm_size? : "", + vm_peak? : "", + rss_file? : "", + rss_shmem? : "", + threads? : "", + fd_size? : ""); + g_free (vm_rss); + g_free (vm_data); + g_free (rss_anon); + g_free (vm_hwm); + g_free (vm_size); + g_free (vm_peak); + g_free (rss_file); + g_free (rss_shmem); + g_free (threads); + g_free (fd_size); + } + g_metrics_file_end_record (memory_metrics_file); + } + + if (slice_metrics_file || slice_metrics_traces_file) + { + GMetricsInstanceCounter *instance_counter = NULL; + GMetricsInstanceCounter *stack_trace_counter = NULL; + const char *name; + + g_slice_lock_metrics (&instance_counter, &stack_trace_counter); + + if (slice_metrics_file) + { + g_metrics_file_start_record (slice_metrics_file); + if (instance_counter) + { + GMetricsInstanceCounterIter iter; + GMetricsInstanceCounterMetrics *slice_metrics; + + g_metrics_instance_counter_iter_init (&iter, instance_counter); + while (g_metrics_instance_counter_iter_next (&iter, &name, &slice_metrics)) + g_metrics_file_add_row (slice_metrics_file, + name, + slice_metrics->total_memory_usage, + slice_metrics->instance_count, + slice_metrics->instance_change > 0? "+" : "", + slice_metrics->instance_change, + slice_metrics->average_instance_change > 0? "+" : "", + slice_metrics->average_instance_change); + } + g_metrics_file_end_record (slice_metrics_file); + } + + if (slice_metrics_traces_file) + { + g_metrics_file_start_record (slice_metrics_traces_file); + if (stack_trace_counter) + { + GMetricsInstanceCounterIter iter; + GMetricsInstanceCounterMetrics *trace_metrics; + + g_metrics_instance_counter_iter_init (&iter, stack_trace_counter); + while (g_metrics_instance_counter_iter_next (&iter, &name, &trace_metrics)) + { + if (trace_metrics->instance_count <= 1) + continue; + + g_metrics_file_add_row (slice_metrics_traces_file, + trace_metrics->comment, + trace_metrics->instance_count, + name); + } + } + g_metrics_file_end_record (slice_metrics_traces_file); + } + g_slice_unlock_metrics (); + } + + G_LOCK (main_context_list); + + if (sources_metrics_file) + { + if (sources_counter == NULL) + sources_counter = g_metrics_instance_counter_new (); + g_metrics_instance_counter_start_record (sources_counter); + } + + for (context_node = main_context_list; context_node != NULL; context_node = context_node->next) + { + GMainContext *context = context_node->data; + GHashTableIter iter; + gpointer value; + + if (!context) + continue; + + LOCK_CONTEXT (context); + g_hash_table_iter_init (&iter, context->sources); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + GSource *source = value; + + if (SOURCE_DESTROYED (source)) + continue; + + if (total_sources_metrics_file) + new_total_sources++; + + if (sources_counter) + { + char *name; + + name = g_strdup (g_source_get_name (source)); + + if (name == NULL) + name = g_strdup_printf ("%p", source); + + g_metrics_instance_counter_add_instance (sources_counter, name, sizeof (GSource)); + g_free (name); + } + } + UNLOCK_CONTEXT (context); + } + + if (sources_counter) + g_metrics_instance_counter_end_record (sources_counter); + + if (total_sources_metrics_file) + { + change = new_total_sources - old_total_sources; + + g_metrics_file_start_record (total_sources_metrics_file); + g_metrics_file_add_row (total_sources_metrics_file, + (gpointer) new_total_sources, + change); + g_metrics_file_end_record (total_sources_metrics_file); + old_total_sources = new_total_sources; + } + + if (sources_metrics_file) + { + GMetricsInstanceCounterIter iter; + GMetricsInstanceCounterMetrics *metrics; + const char *name = NULL; + + g_metrics_file_start_record (sources_metrics_file); + g_metrics_instance_counter_iter_init (&iter, sources_counter); + while (g_metrics_instance_counter_iter_next (&iter, &name, &metrics)) + { + if (metrics->instance_change == 0) + continue; + + g_metrics_file_add_row (sources_metrics_file, + name, + metrics->instance_count, + metrics->instance_change > 0? "+" : "", + metrics->instance_change); + } + g_metrics_file_end_record (sources_metrics_file); + } + + G_UNLOCK (main_context_list); +} + +static gboolean +on_timeout_fd_ready (void) +{ + g_metrics_run_timeout_handlers (); + return G_SOURCE_CONTINUE; +} + /** * g_main_context_new: * @@ -618,6 +918,8 @@ g_main_context_new (void) { static gsize initialised; GMainContext *context; + GSource *metrics_timeout_source = NULL; + gboolean needs_event_loop_metrics = FALSE, needs_event_loop_totals_metrics = FALSE, needs_mem_metrics = FALSE, needs_slice_metrics = FALSE, needs_slice_traces = FALSE; if (g_once_init_enter (&initialised)) { @@ -660,6 +962,66 @@ g_main_context_new (void) g_main_context_add_poll_unlocked (context, 0, &context->wake_up_rec); G_LOCK (main_context_list); + + if (main_context_list == NULL) + { + if (g_metrics_enabled ()) + { + int timeout_fd = g_metrics_get_timeout_fd (); + metrics_timeout_source = g_unix_fd_source_new (timeout_fd, G_IO_IN); + g_source_set_callback (metrics_timeout_source, (GSourceFunc) on_timeout_fd_ready, NULL, NULL); + g_source_set_name (metrics_timeout_source, "metrics timeout source"); + } + needs_event_loop_metrics = g_metrics_requested ("event-loop-sources"); + needs_event_loop_totals_metrics = g_metrics_requested ("event-loop-sources-totals"); + needs_mem_metrics = g_metrics_requested ("memory-usage"); + needs_slice_metrics = g_metrics_requested ("slice-memory-usage"); + needs_slice_traces = g_metrics_requested ("slice-stack-traces"); + } + + if (needs_event_loop_totals_metrics) + total_sources_metrics_file = g_metrics_file_new ("event-loop-sources-totals", + "total sources", "%ld", + "total sources change", "%ld", + NULL); + if (needs_event_loop_metrics) + sources_metrics_file = g_metrics_file_new ("event-loop-sources", + "name", "%s", + "count", "%ld", + "change", "%s%ld", + NULL); + if (needs_mem_metrics) + memory_metrics_file = g_metrics_file_new ("memory-usage", + "VmRSS", "%s", + "VmData", "%s", + "RssAnon", "%s", + "VmHWM", "%s", + "Slice Allocated", "%ld", + "VmSize", "%s", + "VmPeak", "%s", + "RssFile", "%s", + "RssShmem", "%s", + "Threads", "%s", + "FDSize", "%s", + NULL); + + if (needs_slice_metrics) + slice_metrics_file = g_metrics_file_new ("slice-memory-usage", + "name", "%s", + "total-bytes", "%ld", + "number of allocations", "%lu", + "change", "%s%ld", + "average change", "%s%ld", + NULL); + if (needs_slice_traces) + slice_metrics_traces_file = g_metrics_file_new ("slice-stack-traces", + "name", "%s", + "number of hits", "%ld", + "stack trace", "%s", + NULL); + if (needs_event_loop_metrics || needs_event_loop_totals_metrics || needs_mem_metrics || needs_slice_metrics || needs_list_metrics) + g_metrics_start_timeout (on_metrics_timeout); + main_context_list = g_slist_append (main_context_list, context); #ifdef G_MAIN_POLL_DEBUG @@ -669,6 +1031,12 @@ g_main_context_new (void) G_UNLOCK (main_context_list); + if (metrics_timeout_source != NULL) + { + g_source_attach (metrics_timeout_source, context); + g_source_unref (metrics_timeout_source); + } + return context; } diff --git a/glib/gslice.c b/glib/gslice.c index 8e2359515..111f16338 100644 --- a/glib/gslice.c +++ b/glib/gslice.c @@ -392,8 +392,14 @@ slice_config_init (SliceConfig *config) if (RUNNING_ON_VALGRIND) config->always_malloc = TRUE; } + config->always_malloc = TRUE; } +G_LOCK_DEFINE_STATIC (metrics); +static GMetricsTable *metrics_table; +static GMetricsInstanceCounter *instance_counter = NULL; +static GMetricsInstanceCounter *stack_trace_counter = NULL; +static GMetricsStackTraceSampler *stack_trace_sampler = NULL; static void g_slice_init_nomessage (void) { @@ -452,6 +458,16 @@ g_slice_init_nomessage (void) allocator->max_slab_chunk_size_for_magazine_cache = MAX_SLAB_CHUNK_SIZE (allocator); if (allocator->config.always_malloc || allocator->config.bypass_magazines) allocator->max_slab_chunk_size_for_magazine_cache = 0; /* non-optimized cases */ + + G_LOCK (metrics); + if (g_metrics_requested ("slice-memory-usage")) + { + metrics_table = g_metrics_table_new (sizeof (GSliceMetrics)); + instance_counter = g_metrics_instance_counter_new (); + stack_trace_counter = g_metrics_instance_counter_new (); + stack_trace_sampler = g_metrics_stack_trace_sampler_new (); + } + G_UNLOCK (metrics); } static inline guint @@ -967,6 +983,8 @@ thread_memory_magazine2_free (ThreadMemory *tmem, * * Since: 2.10 */ +#include +static gsize g_slice_allocated_memory = 0; /** * g_slice_alloc: @@ -989,6 +1007,17 @@ thread_memory_magazine2_free (ThreadMemory *tmem, */ gpointer g_slice_alloc (gsize mem_size) +{ + char *name = g_strdup_printf ("%lu", mem_size); + gpointer mem = g_slice_alloc_with_name (mem_size, name); + g_free (name); + + return mem; +} + +gpointer +g_slice_alloc_with_name (gsize mem_size, + const char *name) { ThreadMemory *tmem; gsize chunk_size; @@ -1029,6 +1058,29 @@ g_slice_alloc (gsize mem_size) TRACE (GLIB_SLICE_ALLOC((void*)mem, mem_size)); + g_assert (((gssize) mem_size) == mem_size); + + G_LOCK (metrics); + if (metrics_table != NULL && mem_size != 0) + { + GSliceMetrics *metrics = NULL; + + g_slice_allocated_memory += mem_size; + metrics = g_metrics_table_get_record (metrics_table, name); + if (metrics == NULL) + { + GSliceMetrics empty_metrics = { 0 }; + g_metrics_table_set_record (metrics_table, name, &empty_metrics); + metrics = g_metrics_table_get_record (metrics_table, name); + } + metrics->total_usage += mem_size; + metrics->number_of_allocations++; + + if (g_metrics_instance_counter_instance_is_interesting (instance_counter, name)) + g_metrics_stack_trace_sampler_take_sample (stack_trace_sampler, name, mem); + } + G_UNLOCK (metrics); + return mem; } @@ -1055,6 +1107,16 @@ g_slice_alloc0 (gsize mem_size) return mem; } +gpointer +g_slice_alloc0_with_name (gsize mem_size, + const char *name) +{ + gpointer mem = g_slice_alloc_with_name (mem_size, name); + if (mem) + memset (mem, 0, mem_size); + return mem; +} + /** * g_slice_copy: * @block_size: the number of bytes to allocate @@ -1074,7 +1136,20 @@ gpointer g_slice_copy (gsize mem_size, gconstpointer mem_block) { - gpointer mem = g_slice_alloc (mem_size); + gpointer mem; + char *name = g_strdup_printf ("%lu", mem_size); + mem = g_slice_copy_with_name (mem_size, mem_block, name); + g_free (name); + + return mem; +} + +gpointer +g_slice_copy_with_name (gsize mem_size, + gconstpointer mem_block, + const char *name) +{ + gpointer mem = g_slice_alloc_with_name (mem_size, name); if (mem) memcpy (mem, mem_block, mem_size); return mem; @@ -1100,9 +1175,20 @@ g_slice_copy (gsize mem_size, void g_slice_free1 (gsize mem_size, gpointer mem_block) +{ + char *name = g_strdup_printf ("%lu", mem_size); + g_slice_free1_with_name (mem_size, mem_block, name); + g_free (name); +} + +void +g_slice_free1_with_name (gsize mem_size, + gpointer mem_block, + const char *name) { gsize chunk_size = P2ALIGN (mem_size); guint acat = allocator_categorize (chunk_size); + if (G_UNLIKELY (!mem_block)) return; if (G_UNLIKELY (allocator->config.debug_blocks) && @@ -1137,6 +1223,75 @@ g_slice_free1 (gsize mem_size, g_free (mem_block); } TRACE (GLIB_SLICE_FREE((void*)mem_block, mem_size)); + + g_assert (((gssize) mem_size) == mem_size); + G_LOCK (metrics); + g_slice_allocated_memory -= mem_size; + if (metrics_table != NULL && mem_size != 0) + { + GSliceMetrics *metrics = NULL; + + metrics = g_metrics_table_get_record (metrics_table, name); + if (metrics != NULL) + { + metrics->total_usage -= mem_size; + metrics->number_of_allocations--; + + if (metrics->total_usage <= 0) + g_metrics_table_remove_record (metrics_table, name); + } + + g_metrics_stack_trace_sampler_remove_sample (stack_trace_sampler, mem_block); + } + G_UNLOCK (metrics); +} + +gsize +g_slice_get_total_allocated_memory (void) +{ + return g_slice_allocated_memory; +} + +void +g_slice_lock_metrics (GMetricsInstanceCounter **instance_counter_out, + GMetricsInstanceCounter **stack_trace_counter_out) +{ + GMetricsTableIter table_iter; + GSliceMetrics *metrics; + GMetricsStackTraceSample *sample; + GMetricsStackTraceSamplerIter sampler_iter; + const char *name; + G_LOCK (metrics); + + *instance_counter_out = instance_counter; + *stack_trace_counter_out = stack_trace_counter; + + if (instance_counter == NULL || stack_trace_sampler == NULL) + return; + + g_metrics_instance_counter_start_record (instance_counter); + g_metrics_table_iter_init (&table_iter, metrics_table); + while (g_metrics_table_iter_next (&table_iter, &name, &metrics)) + g_metrics_instance_counter_add_instances (instance_counter, name, NULL, metrics->number_of_allocations, metrics->total_usage); + g_metrics_instance_counter_end_record (instance_counter); + + g_metrics_instance_counter_start_record (stack_trace_counter); + g_metrics_stack_trace_sampler_iter_init (&sampler_iter, stack_trace_sampler); + while (g_metrics_stack_trace_sampler_iter_next (&sampler_iter, &sample)) + { + const char *output; + + output = g_metrics_stack_trace_get_output (sample->stack_trace); + g_metrics_instance_counter_add_instances (stack_trace_counter, output, sample->name, sample->number_of_hits, 1); + } + g_metrics_instance_counter_end_record (stack_trace_counter); + +} + +void +g_slice_unlock_metrics (void) +{ + G_UNLOCK (metrics); } /** @@ -1164,6 +1319,18 @@ g_slice_free_chain_with_offset (gsize mem_size, gpointer mem_chain, gsize next_offset) { + char *name = g_strdup_printf ("%lu", mem_size); + g_slice_free_chain_with_offset_and_name (mem_size, mem_chain, next_offset, name); + g_free (name); +} + +void +g_slice_free_chain_with_offset_and_name (gsize mem_size, + gpointer mem_chain, + gsize next_offset, + const char *name) +{ + gssize chain_total = 0, chain_length = 0; gpointer slice = mem_chain; /* while the thread magazines and the magazine cache are implemented so that * they can easily be extended to allow for free lists containing more free @@ -1173,7 +1340,7 @@ g_slice_free_chain_with_offset (gsize mem_size, * the code adapting to lock contention; * - freeing a single node to the thread magazines is very fast, so this * O(list_length) operation is multiplied by a fairly small factor; - * - memory usage histograms on larger applications seem to indicate that + * - memory usage metricss on larger applications seem to indicate that * the amount of released multi node lists is negligible in comparison * to single node releases. * - the major performance bottle neck, namely g_private_get() or @@ -1221,6 +1388,14 @@ g_slice_free_chain_with_offset (gsize mem_size, g_mutex_unlock (&allocator->slab_mutex); } else /* delegate to system malloc */ + { + gboolean is_interesting = FALSE; + + G_LOCK (metrics); + if (instance_counter && stack_trace_sampler) + is_interesting = g_metrics_instance_counter_instance_is_interesting (instance_counter, name); + G_UNLOCK (metrics); + while (slice) { guint8 *current = slice; @@ -1230,8 +1405,36 @@ g_slice_free_chain_with_offset (gsize mem_size, abort(); if (G_UNLIKELY (g_mem_gc_friendly)) memset (current, 0, mem_size); + chain_total += mem_size; + chain_length++; + + if (is_interesting) + { + G_LOCK (metrics); + g_metrics_stack_trace_sampler_remove_sample (stack_trace_sampler, current); + G_UNLOCK (metrics); + } g_free (current); } + + G_LOCK (metrics); + g_slice_allocated_memory -= chain_total; + if (metrics_table != NULL) + { + GSliceMetrics *metrics = NULL; + + metrics = g_metrics_table_get_record (metrics_table, name); + if (metrics != NULL) + { + metrics->total_usage -= chain_total; + metrics->number_of_allocations -= chain_length; + + if (metrics->total_usage <= 0) + g_metrics_table_remove_record (metrics_table, name); + } + } + G_UNLOCK (metrics); + } } /* --- single page allocator --- */ diff --git a/glib/gslice.h b/glib/gslice.h index 80762761f..f9e78bb19 100644 --- a/glib/gslice.h +++ b/glib/gslice.h @@ -22,13 +22,19 @@ #error "Only can be included directly." #endif +#include #include +#include G_BEGIN_DECLS /* slices - fast allocation/release of small memory blocks */ GLIB_AVAILABLE_IN_ALL +gpointer g_slice_alloc_with_name (gsize block_size, const char *name) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); +GLIB_AVAILABLE_IN_ALL +gpointer g_slice_alloc0_with_name (gsize block_size, const char *name) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); +GLIB_AVAILABLE_IN_ALL gpointer g_slice_alloc (gsize block_size) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); GLIB_AVAILABLE_IN_ALL gpointer g_slice_alloc0 (gsize block_size) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); @@ -36,14 +42,28 @@ GLIB_AVAILABLE_IN_ALL gpointer g_slice_copy (gsize block_size, gconstpointer mem_block) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); GLIB_AVAILABLE_IN_ALL +gpointer g_slice_copy_with_name (gsize block_size, + gconstpointer mem_block, + const char *name) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); +GLIB_AVAILABLE_IN_ALL +void g_slice_free1_with_name (gsize block_size, + gpointer mem_block, + const char *name); +GLIB_AVAILABLE_IN_ALL void g_slice_free1 (gsize block_size, gpointer mem_block); GLIB_AVAILABLE_IN_ALL +void g_slice_free_chain_with_offset_and_name (gsize block_size, + gpointer mem_chain, + gsize next_offset, + const char *name); +GLIB_AVAILABLE_IN_ALL void g_slice_free_chain_with_offset (gsize block_size, gpointer mem_chain, gsize next_offset); -#define g_slice_new(type) ((type*) g_slice_alloc (sizeof (type))) -#define g_slice_new0(type) ((type*) g_slice_alloc0 (sizeof (type))) +#define g_slice_new(type) ((type*) g_slice_alloc_with_name (sizeof (type), #type)) +#define g_slice_new0(type) ((type*) g_slice_alloc0_with_name (sizeof (type), #type)) + /* MemoryBlockType * * g_slice_dup (MemoryBlockType, * MemoryBlockType *mem_block); @@ -58,17 +78,17 @@ void g_slice_free_chain_with_offset (gsize block_size, /* we go through extra hoops to ensure type safety */ #define g_slice_dup(type, mem) \ - (1 ? (type*) g_slice_copy (sizeof (type), (mem)) \ + (1 ? (type*) g_slice_copy_with_name (sizeof (type), (mem), #type) \ : ((void) ((type*) 0 == (mem)), (type*) 0)) #define g_slice_free(type, mem) \ G_STMT_START { \ - if (1) g_slice_free1 (sizeof (type), (mem)); \ + if (1) g_slice_free1_with_name (sizeof (type), (mem), #type); \ else (void) ((type*) 0 == (mem)); \ } G_STMT_END #define g_slice_free_chain(type, mem_chain, next) \ G_STMT_START { \ - if (1) g_slice_free_chain_with_offset (sizeof (type), \ - (mem_chain), G_STRUCT_OFFSET (type, next)); \ + if (1) g_slice_free_chain_with_offset_and_name (sizeof (type), \ + (mem_chain), G_STRUCT_OFFSET (type, next), #type); \ else (void) ((type*) 0 == (mem_chain)); \ } G_STMT_END @@ -89,6 +109,21 @@ gint64 g_slice_get_config (GSliceConfig ckey); GLIB_DEPRECATED_IN_2_34 gint64* g_slice_get_config_state (GSliceConfig ckey, gint64 address, guint *n_values); +GLIB_AVAILABLE_IN_ALL +gsize g_slice_get_total_allocated_memory (void); + +typedef struct _GSliceMetrics +{ + gssize total_usage; + gsize number_of_allocations; +} GSliceMetrics; + +GLIB_AVAILABLE_IN_ALL +void g_slice_lock_metrics (GMetricsInstanceCounter **instance_counter, + GMetricsInstanceCounter **stack_trace_counter); +GLIB_AVAILABLE_IN_ALL +void g_slice_unlock_metrics (void); + #ifdef G_ENABLE_DEBUG GLIB_AVAILABLE_IN_ALL void g_slice_debug_tree_statistics (void); diff --git a/glib/gvarianttypeinfo.c b/glib/gvarianttypeinfo.c index 9dade7064..6f9499180 100644 --- a/glib/gvarianttypeinfo.c +++ b/glib/gvarianttypeinfo.c @@ -342,8 +342,8 @@ tuple_info_free (GVariantTypeInfo *info) for (i = 0; i < tuple_info->n_members; i++) g_variant_type_info_unref (tuple_info->members[i].type_info); - g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members, - tuple_info->members); + g_slice_free1_with_name (sizeof (GVariantMemberInfo) * tuple_info->n_members, + tuple_info->members, "GVariantMemberInfo"); g_slice_free (TupleInfo, tuple_info); } @@ -356,7 +356,7 @@ tuple_allocate_members (const GVariantType *type, gsize i = 0; *n_members = g_variant_type_n_items (type); - *members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members); + *members = g_slice_alloc_with_name (sizeof (GVariantMemberInfo) * *n_members, "GVariantMemberInfo"); item_type = g_variant_type_first (type); while (item_type) diff --git a/gobject/gatomicarray.c b/gobject/gatomicarray.c index 57b9ec228..dc63c2073 100644 --- a/gobject/gatomicarray.c +++ b/gobject/gatomicarray.c @@ -74,7 +74,7 @@ freelist_alloc (gsize size, gboolean reuse) } real_size = sizeof (gsize) + MAX (size, sizeof (FreeListNode)); - mem = g_slice_alloc (real_size); + mem = g_slice_alloc_with_name (real_size, "FreeListNode"); mem = ((char *) mem) + sizeof (gsize); G_ATOMIC_ARRAY_DATA_SIZE (mem) = size; return mem; diff --git a/gobject/gtype.c b/gobject/gtype.c index 9e663ce52..aa6e55711 100644 --- a/gobject/gtype.c +++ b/gobject/gtype.c @@ -1846,7 +1846,7 @@ g_type_create_instance (GType type) VALGRIND_MALLOCLIKE_BLOCK (allocated + ALIGN_STRUCT (1), private_size - ALIGN_STRUCT (1), 0, TRUE); } else - allocated = g_slice_alloc0 (private_size + ivar_size); + allocated = g_slice_alloc0_with_name (private_size + ivar_size, type_descriptive_name_I (type)); instance = (GTypeInstance *) (allocated + private_size); @@ -1941,7 +1941,7 @@ g_type_free_instance (GTypeInstance *instance) VALGRIND_FREELIKE_BLOCK (instance, 0); } else - g_slice_free1 (private_size + ivar_size, allocated); + g_slice_free1_with_name (private_size + ivar_size, allocated, type_descriptive_name_I (class->g_type)); #ifdef G_ENABLE_DEBUG IF_DEBUG (INSTANCE_COUNT)