I am trying to track down a problem with leaking cairo_t context structs when using cairo from python. I running under an up to date fedora 20 system. (I suspect that other structs are leaking as well, but I have not deep dived them yet). The attached example program can be run under valgrind to show the leak. The program takes a repeat count that you can use to tell the leak from the startup overhead. python gdk-cairo-leak.py 10 I think the bug is in the reference counting of the cairo_t struct. After creation the ref_count is 2 that that means that it will never be freed as the python object will be deleted causing cairo_destroy to be called that will take the ref count down to 1 but not 0 required to free the storage. Is there a mecahnism to mark the cairo_create and not needing an extra cairo_reference call? I am happy to work up a patch if someone can give me some insight into how the reference counting is supposed to work in the class of API. The following GDB session should show the bug. I have determined the cr->ref_count address and set a watch point. I have annotate the key observations with *****. (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /usr/bin/python gdk-cairo-leak-barry.py 1 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". [New Thread 0x7fffe67e1700 (LWP 18064)] cairo_create Hardware watchpoint 12: *(int *)0x9d8c60 ***** 0x9d8c60 is the cr->ref_count address Old value = 0 New value = 1 _cairo_init (cr=cr entry=0x9d8c60, backend=backend entry=0x7fffea9758a0 <_cairo_default_context_backend>) at cairo.c:247 247 cr->status = CAIRO_STATUS_SUCCESS; ***** cairo_create returns the cairo_t with a ref_count of 1. (gdb) bt 10 #0 _cairo_init (cr=cr entry=0x9d8c60, backend=backend entry=0x7fffea9758a0 <_cairo_default_context_backend>) at cairo.c:247 #1 0x00007fffea681d08 in _cairo_default_context_init (cr=cr entry=0x9d8c60, target=0x9d8640) at cairo-default-context.c:1445 #2 0x00007fffea681d9d in _cairo_default_context_create (target=<optimized out>) at cairo-default-context.c:1468 #3 0x00007fffec9be383 in gdk_cairo_create (window=0x9b32b0) at gdkwindow.c:3180 #4 0x00007fffef2b7d8c in ffi_call_unix64 () at ../src/x86/unix64.S:76 #5 0x00007fffef2b76bc in ffi_call (cif=cif entry=0x7fffffffc980, fn=fn entry=0x7fffec9be310 <gdk_cairo_create>, rvalue=rvalue entry=0x7fffffffc960, avalue=avalue entry=0x7fffffffc8a0) at ../src/x86/ffi64.c:522 #6 0x00007ffff01ade49 in g_callable_info_invoke (info=info entry=0x9d55e0, function=0x7fffec9be310 <gdk_cairo_create>, in_args=in_args entry=0x9b6c50, n_in_args=n_in_args entry=1, out_args=out_args entry=0x0, n_out_args=n_out_args entry=0, return_value=return_value entry=0x7fffffffcb58, is_method=is_method entry=0, throws=0, error=error entry=0x7fffffffcb08) at girepository/gicallableinfo.c:680 #7 0x00007ffff01af199 in g_function_info_invoke (info=info entry=0x9d55e0, in_args=0x9b6c50, n_in_args=1, out_args=0x0, n_out_args=0, return_value=return_value entry=0x7fffffffcb58, error=error entry=0x7fffffffcb08) at girepository/gifunctioninfo.c:274 #8 0x00007ffff03ebab7 in _invoke_callable (function_ptr=0x0, callable_info=0x9d55e0, cache=0x9b52a0, state=0x7fffffffcb10) at pygi- invoke.c:64 #9 pygi_callable_info_invoke (info=<optimized out>, py_args=<optimized out>, kwargs=<optimized out>, cache=<optimized out>, function_ptr=<optimized out>, user_data=<optimized out>) at pygi-invoke.c:652 (More stack frames follow...) (gdb) c Continuing. Breakpoint 4, gdk_cairo_create (window=0x9b32b0) at gdkwindow.c:3182 3182 if (window->impl_window->paint_stack) (gdb) c Continuing. Hardware watchpoint 12: *(int *)0x9d8c60 Old value = 1 New value = 2 cairo_reference (cr=cr entry=0x9d8c60) at cairo.c:279 279 } (gdb) bt 10 #0 cairo_reference (cr=cr entry=0x9d8c60) at cairo.c:279 #1 0x00007fffe5ddebac in cairo_context_from_arg (interface_info=<optimized out>, data=0x9d8c60) at pygi-foreign-cairo.c:63 #2 0x00007ffff03f1d2c in _pygi_marshal_to_py_interface_struct_cache_adapter (state=<optimized out>, callable_cache=<optimized out>, arg_cache=<optimized out>, arg=<optimized out>) at pygi-marshal-to- py.c:752 #3 0x00007ffff03ebc1d in _invoke_marshal_out_args (cache=0x9b52a0, state=0x7fffffffcb10) at pygi-invoke.c:543 #4 pygi_callable_info_invoke (info=<optimized out>, py_args=<optimized out>, kwargs=<optimized out>, cache=<optimized out>, function_ptr=<optimized out>, user_data=<optimized out>) at pygi-invoke.c:657 #5 0x00007ffff7a610d3 in PyObject_Call (func=func entry=<gi.FunctionInfo at remote 0x95dc38>, arg=arg entry=(<X11Window at remote 0x95b1e0>,), kw=kw entry=0x0) at /usr/src/debug/Python-2.7.5/Objects/abstract.c:2529 #6 0x00007ffff7af537c in do_call (nk=<optimized out>, na=1, pp_stack=0x7fffffffcd00, func=<gi.FunctionInfo at remote 0x95dc38>) at /usr/src/debug/Python-2.7.5/Python/ceval.c:4316 #7 call_function (oparg=<optimized out>, pp_stack=0x7fffffffcd00) at /usr/src/debug/Python-2.7.5/Python/ceval.c:4121 #8 PyEval_EvalFrameEx ( f=f entry=Frame 0x9d5cc0, for file /usr/lib64/python2.7/site- packages/gi/overrides/Gdk.py, line 159, in cairo_create (self=<X11Window at remote 0x95b1e0>), throwflag=throwflag entry=0) at /usr/src/debug/Python-2.7.5/Python/ceval.c:2740 #9 0x00007ffff7af7980 in fast_function (nk=<optimized out>, na=1, n=1, pp_stack=0x7fffffffce60, func=<function at remote 0x8ccd70>) at /usr/src/debug/Python-2.7.5/Python/ceval.c:4184 (More stack frames follow...) ***** cairo_reference is called to as part of the process of ***** marshelling the cairo_t into python land. ***** As far as I can tell this is the root cause of the leak. ***** THere is no conditional logic here to prevent extra ***** ref_count increment that I could see in the code. (gdb) c Continuing. sleep ^C ***** I added a time.sleep( 10 ) so that we can see the ref count of the returned cairo_t. Program received signal SIGINT, Interrupt. 0x00007ffff6e1a463 in select () at ../sysdeps/unix/syscall-template.S:81 81 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) (gdb) p *(int *)0x9d8c60 $57 = 2 **** As expected from the available code the ref_count is 2 which 1 to big. (gdb) c Continuing. done Breakpoint 3, INT_cairo_destroy (cr=0x9d8c60) at cairo.c:300 300 if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr-
ref_count))
(gdb) bt 10 #0 INT_cairo_destroy (cr=0x9d8c60) at cairo.c:300 #1 0x00007fffe67ebd72 in pycairo_dealloc (o=0x7ffff7f68310) at context.c:75 #2 0x00007ffff7a84b02 in frame_dealloc (f=Frame 0x9d5af0, for file gdk-cairo- leak-barry.py, line 49, in draw ()) at /usr/src/debug/Python-2.7.5/Objects/frameobject.c:460 #3 0x00007ffff7af799c in fast_function (nk=<optimized out>, na=<optimized out>, n=1, pp_stack=0x7fffffffcfc0, func=<function at remote 0x9515f0>) at /usr/src/debug/Python-2.7.5/Python/ceval.c:4186 #4 call_function (oparg=<optimized out>, pp_stack=0x7fffffffcfc0) at /usr/src/debug/Python-2.7.5/Python/ceval.c:4119 #5 PyEval_EvalFrameEx ( f=f entry=Frame 0x9d5920, for file gdk-cairo-leak-barry.py, line 34, in __gdkEventHandler (self=<CairoLeak(repeat_count=0, window=<X11Window at remote 0x95b1e0>, letterbox_colour=[255, 0, 0, 255]) at remote 0x958200>, event=<Event at remote 0x8b58d8>, data=None), throwflag=throwflag entry=0) at /usr/src/debug/Python-2.7.5/Python/ceval.c:2740 #6 0x00007ffff7af91dd in PyEval_EvalCodeEx (co=<optimized out>, globals=<optimized out>, locals=locals entry=0x0, args=args entry=0x95b068, argcount=3, kws=kws entry=0x0, kwcount=kwcount entry=0, defs=defs entry=0x0, defcount=defcount entry=0, closure=0x0) at /usr/src/debug/Python-2.7.5/Python/ceval.c:3330 #7 0x00007ffff7a860d8 in function_call (func=<function at remote 0x951578>, arg=(<CairoLeak(repeat_count=0, window=<X11Window at remote 0x95b1e0>, letterbox_colour=[255, 0, 0, 255]) at remote 0x958200>, <Event at remote 0x8b58d8>, None), kw=0x0) at /usr/src/debug/Python-2.7.5/Objects/funcobject.c:526 #8 0x00007ffff7a610d3 in PyObject_Call (func=func entry=<function at remote 0x951578>, arg=arg entry=(<CairoLeak(repeat_count=0, window=<X11Window at remote 0x95b1e0>, letterbox_colour=[255, 0, 0, 255]) at remote 0x958200>, <Event at remote 0x8b58d8>, None), kw=kw entry=0x0) at /usr/src/debug/Python-2.7.5/Objects/abstract.c:2529 #9 0x00007ffff7a700c5 in instancemethod_call (func=<function at remote 0x951578>, arg=(<CairoLeak(repeat_count=0, window=<X11Window at remote 0x95b1e0>, letterbox_colour=[255, 0, 0, 255]) at remote 0x958200>, <Event at remote 0x8b58d8>, None), kw=0x0) at /usr/src/debug/Python-2.7.5/Objects/classobject.c:2602 (More stack frames follow...) (gdb) c Continuing. ***** we hit cairo_destory on the freeing of the python side object. Hardware watchpoint 12: *(int *)0x9d8c60 Old value = 2 New value = 1 0x00007fffea678e04 in INT_cairo_destroy (cr=0x9d8c60) at cairo.c:305 305 if (! _cairo_reference_count_dec_and_test (&cr->ref_count)) ***** and the ref count goes to 1. (gdb) c Continuing. [Thread 0x7ffff7fe7740 (LWP 18063) exited] [Inferior 1 (process 18063) exited normally] ***** process exits with 1 leaked cairo_t. (gdb) Barry
Attachment:
gdk-cairo-leak.py
Description: Text Data