Skip to content

Instantly share code, notes, and snippets.

@alanmcgovern
Created April 29, 2017 20:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alanmcgovern/e0e43fe1365acc5169f34b8c4d29b4cd to your computer and use it in GitHub Desktop.
Save alanmcgovern/e0e43fe1365acc5169f34b8c4d29b4cd to your computer and use it in GitHub Desktop.
#include <glib-object.h>
#include <dlfcn.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <execinfo.h>
#define BACKTRACE_DEPTH 50
typedef enum {
EVENT_CREATED = 0,
EVENT_REF = 1,
EVENT_UNREF = 2,
} EventType;
typedef enum
{
DISPLAY_FLAG_NONE = 0,
DISPLAY_FLAG_CREATE = 1,
DISPLAY_FLAG_REFS = 1 << 2,
DISPLAY_FLAG_BACKTRACE = 1 << 3,
DISPLAY_FLAG_ALL =
DISPLAY_FLAG_CREATE | DISPLAY_FLAG_REFS | DISPLAY_FLAG_BACKTRACE,
DISPLAY_FLAG_DEFAULT = DISPLAY_FLAG_CREATE | DISPLAY_FLAG_BACKTRACE,
} DisplayFlags;
typedef struct {
const gchar *name;
DisplayFlags flag;
} DisplayFlagsMapItem;
typedef struct {
GHashTable *objects; /* owned */
} ObjectData;
typedef struct {
EventType type;
void **backtrace;
int symbols;
int old_ref_count;
int new_ref_count;
} Event;
extern char *mono_pmip (void *ip);
/* GObject overrides */
gpointer g_object_new (GType type, const char *first, ...);
gpointer g_object_ref (gpointer object);
void g_object_unref (gpointer object);
static void g_object_finalized (G_GNUC_UNUSED gpointer data, GObject *obj);
/* Functions to deal with tracking ref/unref/creation events */
Event * event_new (EventType type);
Event * event_created_new ();
Event * event_ref_new (int old_ref_count, int new_ref_count);
Event * event_unref_new (int old_ref_count, int new_ref_count);
void event_destroy (Event *event);
/* Helper functions to print out our data */
static void print_trace (void **array, int symbols);
static void print_event (gpointer data, gpointer user_data);
DisplayFlagsMapItem display_flags_map[] =
{
{ "none", DISPLAY_FLAG_NONE },
{ "create", DISPLAY_FLAG_CREATE },
{ "refs", DISPLAY_FLAG_REFS },
{ "backtrace", DISPLAY_FLAG_BACKTRACE },
{ "all", DISPLAY_FLAG_ALL },
};
Event *
event_new (EventType type)
{
Event *event;
event = calloc (1, sizeof (Event));
event->type = type;
event->backtrace = malloc (sizeof (void*) * BACKTRACE_DEPTH);
event->symbols = backtrace (event->backtrace, BACKTRACE_DEPTH);
return event;
}
Event *
event_created_new ()
{
return event_new (EVENT_CREATED);
}
Event *
event_ref_new (int old_ref_count, int new_ref_count)
{
Event *event;
event = event_new (EVENT_REF);
event->old_ref_count = old_ref_count;
event->new_ref_count = new_ref_count;
return event;
}
Event *
event_unref_new (int old_ref_count, int new_ref_count)
{
Event *event;
event = event_new (EVENT_UNREF);
event->old_ref_count = old_ref_count;
event->new_ref_count = new_ref_count;
return event;
}
void
event_destroy (Event *event)
{
free (event->backtrace);
free (event);
}
static void
print_event (gpointer data, G_GNUC_UNUSED gpointer user_data)
{
g_print ("\n");
Event *event = (Event *)data;
if (event->type == EVENT_CREATED)
g_print ("\tCreated\n");
else if (event->type == EVENT_REF)
g_print ("\tRef %d -> %d\n", event->old_ref_count, event->new_ref_count);
else if (event->type == EVENT_UNREF)
g_print ("\tUnref %d -> %d\n", event->old_ref_count, event->new_ref_count);
else
g_print ("\tUnknown event...\n");
print_trace (event->backtrace, event->symbols);
}
static void
print_trace (void **array, int symbols)
{
char **names;
char *managed_frame;
int i;
names = backtrace_symbols (array, symbols);
for (i = 1; i < symbols; ++i) {
if ((managed_frame = mono_pmip (array [i])) != NULL)
g_print ("\t%d %s\n", i, managed_frame);
else
g_print ("\t%s\n", names [i]);
}
}
/* Global static state, which must be accessed with the @gobject_list mutex
* held. */
static volatile ObjectData gobject_list_state = { NULL, };
/* Global lock protecting access to @gobject_list_state, since GObject methods
* may be called from multiple threads concurrently. */
G_LOCK_DEFINE_STATIC (gobject_list);
static gboolean
object_filter (const char *obj_name)
{
const char *filter = "GdkWindow";//g_getenv ("GOBJECT_LIST_FILTER");
if (filter == NULL)
return TRUE;
else
return (strncmp (filter, obj_name, strlen (filter)) == 0);
}
static void
_dump_object_list (GHashTable *hash)
{
GHashTableIter iter;
GObject *obj;
GPtrArray *events;
g_hash_table_iter_init (&iter, hash);
while (g_hash_table_iter_next (&iter, (gpointer) &obj, (gpointer)&events))
{
/* FIXME: Not really sure how we get to this state. */
if (obj == NULL || obj->ref_count == 0 || obj->ref_count > 10)
continue;
g_print (" - %p, %s (refcount: %d):\n", obj, G_OBJECT_TYPE_NAME (obj), obj->ref_count);
g_ptr_array_foreach (events, print_event, NULL);
}
g_print ("%u objects\n", g_hash_table_size (hash));
}
static void
print_still_alive (void)
{
g_print ("\nStill Alive:\n");
G_LOCK (gobject_list);
_dump_object_list (gobject_list_state.objects);
G_UNLOCK (gobject_list);
}
static void
_sig_usr1_handler (G_GNUC_UNUSED int signal)
{
print_still_alive ();
}
static void
_exiting (void)
{
print_still_alive ();
}
static void *
get_func (const char *func_name)
{
static void *handle = NULL;
void *func;
char *error;
G_LOCK (gobject_list);
if (G_UNLIKELY (g_once_init_enter (&handle)))
{
void *_handle;
_handle = dlopen("/Library/Frameworks/Mono.framework/Versions/5.2.0/lib/libgobject-2.0.0.dylib", RTLD_LAZY);
if (_handle == NULL)
g_error ("Failed to open libgobject-2.0.0.dylib: %s", dlerror ());
/* set up objects map */
gobject_list_state.objects = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_ptr_array_unref);
/* Set up exit handler */
atexit (_exiting);
/* Prevent propagation to child processes. */
if (g_getenv ("GOBJECT_PROPAGATE_LD_PRELOAD") == NULL)
{
g_unsetenv ("LD_PRELOAD");
g_unsetenv ("DYLD_INSERT_LIBRARIES");
}
g_once_init_leave (&handle, _handle);
}
func = dlsym (handle, func_name);
if ((error = dlerror ()) != NULL)
g_error ("Failed to find symbol: %s", error);
G_UNLOCK (gobject_list);
return func;
}
gpointer
g_object_new (GType type,
const char *first,
...)
{
static gpointer (* real_g_object_new_valist) (GType, const char *, va_list) = NULL;
va_list var_args;
GObject *obj;
const char *obj_name;
if (!real_g_object_new_valist)
real_g_object_new_valist = get_func ("g_object_new_valist");
va_start (var_args, first);
obj = real_g_object_new_valist (type, first, var_args);
va_end (var_args);
obj_name = G_OBJECT_TYPE_NAME (obj);
G_LOCK (gobject_list);
if (object_filter (obj_name))
{
g_object_weak_ref (obj, g_object_finalized, NULL);
Event *event = event_created_new ();
GPtrArray *array = g_ptr_array_new_with_free_func ((GDestroyNotify) event_destroy);
g_ptr_array_add (array, event);
g_hash_table_insert (gobject_list_state.objects, obj, array);
}
G_UNLOCK (gobject_list);
return obj;
}
gpointer
g_object_ref (gpointer object)
{
static gpointer (* real_g_object_ref) (gpointer) = NULL;
GObject *obj = G_OBJECT (object);
guint ref_count;
GObject *ret;
if (!real_g_object_ref)
real_g_object_ref = get_func ("g_object_ref");
ref_count = obj->ref_count;
ret = real_g_object_ref (object);
G_LOCK (gobject_list);
GPtrArray *events = (GPtrArray *) g_hash_table_lookup (gobject_list_state.objects, object);
if (events)
g_ptr_array_add (events, event_ref_new (ref_count, obj->ref_count));
G_UNLOCK (gobject_list);
return ret;
}
void
g_object_unref (gpointer object)
{
static void (* real_g_object_unref) (gpointer) = NULL;
GObject *obj = G_OBJECT (object);
if (!real_g_object_unref)
real_g_object_unref = get_func ("g_object_unref");
G_LOCK (gobject_list);
GPtrArray *events = (GPtrArray *) g_hash_table_lookup (gobject_list_state.objects, object);
if (events)
g_ptr_array_add (events, event_unref_new (obj->ref_count, obj->ref_count - 1));
G_UNLOCK (gobject_list);
real_g_object_unref (object);
}
static void
g_object_finalized (G_GNUC_UNUSED gpointer data, GObject *obj)
{
G_LOCK (gobject_list);
g_hash_table_remove (gobject_list_state.objects, obj);
G_UNLOCK (gobject_list);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment