3434#include <libunwind.h>
3535#endif
3636
37+ #ifdef WITH_ORIGINS_TRACE
38+ #include "bt-tree.h"
39+ #endif
40+
3741typedef enum
3842{
3943 DISPLAY_FLAG_NONE = 0 ,
4044 DISPLAY_FLAG_CREATE = 1 ,
4145 DISPLAY_FLAG_REFS = 1 << 2 ,
4246 DISPLAY_FLAG_BACKTRACE = 1 << 3 ,
47+ DISPLAY_FLAG_TRACEREFS = 1 << 4 ,
4348 DISPLAY_FLAG_ALL =
44- DISPLAY_FLAG_CREATE | DISPLAY_FLAG_REFS | DISPLAY_FLAG_BACKTRACE ,
49+ DISPLAY_FLAG_CREATE | DISPLAY_FLAG_REFS | DISPLAY_FLAG_BACKTRACE |
50+ DISPLAY_FLAG_TRACEREFS ,
4551 DISPLAY_FLAG_DEFAULT = DISPLAY_FLAG_CREATE ,
4652} DisplayFlags ;
4753
@@ -57,6 +63,7 @@ DisplayFlagsMapItem display_flags_map[] =
5763 { "create" , DISPLAY_FLAG_CREATE },
5864 { "refs" , DISPLAY_FLAG_REFS },
5965 { "backtrace" , DISPLAY_FLAG_BACKTRACE },
66+ { "tracerefs" , DISPLAY_FLAG_TRACEREFS },
6067 { "all" , DISPLAY_FLAG_ALL },
6168};
6269
@@ -71,6 +78,11 @@ typedef struct {
7178 * We keep the string representing the type of the object as we won't be able
7279 * to get it when displaying later as the object would have been destroyed. */
7380 GHashTable * removed ; /* owned */
81+
82+ #ifdef WITH_ORIGINS_TRACE
83+ /* GObject -> BtTrie */
84+ GHashTable * origins ; /* owned */
85+ #endif
7486} ObjectData ;
7587
7688/* Global static state, which must be accessed with the @gobject_list mutex
@@ -128,6 +140,10 @@ display_filter (DisplayFlags flags)
128140 if (display_flags & DISPLAY_FLAG_BACKTRACE )
129141 g_print ("Warning: backtrace is not available, it needs libunwind\n" );
130142#endif
143+ #ifndef WITH_ORIGINS_TRACE
144+ if (display_flags & DISPLAY_FLAG_TRACEREFS )
145+ g_print ("Warning: tracerefs is not available, it needs libunwind\n" );
146+ #endif
131147
132148 parsed = TRUE;
133149 }
@@ -146,6 +162,49 @@ object_filter (const char *obj_name)
146162 return (strncmp (filter , obj_name , strlen (filter )) == 0 );
147163}
148164
165+ static void
166+ save_trace (const char * key , gboolean is_ref )
167+ {
168+ #if defined(HAVE_LIBUNWIND ) && defined(WITH_ORIGINS_TRACE )
169+ unw_context_t uc ;
170+ unw_cursor_t cursor ;
171+ GPtrArray * trace ;
172+ BtTrie * root = NULL ;
173+ gboolean found ;
174+
175+ if (!display_filter (DISPLAY_FLAG_TRACEREFS ))
176+ return ;
177+
178+ trace = g_ptr_array_sized_new (10 );
179+
180+ unw_getcontext (& uc );
181+ unw_init_local (& cursor , & uc );
182+
183+ while (unw_step (& cursor ) > 0 )
184+ {
185+ gchar name [129 ];
186+ unw_word_t off ;
187+ int result ;
188+
189+ result = unw_get_proc_name (& cursor , name , sizeof (name ), & off );
190+ if (result < 0 && result != - UNW_ENOMEM )
191+ break ;
192+
193+ g_ptr_array_insert (trace , -1 , g_strdup (name ));
194+ }
195+
196+ found = g_hash_table_lookup_extended (gobject_list_state .origins ,
197+ (gpointer ) key ,
198+ NULL , (gpointer * )& root );
199+ if (!found ) {
200+ root = bt_create (g_strdup (key ));
201+ g_hash_table_insert (gobject_list_state .origins , (gpointer ) key , root );
202+ }
203+ bt_insert (root , trace , is_ref );
204+ g_ptr_array_unref (trace );
205+ #endif
206+ }
207+
149208static void
150209print_trace (void )
151210{
@@ -234,13 +293,31 @@ _sig_usr2_handler (G_GNUC_UNUSED int signal)
234293 G_UNLOCK (gobject_list );
235294}
236295
296+ #ifdef WITH_ORIGINS_TRACE
297+ static void
298+ print_refs (G_GNUC_UNUSED gpointer key , gpointer value , gpointer user_data )
299+ {
300+ gint * no = (gpointer ) user_data ;
301+ BtTrie * bt_trie = value ;
302+ g_print ("#%d\n" , ++ * no );
303+ bt_print_tree (bt_trie , 0 );
304+ }
305+ #endif
306+
237307static void
238308print_still_alive (void )
239309{
240310 g_print ("\nStill Alive:\n" );
241311
242312 G_LOCK (gobject_list );
243313 _dump_object_list (gobject_list_state .objects );
314+ #ifdef WITH_ORIGINS_TRACE
315+ if (display_filter (DISPLAY_FLAG_TRACEREFS )) {
316+ guint no = 0 ;
317+ g_print ("\nReferences:\n" );
318+ g_hash_table_foreach (gobject_list_state .origins , print_refs , (gpointer ) & no );
319+ }
320+ #endif
244321 G_UNLOCK (gobject_list );
245322}
246323
@@ -291,6 +368,10 @@ get_func (const char *func_name)
291368 gobject_list_state .objects = g_hash_table_new (NULL , NULL );
292369 gobject_list_state .added = g_hash_table_new (NULL , NULL );
293370 gobject_list_state .removed = g_hash_table_new_full (NULL , NULL , NULL , g_free );
371+ #ifdef WITH_ORIGINS_TRACE
372+ gobject_list_state .origins =
373+ g_hash_table_new_full (NULL , NULL , NULL , (GDestroyNotify ) bt_free );
374+ #endif
294375
295376 /* Set up exit handler */
296377 atexit (_exiting );
@@ -338,6 +419,9 @@ _object_finalized (G_GNUC_UNUSED gpointer data,
338419
339420 g_hash_table_remove (gobject_list_state .objects , obj );
340421 g_hash_table_remove (gobject_list_state .added , obj );
422+ #ifdef WITH_ORIGINS_TRACE
423+ g_hash_table_remove (gobject_list_state .origins , G_OBJECT_TYPE_NAME (obj ));
424+ #endif
341425
342426 G_UNLOCK (gobject_list );
343427}
@@ -371,6 +455,7 @@ g_object_new (GType type,
371455
372456 g_print (" ++ Created object %p, %s\n" , obj , obj_name );
373457 print_trace ();
458+ save_trace (obj_name , TRUE);
374459
375460 g_mutex_unlock (& output_mutex );
376461 }
@@ -424,6 +509,7 @@ g_object_ref (gpointer object)
424509 g_print (" + Reffed object %p, %s; ref_count: %d -> %d\n" ,
425510 obj , obj_name , ref_count , obj -> ref_count );
426511 print_trace ();
512+ save_trace (obj_name , TRUE);
427513
428514 g_mutex_unlock (& output_mutex );
429515 }
@@ -449,6 +535,7 @@ g_object_unref (gpointer object)
449535 g_print (" - Unreffed object %p, %s; ref_count: %d -> %d\n" ,
450536 obj , obj_name , obj -> ref_count , obj -> ref_count - 1 );
451537 print_trace ();
538+ save_trace (obj_name , FALSE);
452539
453540 g_mutex_unlock (& output_mutex );
454541 }
0 commit comments