gobject patch to track memory use



I spent some day today hacking up a patch to gobject that lets you track
what types of GObjects are using memory.

It keeps a list of all live objects, and when you send the process a
SIGUSR2 it prints a memory profile. For the size it counts the basic
size of the object plus all private data registered with the object. 

Furthermore it adds some API that lets you register a function to
calculate the size an object uses. This is useful for objects that own
memory allocations that are not GObjects. I also made a gtk+ patch using
this to track the real size of GdkPixbuf object.

Even without specific functions for all types this is quite useful, as
you can see the object counts. I've already detected some strange things
in nautilus with this. The top of the memory profile there looks like:

GtkImage: 231 allocated at 104 base size bytes, 23 kb total size
GstPadTemplate: 334 allocated at 76 base size bytes, 24 kb total size
GtkSeparatorMenuItem: 284 allocated at 96 base size bytes, 26 kb total size
NautilusIconCanvasItem: 502 allocated at 64 base size bytes, 31 kb total size
GtkAccelLabel: 306 allocated at 168 base size bytes, 50 kb total size
NautilusVFSFile: 1005 allocated at 128 base size bytes, 296 kb total size
GdkPixbuf: 340 allocated at 52 base size bytes, 6409 kb total size

(only NautilusVFSFile and GdkPixbuf have specific memuse functions here)

Maybe people want to play around a bit with this. It seems there is some
interest in memory profiling at the moment.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Alexander Larsson                                            Red Hat, Inc 
                   alexl redhat com    alla lysator liu se 
He's an immortal vegetarian farmboy in drag. She's a radical winged safe 
cracker prone to fits of savage, blood-crazed rage. They fight crime! 
Index: gobject/gtype.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gtype.c,v
retrieving revision 1.78
diff -u -p -r1.78 gtype.c
--- gobject/gtype.c	1 Nov 2004 18:47:12 -0000	1.78
+++ gobject/gtype.c	24 Feb 2005 17:11:40 -0000
@@ -158,6 +158,7 @@ static void                             
                                                                          TypeNode               *node);
 static gboolean				type_node_is_a_L		(TypeNode		*node,
 									 TypeNode		*iface_node);
+static void                             install_mem_use_signal_handler  (void);
 
 
 /* --- enumeration --- */
@@ -191,6 +192,7 @@ struct _TypeNode
   TypeData * volatile data;
   GQuark       qname;
   GData       *global_gdata;
+  GList       *instances;
   union {
     IFaceEntry  *iface_entries;		/* for !iface types */
     GType       *prerequisistes;
@@ -279,6 +281,7 @@ struct _InstanceData
   guint16            n_preallocs;
   GInstanceInitFunc  instance_init;
   GMemChunk        *mem_chunk;
+  GMemUseFunc        mem_use;
 };
 union _TypeData
 {
@@ -1575,6 +1578,11 @@ g_type_create_instance (GType type)
   else
     instance = g_malloc0 (total_instance_size);	/* fine without read lock */
 
+  
+  G_WRITE_LOCK (&type_rw_lock);
+  node->instances = g_list_prepend (node->instances, instance);
+  G_WRITE_UNLOCK (&type_rw_lock);
+  
   if (node->data->instance.private_size)
     instance_real_class_set (instance, class);
   for (i = node->n_supers; i > 0; i--)
@@ -1625,7 +1633,12 @@ g_type_free_instance (GTypeInstance *ins
   instance->g_class = NULL;
 #ifdef G_ENABLE_DEBUG  
   memset (instance, 0xaa, type_total_instance_size_I (node));	/* debugging hack */
-#endif  
+#endif
+  
+  G_WRITE_LOCK (&type_rw_lock);
+  node->instances = g_list_remove (node->instances, instance);
+  G_WRITE_UNLOCK (&type_rw_lock);
+  
   if (node->data->instance.n_preallocs)
     {
       G_WRITE_LOCK (&type_rw_lock);
@@ -2242,6 +2255,24 @@ g_type_register_fundamental (GType      
   return NODE_TYPE (node);
 }
 
+void
+g_type_register_memuse_function (GType type,
+				 GMemUseFunc memuse)
+{
+  TypeNode *node;
+  
+  node = lookup_type_node_I (type);
+
+  if (node && node->is_instantiatable && node->data != NULL)
+    {
+      G_WRITE_LOCK (&type_rw_lock);
+
+      node->data->instance.mem_use = memuse;
+
+      G_WRITE_UNLOCK (&type_rw_lock);
+    }
+}
+
 GType
 g_type_register_static (GType            parent_type,
 			const gchar     *type_name,
@@ -3477,6 +3508,8 @@ g_type_init_with_debug_flags (GTypeDebug
   /* Signal system
    */
   g_signal_init ();
+
+  install_mem_use_signal_handler ();
   
   G_UNLOCK (type_init_lock);
 }
@@ -3579,4 +3612,134 @@ g_type_instance_get_private (GTypeInstan
     }
 
   return G_STRUCT_MEMBER_P (instance, offset);
+}
+
+
+typedef struct {
+  const char *name;
+  gsize instance_size;
+  int n_allocated;
+  gsize total_size;
+} NodeMemUse;
+
+static gint
+compare_memuse (gconstpointer  a,
+		gconstpointer  b)
+{
+  const NodeMemUse *m1 = a;
+  const NodeMemUse *m2 = b;
+  gsize s1, s2;
+
+  s1 = m1->total_size; 
+  s2 = m2->total_size; 
+  if (s1 < s2)
+    return -1;
+  if (s1 == s2)
+    return 0;
+  return 1;
+}
+
+static void
+report_mem_use_for_node (gpointer       key,
+			 gpointer       value,
+			 gpointer       user_data)
+{
+  TypeNode *node;
+  gsize total_instance_size;
+  gsize total_size;
+  int n_allocated;
+  GType type;
+  GList **list, *l;
+  NodeMemUse *memuse;
+  int i;
+  
+  list = user_data;
+  
+  type = (GType)value;
+  node = lookup_type_node_I (type);
+  if (node == NULL ||
+      !node->is_instantiatable ||
+      node->data == NULL)
+    return;
+
+  total_instance_size = type_total_instance_size_I (node);
+  if (node->data->instance.private_size)
+    total_instance_size = ALIGN_STRUCT (total_instance_size);
+
+  memuse = g_new (NodeMemUse, 1);
+  memuse->name = NODE_NAME(node);
+  memuse->instance_size = total_instance_size;
+
+  total_size = 0;
+  n_allocated = 0;
+  for (l = node->instances; l != NULL; l = l->next)
+    {
+      GTypeInstance *instance = l->data;
+
+      for (i = 0; i < node->n_supers; i++) {
+	GType instance_type;
+	TypeNode *instance_node;
+
+	instance_type = node->supers[i];
+	instance_node = lookup_type_node_I (instance_type);
+
+	if (instance_node != NULL &&
+	    instance_node->is_instantiatable &&
+	    instance_node->data != NULL &&
+	    instance_node->data->instance.mem_use != NULL)
+	  total_size += instance_node->data->instance.mem_use (instance);
+      }
+
+      n_allocated ++;
+    }
+  total_size += n_allocated * total_instance_size;
+  
+  memuse->n_allocated = n_allocated;
+  memuse->total_size = total_size;
+
+  *list = g_list_append (*list, memuse);
+}
+
+static void
+report_mem_use (int i)
+{
+  GList *list, *l;
+  NodeMemUse *memuse;
+  
+  G_READ_LOCK (&type_rw_lock);
+  list = NULL;
+  g_hash_table_foreach (static_type_nodes_ht, report_mem_use_for_node, &list);
+
+  list = g_list_sort (list, compare_memuse);
+
+  for (l = list; l != NULL; l = l->next)
+    {
+      memuse = l->data;
+      
+      g_print ("%s: %d allocated at %lu base size bytes, %lu kb total size\n",
+	       memuse->name,
+	       memuse->n_allocated, 
+	       (gulong) memuse->instance_size,
+	       (gulong) (memuse->total_size / 1024));
+      
+      g_free (memuse);
+    }
+  g_list_free (list);
+
+  G_READ_UNLOCK (&type_rw_lock);
+  
+}
+
+#include <signal.h>
+
+static void
+install_mem_use_signal_handler (void)
+{
+  struct sigaction action;
+  
+  action.sa_handler = report_mem_use;
+  sigemptyset (&action.sa_mask);
+  action.sa_flags = 0;
+  
+  sigaction(SIGUSR2, &action, NULL);
 }
Index: gobject/gtype.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gtype.h,v
retrieving revision 1.60
diff -u -p -r1.60 gtype.h
--- gobject/gtype.h	24 Oct 2004 01:22:29 -0000	1.60
+++ gobject/gtype.h	24 Feb 2005 17:11:40 -0000
@@ -221,6 +221,7 @@ typedef gboolean (*GTypeClassCacheFunc)	
 					      GTypeClass      *g_class);
 typedef void     (*GTypeInterfaceCheckFunc)  (gpointer	       check_data,
 					      gpointer         g_iface);
+typedef gsize    (*GMemUseFunc)              (GTypeInstance   *instance);
 typedef enum    /*< skip >*/
 {
   G_TYPE_FLAG_CLASSED           = (1 << 0),
@@ -310,6 +311,8 @@ void     g_type_class_add_private       
                                          gsize                       private_size);
 gpointer g_type_instance_get_private    (GTypeInstance              *instance,
                                          GType                       private_type);
+void g_type_register_memuse_function    (GType                       type,
+					 GMemUseFunc                 memuse);
 
 
 /* --- GType boilerplate --- */
Index: gdk-pixbuf/gdk-pixbuf.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk-pixbuf/gdk-pixbuf.c,v
retrieving revision 1.64
diff -u -p -r1.64 gdk-pixbuf.c
--- gdk-pixbuf/gdk-pixbuf.c	5 Nov 2004 01:43:31 -0000	1.64
+++ gdk-pixbuf/gdk-pixbuf.c	24 Feb 2005 17:11:53 -0000
@@ -59,6 +59,12 @@ enum 
 
 static gpointer parent_class;
 
+static gsize
+pixbuf_memuse (GdkPixbuf *pixbuf)
+{
+        return pixbuf->rowstride * pixbuf->height;
+}
+
 GType
 gdk_pixbuf_get_type (void)
 {
@@ -80,6 +86,8 @@ gdk_pixbuf_get_type (void)
                 object_type = g_type_register_static (G_TYPE_OBJECT,
                                                       "GdkPixbuf",
                                                       &object_info, 0);
+                g_type_register_memuse_function (object_type,
+                                                 (GMemUseFunc) pixbuf_memuse);
         }
   
         return object_type;
Index: libnautilus-private/nautilus-file.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-file.c,v
retrieving revision 1.362
diff -u -p -r1.362 nautilus-file.c
--- libnautilus-private/nautilus-file.c	22 Feb 2005 10:41:46 -0000	1.362
+++ libnautilus-private/nautilus-file.c	24 Feb 2005 17:20:23 -0000
@@ -132,6 +132,39 @@ static gboolean update_info_and_name    
 static char *   nautilus_file_get_display_name_nocopy        (NautilusFile          *file);
 static char *   nautilus_file_get_display_name_collation_key (NautilusFile          *file);
 
+static gsize
+file_memuse (NautilusFile *file)
+{
+	gsize size;
+	NautilusFileDetails *details = file->details;
+	GList *l;
+
+	if (details == NULL)
+		return 0;
+
+	size = 0;
+	
+	size += eel_strlen (details->relative_uri) + 1;
+	size += eel_strlen (details->cached_display_name) + 1;
+	size += eel_strlen (details->display_name_collation_key) + 1;
+	if (details->info)
+		size += sizeof(GnomeVFSFileInfo);
+
+	for (l = details->mime_list; l != NULL; l = l->next) {
+		size += sizeof(GList);
+		size += eel_strlen (l->data) + 1;
+	}
+
+	size += eel_strlen (details->top_left_text);
+	size += eel_strlen (details->display_name);
+	size += eel_strlen (details->custom_icon);
+	size += eel_strlen (details->activation_uri);
+	size += eel_strlen (details->guessed_mime_type);
+
+	return size;
+}
+
+
 GType
 nautilus_file_get_type (void)
 {
@@ -162,6 +195,8 @@ nautilus_file_get_type (void)
 		g_type_add_interface_static (type, 
 					     NAUTILUS_TYPE_FILE_INFO,
 					     &file_info_iface_info);
+                g_type_register_memuse_function (type,
+                                                 (GMemUseFunc) file_memuse);
 	}
 	
 	return type;


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