mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-21 08:28:53 +02:00
gmain: Add debug metrics for gnome-shell
This commit is contained in:
37
glib/ghash.c
37
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);
|
||||
|
@@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
368
glib/gmain.c
368
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;
|
||||
}
|
||||
|
||||
|
207
glib/gslice.c
207
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 <gatomic.h>
|
||||
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 --- */
|
||||
|
@@ -22,13 +22,19 @@
|
||||
#error "Only <glib.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <glib/gmetrics.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user