Description
I'm not sure if this is the best way to reproduce/detect it, but please bear with me.
- Install gdb, valgrind and gstreamer's debugging symbol (for Ubuntu 20.04, it's
libgstreamer1.0-0-dbg
). - Prepare 2 terminal windows. The first one is for node and the second one is for gdb.
- On terminal 1, launch node like this:
valgrind --vgdb=yes node --expose-gc
. - Initialize node-gtk and Gstreamer, as usual. Valgrind may print something; it's unrelated to this issue.
const gi = require('node-gtk')
const Gst = gi.require('Gst', '1.0')
Gst.init(null);
- On terminal 2, run
gdb
. Then, connect to valgrind using:
target remote | vgdb
- Set the breakpoint on
gst_buffer_new()
this way, so that the return value gets print automatically:
break gst_buffer_new
command
finish
end
continue
Unfortunately, we cannot set it to also automatically continue after finishing. So we have to do it manually.
7. On terminal 1, run the following code:
let b;
b = new Gst.Buffer(); // Won't leak.
b = Gst.Buffer.new(); // Will leak.
b = null;
b; // Apparently clears JIT cache
global.gc(); // Make sure that things that should be freed will be freed
While running this, GDB should break 2 times, with a line for each value returned. Take note of these addresses.
8. After global.gc()
finishes, press Ctrl+C on terminal 2 to bring up the prompt. Then, run:
monitor v.info location <address 1>
There will be a long output, but the most important part is the first line. It should be Address <address 1> is 0 bytes inside a block of size 280 free'd
, which indicate that this address is freed.
9. Do the same with address 2 (except Ctrl+C). Now, the first line will change to Address <address 2> is 0 bytes inside a block of size 280 alloc'd
. This means the address is not freed.
10. Just in case something else is actually holding to the second GstBuffer, run:
monitor who_points_at <address 2>
The only output should be Searching for pointers to <address 2>
. This means nothing can refer to it now; this GstBuffer is now leaked.
GstBuffer
is a GstMiniObject
, which in itself is a boxed type. I've looked into BoxedConstructor()
inside boxed.cc
; it seems to only either make a copy to itself or assume that the memory is managed elsewhere. The condition (mustCopy
) differs in different code paths that lead to it, which I'm unable to follow. So, I'm not sure where to start.