Skip to content

Instantly share code, notes, and snippets.

@oleavr
Last active November 18, 2019 18:52
Show Gist options
  • Save oleavr/a845ca9839d74c5dd2e34ef976d0d9b6 to your computer and use it in GitHub Desktop.
Save oleavr/a845ca9839d74c5dd2e34ef976d0d9b6 to your computer and use it in GitHub Desktop.
GObject leak tracker in 46 lines of code

GObject leak tracker

To use it on a running process, first pip install frida to grab Frida's python bindings and CLI tools, then:

$ frida FooApp -l gobject-leak-tracker.js

Then in the REPL you can call count() and list() to inspect the instances currently alive:

[Local::ProcName::FooApp]-> count()
4
[Local::ProcName::FooApp]-> list()
0x7fc119006360: {
  "type": "GTask",
  "creator": [
    "0x1061cdaff FooApp!g_object_newv gobject.c:1921",
    "0x1061cd783 FooApp!g_object_new gobject.c:1614",
    "0x10614066d FooApp!g_task_new gtask.c:708",
    "0x10613bda0 FooApp!g_socket_listener_accept_socket_async gsocketlistener.c:816",
    "0x10613c0bd FooApp!g_socket_listener_accept_async gsocketlistener.c:887",
    "0x10613f9cb FooApp!do_accept gsocketservice.c:117",
    "0x10613fdab FooApp!g_socket_service_ready gsocketservice.c:307",
    "0x106141fd4 FooApp!g_task_return_now gtask.c:1124",
    "0x106141146 FooApp!g_task_return gtask.c:1183",
    "0x106141407 FooApp!g_task_return_pointer gtask.c:1555",
    "0x10613bef1 FooApp!accept_ready gsocketlistener.c:780",
    "0x106135cf1 FooApp!socket_source_dispatch gsocket.c:3290",
    "0x106212503 FooApp!g_main_dispatch gmain.c:3210",
    "0x106212350 FooApp!g_main_context_dispatch gmain.c:3828",
    "0x10621283e FooApp!g_main_context_iterate gmain.c:3898",
    "0x106212c42 FooApp!g_main_loop_run gmain.c:4089"
  ]
}
0x7fc11881a330: {
  "type": "GeeArrayList",
  "creator": [
    "0x10609d6e5 FooApp!gee_abstract_collection_constructor abstractcollection.c:539",
    "0x10609ebc5 FooApp!gee_abstract_list_constructor abstractlist.c:621",
    "0x10609c0c5 FooApp!gee_abstract_bidir_list_constructor abstractbidirlist.c:617",
    "0x1061d25e0 FooApp!g_object_new_with_custom_constructor gobject.c:1692",
    "0x1061ce6d0 FooApp!g_object_new_internal gobject.c:1772",
    "0x1061cdaff FooApp!g_object_newv gobject.c:1921",
    "0x1061cd783 FooApp!g_object_new gobject.c:1614",
    "0x10609c823 FooApp!gee_abstract_collection_construct abstractcollection.c:484",
    "0x10609df85 FooApp!gee_abstract_list_construct abstractlist.c:578",
    "0x10609b715 FooApp!gee_abstract_bidir_list_construct abstractbidirlist.c:574",
    "0x1060a1dd0 FooApp!gee_array_list_construct arraylist.c:612",
    "0x1060a2081 FooApp!gee_array_list_new arraylist.c:653",
    "0x10609a56a FooApp!__lambda14_ system.c:1464",
    "0x10609a3a5 FooApp!___lambda14__gsource_func system.c:1518",
    "0x10620e863 FooApp!g_idle_dispatch gmain.c:5485",
    "0x106212503 FooApp!g_main_dispatch gmain.c:3210"
  ]
}
0x7fc1199c55b0: {
  "type": "GeeFunctionsEqualDataFuncClosure",
  "creator": [
    "0x1060b08c1 FooApp!gee_functions_equal_data_func_closure_new functions.c:821",
    "0x1060a1ee4 FooApp!gee_array_list_construct arraylist.c:635",
    "0x1060a2081 FooApp!gee_array_list_new arraylist.c:653",
    "0x10609a56a FooApp!__lambda14_ system.c:1464",
    "0x10609a3a5 FooApp!___lambda14__gsource_func system.c:1518",
    "0x10620e863 FooApp!g_idle_dispatch gmain.c:5485",
    "0x106212503 FooApp!g_main_dispatch gmain.c:3210",
    "0x106212350 FooApp!g_main_context_dispatch gmain.c:3828",
    "0x10621283e FooApp!g_main_context_iterate gmain.c:3898",
    "0x106212c42 FooApp!g_main_loop_run gmain.c:4089",
    "0x10604c18f FooApp!frida_server_application_run server.c:866",
    "0x10604b959 FooApp!frida_server_main server.c:741",
    "0x10604c1c7 FooApp!main server.c:787",
    "0x10604aba4 FooApp!start"
  ]
}
0x7fc1199c49c0: {
  "type": "GTask",
  "creator": [
    "0x1061cdaff FooApp!g_object_newv gobject.c:1921",
    "0x1061cd783 FooApp!g_object_new gobject.c:1614",
    "0x10614066d FooApp!g_task_new gtask.c:708",
    "0x106193cc6 FooApp!_g_socket_read_with_control_messages gdbusprivate.c:176",
    "0x106193465 FooApp!_g_dbus_worker_do_read_unlocked gdbusprivate.c:876",
    "0x106193bee FooApp!_g_dbus_worker_do_read_cb gdbusprivate.c:814",
    "0x106141fd4 FooApp!g_task_return_now gtask.c:1124",
    "0x106142025 FooApp!complete_in_idle_cb gtask.c:1139",
    "0x10620e863 FooApp!g_idle_dispatch gmain.c:5485",
    "0x106212503 FooApp!g_main_dispatch gmain.c:3210",
    "0x106212350 FooApp!g_main_context_dispatch gmain.c:3828",
    "0x10621283e FooApp!g_main_context_iterate gmain.c:3898",
    "0x106212c42 FooApp!g_main_loop_run gmain.c:4089",
    "0x10619319e FooApp!gdbus_shared_thread_func gdbusprivate.c:251",
    "0x10623123d FooApp!g_thread_proxy gthread.c:764",
    "0x7fff8b1ec99d libsystem_pthread.dylib!_pthread_body"
  ]
}
undefined
[Local::ProcName::FooApp]->
var instances = {};
var _g_type_name = new NativeFunction(DebugSymbol.getFunctionByName('g_type_name'), 'pointer', ['pointer']);
function count() {
return Object.keys(instances).length;
}
function list() {
Object.keys(instances).forEach(function (handle) {
var details = instances[handle];
console.log(handle + ': ' + JSON.stringify(details, null, 2));
});
}
Interceptor.attach(DebugSymbol.getFunctionByName('g_type_create_instance'), {
onLeave: function (retval) {
instances[retval] = {
type: g_type_name(G_TYPE_FROM_INSTANCE(retval)),
creator: backtrace(this.context)
};
}
});
Interceptor.attach(DebugSymbol.getFunctionByName('g_type_free_instance'), {
onEnter: function (args) {
var handle = args[0];
delete instances[handle];
}
});
function G_TYPE_FROM_INSTANCE(instance) {
var klass = Memory.readPointer(instance);
var gtype = Memory.readPointer(klass);
return gtype;
}
function g_type_name(type) {
return Memory.readUtf8String(_g_type_name(type));
}
function backtrace(context) {
return Thread.backtrace(context).map(DebugSymbol.fromAddress).map(function (symbol) { return symbol.toString(); });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment