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
@@ -123,6 +135,10 @@ display_filter (DisplayFlags flags)
123135 if (display_flags & DISPLAY_FLAG_BACKTRACE )
124136 g_print ("Warning: backtrace is not available, it needs libunwind\n" );
125137#endif
138+ #ifndef WITH_ORIGINS_TRACE
139+ if (display_flags & DISPLAY_FLAG_TRACEREFS )
140+ g_print ("Warning: tracerefs is not available, it needs libunwind\n" );
141+ #endif
126142
127143 parsed = TRUE;
128144 }
@@ -141,6 +157,49 @@ object_filter (const char *obj_name)
141157 return (strncmp (filter , obj_name , strlen (filter )) == 0 );
142158}
143159
160+ static void
161+ save_trace (const char * key , gboolean is_ref )
162+ {
163+ #if defined(HAVE_LIBUNWIND ) && defined(WITH_ORIGINS_TRACE )
164+ unw_context_t uc ;
165+ unw_cursor_t cursor ;
166+ GPtrArray * trace ;
167+ BtTrie * root = NULL ;
168+ gboolean found ;
169+
170+ if (!display_filter (DISPLAY_FLAG_TRACEREFS ))
171+ return ;
172+
173+ trace = g_ptr_array_sized_new (10 );
174+
175+ unw_getcontext (& uc );
176+ unw_init_local (& cursor , & uc );
177+
178+ while (unw_step (& cursor ) > 0 )
179+ {
180+ gchar name [129 ];
181+ unw_word_t off ;
182+ int result ;
183+
184+ result = unw_get_proc_name (& cursor , name , sizeof (name ), & off );
185+ if (result < 0 && result != - UNW_ENOMEM )
186+ break ;
187+
188+ g_ptr_array_insert (trace , -1 , g_strdup (name ));
189+ }
190+
191+ found = g_hash_table_lookup_extended (gobject_list_state .origins ,
192+ (gpointer ) key ,
193+ NULL , (gpointer * )& root );
194+ if (!found ) {
195+ root = bt_create (g_strdup (key ));
196+ g_hash_table_insert (gobject_list_state .origins , (gpointer ) key , root );
197+ }
198+ bt_insert (root , trace , is_ref );
199+ g_ptr_array_unref (trace );
200+ #endif
201+ }
202+
144203static void
145204print_trace (void )
146205{
@@ -229,13 +288,31 @@ _sig_usr2_handler (G_GNUC_UNUSED int signal)
229288 G_UNLOCK (gobject_list );
230289}
231290
291+ #ifdef WITH_ORIGINS_TRACE
292+ static void
293+ print_refs (G_GNUC_UNUSED gpointer key , gpointer value , gpointer user_data )
294+ {
295+ gint * no = (gpointer ) user_data ;
296+ BtTrie * bt_trie = value ;
297+ g_print ("#%d\n" , ++ * no );
298+ bt_print_tree (bt_trie , 0 );
299+ }
300+ #endif
301+
232302static void
233303print_still_alive (void )
234304{
235305 g_print ("\nStill Alive:\n" );
236306
237307 G_LOCK (gobject_list );
238308 _dump_object_list (gobject_list_state .objects );
309+ #ifdef WITH_ORIGINS_TRACE
310+ if (display_filter (DISPLAY_FLAG_TRACEREFS )) {
311+ guint no = 0 ;
312+ g_print ("\nReferences:\n" );
313+ g_hash_table_foreach (gobject_list_state .origins , print_refs , (gpointer ) & no );
314+ }
315+ #endif
239316 G_UNLOCK (gobject_list );
240317}
241318
@@ -286,6 +363,10 @@ get_func (const char *func_name)
286363 gobject_list_state .objects = g_hash_table_new (NULL , NULL );
287364 gobject_list_state .added = g_hash_table_new (NULL , NULL );
288365 gobject_list_state .removed = g_hash_table_new_full (NULL , NULL , NULL , g_free );
366+ #ifdef WITH_ORIGINS_TRACE
367+ gobject_list_state .origins =
368+ g_hash_table_new_full (NULL , NULL , NULL , (GDestroyNotify ) bt_free );
369+ #endif
289370
290371 /* Set up exit handler */
291372 atexit (_exiting );
@@ -329,6 +410,9 @@ _object_finalized (G_GNUC_UNUSED gpointer data,
329410
330411 g_hash_table_remove (gobject_list_state .objects , obj );
331412 g_hash_table_remove (gobject_list_state .added , obj );
413+ #ifdef WITH_ORIGINS_TRACE
414+ g_hash_table_remove (gobject_list_state .origins , obj );
415+ #endif
332416
333417 G_UNLOCK (gobject_list );
334418}
@@ -360,6 +444,7 @@ g_object_new (GType type,
360444 {
361445 g_print (" ++ Created object %p, %s\n" , obj , obj_name );
362446 print_trace ();
447+ save_trace (obj_name , TRUE);
363448 }
364449
365450 /* FIXME: For thread safety, GWeakRef should be used here, except it
@@ -409,6 +494,7 @@ g_object_ref (gpointer object)
409494 g_print (" + Reffed object %p, %s; ref_count: %d -> %d\n" ,
410495 obj , obj_name , ref_count , obj -> ref_count );
411496 print_trace ();
497+ save_trace (obj_name , TRUE);
412498 }
413499
414500 return ret ;
@@ -430,6 +516,7 @@ g_object_unref (gpointer object)
430516 g_print (" - Unreffed object %p, %s; ref_count: %d -> %d\n" ,
431517 obj , obj_name , obj -> ref_count , obj -> ref_count - 1 );
432518 print_trace ();
519+ save_trace (obj_name , FALSE);
433520 }
434521
435522 real_g_object_unref (object );
0 commit comments