[glib/halfline/debug-metrics: 16/19] gmain: Add debug metrics for gnome-shell




commit ab3cd55b279e4ab55eb11d888afdbdd7ec848f49
Author: Ray Strode <rstrode redhat com>
Date:   Wed May 26 23:47:11 2021 -0400

    gmain: Add debug metrics for gnome-shell

 glib/ghash.c            |  37 ++---
 glib/ghook.c            |   2 +-
 glib/gmain.c            | 376 ++++++++++++++++++++++++++++++++++++++++++++++++
 glib/gslice.c           | 150 ++++++++++++++++++-
 glib/gslice.h           |  46 +++++-
 glib/gvarianttypeinfo.c |   6 +-
 gobject/gatomicarray.c  |   2 +-
 gobject/gtype.c         |   4 +-
 8 files changed, 590 insertions(+), 33 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..1ddd3e644 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;
diff --git a/glib/gmain.c b/glib/gmain.c
index 2f4f578d1..5a885594f 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -606,6 +606,323 @@ g_main_context_new_with_next_id (guint next_id)
   return ret;
 }
 
+typedef struct _GSourceMetrics GSourceMetrics;
+struct _GSourceMetrics {
+  long  instance_count;
+  long  instance_change;
+};
+static GMetricsTable *histograms[2] = { NULL };
+static int old_histogram_index = 0;
+static GMetricsTable *old_histogram = NULL;
+
+static gint
+metrics_sort (GSourceMetrics *a, GSourceMetrics *b)
+{
+  return b->instance_count - a->instance_count;
+}
+
+static GMetricsFile *total_sources_metrics_file;
+static GMetricsFile *sources_metrics_file;
+static GMetricsFile *memory_metrics_file;
+static GMetricsFile *slice_metrics_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;
+      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]));
+
+      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)
+{
+  GMetricsTable *histogram;
+  guint new_histogram_index = (old_histogram_index + 1) % G_N_ELEMENTS (histograms);
+  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)
+    {
+      GMetricsTable *slice_metrics_table;
+      GMetricsTableIter iter;
+      GSliceMetrics *slice_metrics;
+      const char *name;
+
+      g_metrics_file_start_record (slice_metrics_file);
+      slice_metrics_table = g_slice_lock_metrics_table ();
+      g_metrics_table_iter_init (&iter, slice_metrics_table);
+      while (g_metrics_table_iter_next (&iter, &name, &slice_metrics))
+        g_metrics_file_add_row (slice_metrics_file,
+                                name,
+                                slice_metrics->total_usage,
+                                slice_metrics->number_of_allocations);
+      g_slice_unlock_metrics_table ();
+      g_metrics_file_end_record (slice_metrics_file);
+    }
+
+  G_LOCK (main_context_list);
+
+  old_histogram = histograms[old_histogram_index];
+  if (histograms[new_histogram_index] == NULL)
+    histograms[new_histogram_index] = g_metrics_table_new (sizeof (GSourceMetrics));
+
+  histogram = histograms[new_histogram_index];
+  g_metrics_table_clear (histogram);
+
+  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;
+          char *name;
+          GSourceMetrics *old_metrics = NULL;
+          GSourceMetrics *metrics;
+          long old_instance_count = 0;
+
+          if (SOURCE_DESTROYED (source))
+            continue;
+
+          if (total_sources_metrics_file)
+            new_total_sources++;
+          name = g_strdup (g_source_get_name (source));
+
+          if (name == NULL)
+            name = g_strdup_printf ("%p", source);
+
+          if (old_histogram != NULL)
+            {
+              old_metrics = g_metrics_table_get_record (old_histogram, name);
+
+              if (old_metrics != NULL)
+                  old_instance_count = old_metrics->instance_count;
+            }
+
+          metrics = g_metrics_table_get_record (histogram, name);
+          if (metrics == NULL)
+            {
+              GSourceMetrics new_metrics = { 0 };
+              g_metrics_table_set_record (histogram, name, &new_metrics);
+
+              metrics = g_metrics_table_get_record (histogram, name);
+            }
+
+          g_free (name);
+
+          metrics->instance_count++;
+          metrics->instance_change = metrics->instance_count - old_instance_count;
+        }
+      UNLOCK_CONTEXT (context);
+    }
+
+  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 (old_histogram != NULL)
+    {
+      GMetricsTableIter iter;
+      GSourceMetrics *old_metrics = NULL;
+      const char *old_name = NULL;
+
+      g_metrics_table_iter_init (&iter, old_histogram);
+      while (g_metrics_table_iter_next (&iter, &old_name, &old_metrics))
+        {
+          GSourceMetrics *metrics;
+
+          metrics = g_metrics_table_get_record (histogram, old_name);
+          if (metrics == NULL)
+            {
+              GSourceMetrics new_metrics = { 0 };
+              new_metrics.instance_change = -old_metrics->instance_count;
+              g_metrics_table_set_record (histogram, old_name, &new_metrics);
+            }
+        }
+    }
+
+  if (sources_metrics_file)
+    {
+      GMetricsTableIter iter;
+      GSourceMetrics *metrics = NULL;
+      const char *name = NULL;
+
+      g_metrics_file_start_record (sources_metrics_file);
+      g_metrics_table_sorted_iter_init (&iter, histogram, (GCompareFunc) metrics_sort);
+      while (g_metrics_table_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);
+    }
+  if (old_histogram != NULL)
+    g_metrics_table_clear (old_histogram);
+  old_histogram_index = new_histogram_index;
+
+  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 +935,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;
 
   if (g_once_init_enter (&initialised))
     {
@@ -660,6 +979,57 @@ 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");
+    }
+
+    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",
+                                               NULL);
+    if (needs_event_loop_metrics || needs_event_loop_totals_metrics || needs_mem_metrics || 
needs_slice_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 +1039,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..93ac589b1 100644
--- a/glib/gslice.c
+++ b/glib/gslice.c
@@ -392,8 +392,12 @@ 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 void
 g_slice_init_nomessage (void)
 {
@@ -452,6 +456,11 @@ 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));
+  G_UNLOCK (metrics);
 }
 
 static inline guint
@@ -967,6 +976,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 +1000,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 +1051,26 @@ 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++;
+    }
+  G_UNLOCK (metrics);
+
   return mem;
 }
 
@@ -1055,6 +1097,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 +1126,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 +1165,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 +1213,44 @@ 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)
+    {
+      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_UNLOCK (metrics);
+}
+
+gsize
+g_slice_get_total_allocated_memory (void)
+{
+  return g_slice_allocated_memory;
+}
+
+GMetricsTable *
+g_slice_lock_metrics_table (void)
+{
+  G_LOCK (metrics);
+  return metrics_table;
+}
+
+void
+g_slice_unlock_metrics_table (void)
+{
+  G_UNLOCK (metrics);
 }
 
 /**
@@ -1164,6 +1278,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 +1299,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
@@ -1230,8 +1356,28 @@ 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++;
         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..ee19cb8a8 100644
--- a/glib/gslice.h
+++ b/glib/gslice.h
@@ -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,20 @@ 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
+GMetricsTable *g_slice_lock_metrics_table (void);
+GLIB_AVAILABLE_IN_ALL
+void        g_slice_unlock_metrics_table (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..1d681f445 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, class->g_type);
 
 #ifdef G_ENABLE_DEBUG
   IF_DEBUG (INSTANCE_COUNT)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]