Subject: Re: bug#43389: 28.0.50; Emacs memory leaks using hard disk all time

From: Trevor Bentley <[email-masked]>
Date: Wed, 20 Jan 2021 13:02:23 +0100

> I tried setting a breakpoint in longjmp, but it's called 3 times 
> for every keypress!  That's inconvenient.  Running one single 
> instance now with a conditional breakpoint on longjmp: it will 
> break if longjmp is called while it's in unblock_input(). 

I disappeared for ages because... the problem disappeared.  I went 
a month without reproducing it, despite putting a hold on 
upgrading both system and emacs packages while debugging.  Very 
odd.

But today it appeared again.  And, for the first time, in a gdb 
session with breakpoints to confirm my theory.  I believe I've 
found the underlying issue.

If you have a look at this long backtrace, you can see that we are 
inside a garbage_collect call (frame #38).  An X11 focus event 
comes in, triggering a bunch of GTK/GDK/X calls.  Mysteriously, 
this leads to a maybe_quit() call which in turn calls longjmp(). 
longjmp jumps right out of the garbage collect, leaving it 
unfinished.

Internally in garbage_collect, consing_until_gc was set to the 
HI_THRESHOLD upper-bound.  It is left that way when longjmp leaps 
out of it, and no automatic garbage collect is ever performed 
again.  This is the start of the ballooning memory.

This also explains why my minimized emacs session never hits it 
and my work sessions hit it very often, and less often on 
weekends.  It's triggered by focus events.  I flitter around 
between windows constantly while working.

I don't know emacs internals, so you'll have to figure out if this 
is X dependent (probably) and/or GTK dependent.  It should be 
possible to come up with an easier way to reproduce it now.

Backtrace:
-----------
(gdb) bt #0  0x00007ffff5571230 in siglongjmp () at 
/usr/lib/libc.so.6 #1  0x00005555557bd38d in unwind_to_catch 
(catch=0x555555dfc320, type=NONLOCAL_EXIT_THROW, value=0x30) at 
eval.c:1181 #2  0x00005555557bd427 in Fthrow (tag=0xe75830, 
value=0x30) at eval.c:1198 #3  0x00005555557bdea7 in 
process_quit_flag () at eval.c:1526 #4  0x00005555557bdeef in 
maybe_quit () at eval.c:1547 #5  0x00005555557cbbb1 in Fassq 
(key=0xd0b0, alist=0x55555901c573) at fns.c:1609 #6 
0x0000555555632b63 in window_parameter (w=0x555555f2d088, 
parameter=0xd0b0) at window.c:2262 #7  0x000055555563a075 in 
window_wants_tab_line (w=0x555555f2d088) at window.c:5410 #8 
0x00005555555c22b1 in get_phys_cursor_geometry (w=0x555555f2d088, 
row=0x55555d9f3ef0, glyph=0x55555fd20e00, xp=0x7fffffff9c48, 
yp=0x7fffffff9c4c, heightp=0x7fffffff9c50) at xdisp.c:2650 #9 
0x00005555556c1b12 in x_draw_hollow_cursor (w=0x555555f2d088, 
row=0x55555d9f3ef0) at xterm.c:9495 #10 0x00005555556c24f9 in 
x_draw_window_cursor (w=0x555555f2d088, glyph_row=0x55555d9f3ef0, 
x=32, y=678, cursor_type=HOLLOW_BOX_CURSOR, cursor_width=1, 
on_p=true, active_p=false) at xterm.c:9682 #11 0x000055555561a922 
in display_and_set_cursor (w=0x555555f2d088, on=true, hpos=2, 
vpos=18, x=32, y=678) at xdisp.c:31738 #12 0x000055555561aa5b in 
update_window_cursor (w=0x555555f2d088, on=true) at xdisp.c:31773 
#13 0x000055555561aabf in update_cursor_in_window_tree 
(w=0x555555f2d088, on_p=true) at xdisp.c:31791 #14 
0x000055555561aaab in update_cursor_in_window_tree 
(w=0x55555907a490, on_p=true) at xdisp.c:31789 #15 
0x000055555561aaab in update_cursor_in_window_tree 
(w=0x55555a514b68, on_p=true) at xdisp.c:31789 #16 
0x000055555561ab37 in gui_update_cursor (f=0x555556625468, 
on_p=true) at xdisp.c:31805 #17 0x00005555556b9829 in 
x_frame_unhighlight (f=0x555556625468) at xterm.c:4490 #18 
0x00005555556ba22d in x_frame_rehighlight (dpyinfo=0x55555626d6c0) 
at xterm.c:4852 #19 0x00005555556b98fc in x_new_focus_frame 
(dpyinfo=0x55555626d6c0, frame=0x0) at xterm.c:4520 #20 
0x00005555556b9a3d in x_focus_changed (type=10, state=2, 
dpyinfo=0x55555626d6c0, frame=0x555556625468, bufp=0x7fffffffa0d0) 
at xterm.c:4554 #21 0x00005555556ba0a6 in x_detect_focus_change 
(dpyinfo=0x55555626d6c0, frame=0x555556625468, 
event=0x7fffffffa840, bufp=0x7fffffffa0d0) at xterm.c:4787 #22 
0x00005555556c0235 in handle_one_xevent (dpyinfo=0x55555626d6c0, 
event=0x7fffffffa840, finish=0x555555c901d4 <current_finish>, 
hold_quit=0x7fffffffab50) at xterm.c:8810 #23 0x00005555556bde28 
in event_handler_gdk (gxev=0x7fffffffa840, ev=0x55555cccf0c0, 
data=0x0) at xterm.c:7768 #24 0x00007ffff75f780f in  () at 
/usr/lib/libgdk-3.so.0 #25 0x00007ffff75fb3cb in  () at 
/usr/lib/libgdk-3.so.0 #26 0x00007ffff759f15b in 
gdk_display_get_event () at /usr/lib/libgdk-3.so.0 #27 
0x00007ffff75fb104 in  () at /usr/lib/libgdk-3.so.0 #28 
0x00007ffff6fcb8f4 in g_main_context_dispatch () at 
/usr/lib/libglib-2.0.so.0 #29 0x00007ffff701f821 in  () at 
/usr/lib/libglib-2.0.so.0 #30 0x00007ffff6fca121 in 
g_main_context_iteration () at /usr/lib/libglib-2.0.so.0 #31 
0x00007ffff784e2c7 in gtk_main_iteration () at 
/usr/lib/libgtk-3.so.0 #32 0x00005555556c1821 in XTread_socket 
(terminal=0x5555560b7460, hold_quit=0x7fffffffab50) at 
xterm.c:9395 #33 0x000055555570f3a2 in gobble_input () at 
keyboard.c:6890 #34 0x000055555570f894 in handle_async_input () at 
keyboard.c:7121 #35 0x000055555570f8dd in process_pending_signals 
() at keyboard.c:7139 #36 0x000055555570f9cf in unblock_input_to 
(level=0) at keyboard.c:7162 #37 0x000055555570fa4c in 
unblock_input () at keyboard.c:7187 #38 0x000055555578f49a in 
garbage_collect () at alloc.c:6121 #39 0x000055555578efe7 in 
maybe_garbage_collect () at alloc.c:5964 #40 0x00005555557bb292 in 
maybe_gc () at lisp.h:5041 #41 0x00005555557c12d6 in Ffuncall 
(nargs=2, args=0x7fffffffad68) at eval.c:2793 #42 
0x000055555580f7d6 in exec_byte_code
...  --------------

For breakpoints, I am doing the following:

1) make a global static variable in alloc.c:
static int enable_gc_trace = 0;

2) in garbage_collect(), 'enable_gc_trace++' when it starts and 
'enable_gc_trace--' when it ends.  I just wrapped the call to 
unblock_input(), but you could widen that window.

3) run in gdb with conditional breakpoints on GC and longjmp 
functions:
b siglongjmp if enable_gc_trace > 0
b internal_catch if enable_gc_trace > 0
b internal_catch_all if enable_gc_trace > 0
b maybe_garbage_collect if enable_gc_trace > 0

-Trevor