Debugging an Emacs segfault
During an attempt to install Jinx, my Emacs broke. In this attempt I tried to update the system, Emacs, and Doom Emacs, to try to fix the issue, but it created a new one. The resulting error was not clear, just a segmentation fault, and here I will try to walk through the thought process of debugging it.
I isolated the issue by removing Doom Emacs, and the problem went away. The next attempt was with a fresh install of Doom Emacs, the same problem returned. It was tested and the same results were obtained with Emacs 28.1, 28.2 and 29.1.
A naive attempt
My first attempt to try to determine a possible cause for this was to compile
Emacs with the debugging symbols required for a better understanding of the
situation. When using nix
this can be accomplished with:
nix-build -E 'with import <nixpkgs> {}; enableDebugging emacs29-gtk3'
This will generate an output folder named result
in which the binaries of Emacs
will be located:
./result/bin/emacs-29.1 --debug-init
Loading /home/rafael/.emacs.d/lisp/doom.el (source)... Loading /home/rafael/.emacs.d/lisp/doom.el (source)...done 0.022212:*:load: doom-start nil 0.027026:*:hook:doom-before-init-hook: run doom--begin-init-h 0.027076::context: +init (t) 0.027440:*:init:load: ~/.doom.d/init t Fatal error 11: Segmentation fault Backtrace: ./result/bin/emacs-29.1(emacs_backtrace+0x46)[0x585a5e] ./result/bin/emacs-29.1(terminate_due_to_signal+0x9c)[0x567eb3] ./result/bin/emacs-29.1[0x583792] ./result/bin/emacs-29.1[0x5837bf] ./result/bin/emacs-29.1[0x583823] ./result/bin/emacs-29.1[0x583912] /nix/store/9y8pmvk8gdwwznmkzxa6pwyah52xy3nk-glibc-2.38-27/lib/libc.so.6(+0x3da70)[0x7f6a386fda70] ./result/bin/emacs-29.1(lookup_image+0x81)[0x675dca] ./result/bin/emacs-29.1[0x49df99] ./result/bin/emacs-29.1[0x49e2cb] ./result/bin/emacs-29.1[0x49e926] ./result/bin/emacs-29.1[0x49918c] ./result/bin/emacs-29.1[0x4a16f3] ./result/bin/emacs-29.1[0x49ffa9] ./result/bin/emacs-29.1[0x4a7592] ./result/bin/emacs-29.1(try_window+0xe0)[0x4aabfa] ./result/bin/emacs-29.1[0x4c0b22] ./result/bin/emacs-29.1[0x4c2c3f] ./result/bin/emacs-29.1(internal_condition_case_1+0x63)[0x5e2b68] ./result/bin/emacs-29.1[0x48b9d8] ./result/bin/emacs-29.1[0x48b95d] ./result/bin/emacs-29.1[0x4b2db4] ./result/bin/emacs-29.1(redisplay_preserve_echo_area+0xb0)[0x4b3616] ./result/bin/emacs-29.1(Fredisplay+0x6b)[0x47208f] /nix/store/kshwj88iz6xkhsyhkw745x6ivghfx44d-emacs-gtk3-29.1/bin/../lib/emacs/29.1/native-lisp/29.1-4ebe15f7/preloaded/subr-13adf6a6-b5d2bdc1.eln(F7369742d666f72_sit_for_0+0x138)[0x7f6a3213b1e8] ./result/bin/emacs-29.1(funcall_subr+0xdb)[0x5e5194] ./result/bin/emacs-29.1(funcall_general+0x1b4)[0x5e6ad7] ./result/bin/emacs-29.1(Ffuncall+0xc2)[0x5e3b5c] /nix/store/kshwj88iz6xkhsyhkw745x6ivghfx44d-emacs-gtk3-29.1/lib/emacs/29.1/native-lisp/29.1-4ebe15f7/warnings-2 8e75f4d-9f48f568.eln(F646973706c61792d7761726e696e67_display_warning_0+0xa19)[0x7f6a20c3c389] ./result/bin/emacs-29.1(funcall_subr+0xf5)[0x5e51ae] ./result/bin/emacs-29.1(exec_byte_code+0x7e2)[0x619514] ./result/bin/emacs-29.1[0x5e4b69] ./result/bin/emacs-29.1[0x5e66f2] ./result/bin/emacs-29.1(funcall_general+0x1c4)[0x5e6ae7] ./result/bin/emacs-29.1(Ffuncall+0xc2)[0x5e3b5c] ./result/bin/emacs-29.1(Fapply+0x2a4)[0x5e3ef4] ./result/bin/emacs-29.1(apply1+0x3c)[0x5e3f62] ./result/bin/emacs-29.1[0x625836] ./result/bin/emacs-29.1(internal_condition_case_1+0x63)[0x5e2b68] ./result/bin/emacs-29.1[0x6260f5] ./result/bin/emacs-29.1[0x62966a] ... Segmentation fault (core dumped)
The above output doesn't give that much information about the cause or possible location of the error.
Debug
A next step is to debug the current binary with gdb
and try to collect more
insights on the situation. On NixOS
segmentation faults trigger a coredump
which
is handled by coredumpctl
.
First we list the last coredump
:
coredumpctl list -1 --json=pretty
And we get the following output:
[
{
"time" : 1702648768467115,
"pid" : 1917731,
"uid" : 1002,
"gid" : 100,
"sig" : 11,
"corefile" : "present",
"exe" : "/nix/store/kshwj88iz6xkhsyhkw745x6ivghfx44d-emacs-gtk3-29.1/bin/.emacs-29.1-wrapped",
"size" : 63850863
}
]
With this we have enough information to debug out current coredump
, for this gdb
is required to be installed.
coredumpctl debug /nix/store/kshwj88iz6xkhsyhkw745x6ivghfx44d-emacs-gtk3-29.1/bin/.emacs-29.1-wrapped
After this an gdb
session is launched and a prompt will be presented, the
backtrace of execution can be explored with bt
.
(gdb) bt #0 0x00007f4e75f4cd7c in __pthread_kill_implementation () from /nix/store/qn3ggz5sf3hkjs2c797xf7nan3amdxmp-glibc-2.38-27/lib/libc.so.6 #1 0x00007f4e75efd9c6 in raise () from /nix/store/qn3ggz5sf3hkjs2c797xf7nan3amdxmp-glibc-2.38-27/lib/libc.so.6 #2 0x000000000046f5c9 in terminate_due_to_signal () #3 0x000000000046fafd in handle_fatal_signal () #4 0x00000000005c9938 in deliver_thread_signal.constprop () #5 0x00000000005c99bc in handle_sigsegv () #6 <signal handler called> #7 0x0000000000496749 in normal_char_ascent_descent () #8 0x00000000004af1c9 in handle_single_display_spec () #9 0x00000000004aff01 in handle_display_spec () #10 0x00000000004b0980 in handle_display_prop () #11 0x00000000004aab96 in handle_stop () #12 0x00000000004ac1ec in next_element_from_string () #13 0x00000000004b25c4 in get_next_display_element () #14 0x00000000004c32e8 in display_string () #15 0x00000000004c3d6d in display_mode_element () #16 0x00000000004c39ed in display_mode_element () #17 0x00000000004c535c in display_mode_element () #18 0x00000000004c535c in display_mode_element () #19 0x00000000004c39ed in display_mode_element () #20 0x00000000004c535c in display_mode_element () #21 0x00000000004c6f20 in display_mode_line () #22 0x00000000004c9295 in display_mode_lines () #23 0x00000000004c94cb in redisplay_mode_lines () #24 0x00000000004c9a4b in echo_area_display () #25 0x00000000004cbd39 in message3_nolog () #26 0x00000000004cc020 in message3 () #27 0x0000000000632755 in Fmessage () #28 0x000000000063a731 in Ffuncall () #29 0x000000000063e4f2 in eval_sub () #30 0x000000000063e91d in Fprogn () #31 0x000000000063eedd in funcall_lambda () #32 0x000000000063a731 in Ffuncall ()
Dropping the frame for the first one which seems related to Emacs code will give
#7 0x0000000000496749 in normal_char_ascent_descent ()
And then disassembling the function:
(gdb) frame 7 #7 0x0000000000496749 in normal_char_ascent_descent () (gdb) disas Dump of assembler code for function normal_char_ascent_descent: 0x0000000000496730 <+0>: push %r12 0x0000000000496732 <+2>: mov %rdx,%r12 0x0000000000496735 <+5>: push %rbp 0x0000000000496736 <+6>: push %rbx 0x0000000000496737 <+7>: sub $0x10,%rsp 0x000000000049673b <+11>: mov %fs:0x28,%rax 0x0000000000496744 <+20>: mov %rax,0x8(%rsp) => 0x0000000000496749 <+25>: mov 0xa8(%rdi),%eax 0x000000000049674f <+31>: mov %eax,(%rdx) 0x0000000000496751 <+33>: mov 0xac(%rdi),%eax 0x0000000000496757 <+39>: mov %eax,(%rcx) 0x0000000000496759 <+41>: mov 0x98(%rdi),%edx 0x000000000049675f <+47>: test %edx,%edx 0x0000000000496761 <+49>: jle 0x4967d0 <normal_char_ascent_descent+160> 0x0000000000496763 <+51>: add 0xa8(%rdi),%eax 0x0000000000496769 <+57>: lea (%rdx,%rdx,2),%edx 0x000000000049676c <+60>: mov %rdi,%rbx 0x000000000049676f <+63>: cmp %edx,%eax 0x0000000000496771 <+65>: jle 0x4967d0 <normal_char_ascent_descent+160> 0x0000000000496773 <+67>: cmp $0xffffffff,%esi 0x0000000000496776 <+70>: mov $0x7b,%eax 0x000000000049677b <+75>: mov %rcx,%rbp 0x000000000049677e <+78>: cmove %eax,%esi 0x0000000000496781 <+81>: mov 0xd0(%rdi),%rax 0x0000000000496788 <+88>: call *0x60(%rax) 0x000000000049678b <+91>: cmp $0xffffffff,%eax 0x000000000049678e <+94>: je 0x4967d0 <normal_char_ascent_descent+160>
The execution point which triggered the error is
0x0000000000496744 <+20>: mov %rax,0x8(%rsp) => 0x0000000000496749 <+25>: mov 0xa8(%rdi),%eax
And exploring the registers we get:
(gdb) info registers rax 0x969a045504f3f00 678249438456528640 rbx 0x42523f0 69542896 rcx 0x7ffd00402c24 140724607659044 rdx 0x7ffd00402c20 140724607659040 rsi 0xffffffff 4294967295 rdi 0x0 0 rbp 0x7ffd00403570 0x7ffd00403570 rsp 0x7ffd00402b90 0x7ffd00402b90 r8 0x7ffd004036d8 140724607661784 r9 0x0 0 r10 0x1 1 r11 0x0 0 r12 0x7ffd00402c20 140724607659040 r13 0x0 0 r14 0x0 0 r15 0x4258054 69566548 rip 0x496749 0x496749 <normal_char_ascent_descent+25> eflags 0x246 [ PF ZF IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0
The current assumption is that rdi
is a null pointer, but without proper context
is hard to determine if this is the actual cause of just the consequence of
another situation triggered somewhere else.
Exploring the function signature from xdisp.c
:
static void
normal_char_ascent_descent (struct font *font, int c, int *ascent, int *descent)
Considering the assembly code above, the error is triggered by the first statement of the function, which is:
normal_char_ascent_descent (struct font *font, int c, int *ascent, int *descent)
{
*ascent = FONT_BASE (font);
*descent = FONT_DESCENT (font);
FONT_BASE
seems like a function call at the source code, but in the assembly
seems like a direct access to a struct
field, this suspicion is solved since it
is defined as a macro at font.h
:
#define FONT_BASE(f) ((f)->ascent)
Download and build Emacs
My approach to gather more information is to download the source code and setup the development environment on my local machine so I can step by step debug the code and determine the root cause of the problem. I'm using NixOS so to determine which version of Emacs I'm using is just a simple look at the source code.
Download the right version
The current version of Emacs that I'm using is defined by the following nix
code:
emacs29 = import ./make-emacs.nix (mkArgs {
pname = "emacs";
version = "29.1";
variant = "mainline";
rev = "29.1";
hash = "sha256-3HDCwtOKvkXwSULf3W7YgTz4GV8zvYnh2RrL28qzGKg=";
});
This version can be downloaded directly from GNU.org
with:
wget https://ftp.gnu.org/gnu/emacs/emacs-29.1.tar.xz
wget https://ftp.gnu.org/gnu/emacs/emacs-29.1.tar.xz.sig
Validate the signatures
Signature validation is an optional step but considered necessary for a full and validated workflow. First we start by downloading the key used to sign Emacs:
gpg --keyserver keyserver.ubuntu.com --recv-keys 17E90D521672C04631B1183EE78DAE0F3115E06B
gpg: key E78DAE0F3115E06B: public key "Eli Zaretskii <eliz@gnu.org>" imported gpg: Total number processed: 1 gpg: imported: 1
Next two steps are to trust and sign the key so it can properly be used on the signature validation.
gpg --edit-key 17E90D521672C04631B1183EE78DAE0F3115E06B
gpg (GnuPG) 2.4.1; Copyright (C) 2023 g10 Code GmbH This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. pub rsa4096/E78DAE0F3115E06B created: 2022-03-09 expires: never usage: SC trust: unknown validity: unknown sub rsa4096/98D2EE6D730F2472 created: 2022-03-09 expires: never usage: E [ unknown] (1). Eli Zaretskii <eliz@gnu.org> gpg> trust pub rsa4096/E78DAE0F3115E06B created: 2022-03-09 expires: never usage: SC trust: unknown validity: unknown sub rsa4096/98D2EE6D730F2472 created: 2022-03-09 expires: never usage: E [ unknown] (1). Eli Zaretskii <eliz@gnu.org> Please decide how far you trust this user to correctly verify other users' keys (by looking at passports, checking fingerprints from different sources, etc.) 1 = I don't know or won't say 2 = I do NOT trust 3 = I trust marginally 4 = I trust fully 5 = I trust ultimately m = back to the main menu Your decision? 4 pub rsa4096/E78DAE0F3115E06B created: 2022-03-09 expires: never usage: SC trust: full validity: unknown sub rsa4096/98D2EE6D730F2472 created: 2022-03-09 expires: never usage: E [ unknown] (1). Eli Zaretskii <eliz@gnu.org> Please note that the shown key validity is not necessarily correct unless you restart the program.
Sign the key with:
gpg --lsign-key "17E9 0D52 1672 C046 31B1 183E E78D AE0F 3115 E06B"
pub rsa4096/E78DAE0F3115E06B created: 2022-03-09 expires: never usage: SC trust: full validity: unknown sub rsa4096/98D2EE6D730F2472 created: 2022-03-09 expires: never usage: E [ unknown] (1). Eli Zaretskii <eliz@gnu.org> pub rsa4096/E78DAE0F3115E06B created: 2022-03-09 expires: never usage: SC trust: full validity: unknown Primary key fingerprint: 17E9 0D52 1672 C046 31B1 183E E78D AE0F 3115 E06B Eli Zaretskii <eliz@gnu.org> Are you sure that you want to sign this key with your key "Rafael <rafael@...>" (317B6999F8FB5701) The signature will be marked as non-exportable. Really sign? (y/N) y
And finally verify the downloaded binaries with:
gpg --verify emacs-29.1.tar.xz.sig emacs-29.1.tar.xz
gpg: Signature made Sun 30 Jul 2023 08:49:54 AM CEST gpg: using RSA key 17E90D521672C04631B1183EE78DAE0F3115E06B gpg: checking the trustdb gpg: marginals needed: 3 completes needed: 1 trust model: pgp gpg: depth: 0 valid: 4 signed: 1 trust: 0-, 0q, 0n, 0m, 0f, 4u gpg: depth: 1 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 1f, 0u gpg: next trustdb check due at 2024-08-10 gpg: Good signature from "Eli Zaretskii <eliz@gnu.org>" [full]
Build
The first step to build Emacs is to extract the downloaded archive
xz -d emacs-29.1.tar.xz
tar -xvf emacs-29.1.tar
The source code will be available on the respective extracted directory. If you
are using nix
, you can use directly the development packages from the
derivation:
nix develop nixpkgs#emacs29-gtk3
Build an non-optimized version for debugging:
CFLAGS='-g3 -O0' ./configure
checking for xcrun... no checking for GNU Make... make checking build system type... x86_64-pc-linux-gnu checking host system type... x86_64-pc-linux-gnu checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... no checking for suffix of object files... o checking whether the compiler supports GNU C... yes checking whether gcc accepts -g... yes checking for gcc option to enable C11 features... none needed checking whether the compiler is clang... no checking for compiler option needed when checking for declarations... none checking whether gcc and cc understand -c and -o together... yes checking for stdio.h... yes checking for stdlib.h... yes checking for string.h... yes checking for inttypes.h... yes checking for stdint.h... yes checking for strings.h... yes checking for sys/stat.h... yes
And compile it with
make
After this the binaries will be available inside the directory src
.
Debug
With the new built binary just run gdb
and let's set a breakpoint on the
function which we found in our previous debugging iteration.
gdb ./src/emacs
Now we can set breakpoints using the code as reference, such as:
b xdisp.c:29826
And finally execute it with the r
command. As is possible to see by the image
below, is much more clear the actual status of the debugging session.
The parameters are shown along with the function calls, so is possible to
validate our first assumption that the first argument was a null pointer. Also now the backtrace on gdb
will give us more useful information:
(gdb) bt #0 0x00000000004a3532 in normal_char_ascent_descent (font=0x0, c=-1, ascent=0x7ffffffed370, descent=0x7ffffffed374) at xdisp.c:29826 #1 0x00000000004a3667 in normal_char_height (font=0x0, c=-1) at xdisp.c:29862 #2 0x000000000045c29d in handle_single_display_spec (it=0x7ffffffee2a0, spec=0x4085763, object=0x40806c4, overlay=0x0, position=0x7ffffffee408, bufpos=0, display_replaced=0, frame_window_p=true, enable_eval_p=true) at xdisp.c:5971 #3 0x000000000045b575 in handle_display_spec (it=0x7ffffffee2a0, spec=0x4085763, object=0x40806c4, overlay=0x0, position=0x7ffffffee408, bufpos=0, frame_window_p=true) at xdisp.c:5719 #4 0x000000000045b0d4 in handle_display_prop (it=0x7ffffffee2a0) at xdisp.c:5627 #5 0x000000000045744e in handle_stop (it=0x7ffffffee2a0) at xdisp.c:4134 #6 0x0000000000464c8c in next_element_from_string (it=0x7ffffffee2a0) at xdisp.c:9103 #7 0x000000000046207f in get_next_display_element (it=0x7ffffffee2a0) at xdisp.c:8066 #8 0x000000000049faca in display_string (string=0x0, lisp_string=0x40806c4, face_string=0x0, face_string_pos=0, start=0, it=0x7ffffffee2a0, field_width=0, precision=3, max_x=656, multibyte=1) at xdisp.c:28661 #9 0x000000000049c4dd in display_mode_element (it=0x7ffffffee2a0, depth=6, field_width=0, precision=-2, elt=0x40806c4, props=0x0, risky=false) at xdisp.c:27224 #10 0x000000000049cb5e in display_mode_element (it=0x7ffffffee2a0, depth=5, field_width=0, precision=-2, elt=0x3f4f4e3, props=0x0, risky=false) at xdisp.c:27395 #11 0x000000000049ce8c in display_mode_element (it=0x7ffffffee2a0, depth=4, field_width=0, precision=0, elt=0x3f4f4f3, props=0x0, risky=false) at xdisp.c:27472 #12 0x000000000049ce8c in display_mode_element (it=0x7ffffffee2a0, depth=3, field_width=0, precision=0, elt=0x3f65383, props=0x0, risky=false) at xdisp.c:27472 #13 0x000000000049cb5e in display_mode_element (it=0x7ffffffee2a0, depth=2, field_width=0, precision=0, elt=0x3f67843, props=0x0, risky=false) at xdisp.c:27395 #14 0x000000000049ce8c in display_mode_element (it=0x7ffffffee2a0, depth=1, field_width=0, precision=0, elt=0x3f67853, props=0x0, risky=false) at xdisp.c:27472 #15 0x000000000049b665 in display_mode_line (w=0x142a1d8, face_id=MODE_LINE_ACTIVE_FACE_ID, format=0x3f67863) at xdisp.c:26898 #16 0x000000000049b346 in display_mode_lines (w=0x142a1d8) at xdisp.c:26811 #17 0x000000000049b04e in redisplay_mode_lines (window=0x142a1dd, force=false) at xdisp.c:26746 #18 0x0000000000472a0f in echo_area_display (update_frame_p=true) at xdisp.c:13194
The above output was trimmed to the relevant elements only, as we may consider
the function call chain echo_area_display -> display_mode_line ->
handle_single_display_spec
.
On the execution call, the first member which seems to retrieve the font to pass
it forward is xdisp.handle_single_display_spec
. To examine it, we drop to the
second execution frame with frame 2
.
We can print the full details of the face
structure with:
(gdb) p *((struct face*) face) $13 = {lface = {0x6d50, 0x395ddd4, 0x142aed4, 0xc420, 0x18a, 0x7ffff38576a0, 0xc420, 0x0, 0x0, 0x32a95c4, 0x3644fc4, 0x0, 0x0, 0x0, 0x0, 0x408087d, 0x0, 0x1439204, 0x10ef0, 0x0}, id = 22, gc = 0x0, stipple = 0, foreground = 4288200293, background = 4280032804, underline_color = 0, overline_color = 0, strike_through_color = 0, box_color = 0, font = 0x0, fontset = -1, box_vertical_line_width = 0, box_horizontal_line_width = 0, underline_pixels_above_descent_line = 0, box = FACE_NO_BOX, underline = FACE_NO_UNDERLINE, use_box_color_for_shadows_p = false, overline_p = false, strike_through_p = false, foreground_defaulted_p = false, background_defaulted_p = false, underline_defaulted_p = false, overline_color_defaulted_p = false, strike_through_color_defaulted_p = false, box_color_defaulted_p = false, underline_at_descent_line_p = false, tty_bold_p = false, tty_italic_p = false, tty_underline_p = false, tty_reverse_p = false, tty_strike_through_p = false, colors_copied_bitwise_p = false, overstrike = false, hash = 35184229174591, next = 0x0, prev = 0x0, ascii_face = 0x407d8a0, extra = 0x0}
The code below is when the face_id
fails to be converted from an id
to a face
structure which contains a valid font
.
#ifdef HAVE_WINDOW_SYSTEM
value = XCAR (XCDR (spec));
if (NUMBERP (value))
{
struct face *face = FACE_FROM_ID (it->f, it->face_id);
it->voffset = - (XFLOATINT (value)
* (normal_char_height (face->font, -1)));
}
#endif /* HAVE_WINDOW_SYSTEM */
The retrieval is defined at frame.h:
/* Return a non-null pointer to the cached face with ID on frame F. */
INLINE struct face *
FACE_FROM_ID (struct frame *f, int id)
{
eassert (0 <= id && id < FRAME_FACE_CACHE (f)->used);
return FRAME_FACE_CACHE (f)->faces_by_id[id];
}
The FRAME_FACE_CACHE
macro is defined at frame.h as:
#define FRAME_FACE_CACHE(F) (F)->face_cache
Back to gdb
a small investigation in the font cache gives:
(gdb) print it->face_id $16 = 22 (gdb) print it->f->face_cache $23 = (struct face_cache *) 0x1434ec0 (gdb) print it->f->face_cache $23 = (struct face_cache *) 0x1434ec0 (gdb) print it->face_id $24 = 22 (gdb) print it->f->face_cache[22] $26 = {buckets = 0x7ffff72cf064 <ft_glyph_private_key>, f = 0x7ffff726d7c0 <_cairo_ft_glyph_fini>, faces_by_id = 0x1, size = 49, used = 21490624, menu_face_changed_p = false}
For further debugging setting a conditional breakpoint would be useful, this can be done with:
(gdb) info breakpoints Num Type Disp Enb Address What 2 breakpoint keep y 0x00000000004a352e in normal_char_ascent_descent at xdisp.c:29826 breakpoint already hit 12 times 3 breakpoint keep y 0x000000000045c29d xdisp.c:5971 4 breakpoint keep y 0x000000000045c248 in handle_single_display_spec at xdisp.c:5969 5 breakpoint keep y 0x000000000045c29d xdisp.c:5971
Then to set the breakpoint based on the face_id
.
(gdb) condition 4 it->face_id == 22
Conclusion
After some time of debugging my conclusion was that the cause was a missing font in the frame cache which is a glyph font (nerd-icons) used to render the modeline. Since Doom Emacs uses doom-modeline, I continued my investigation towards that direction.
Solution
The simplest solution is to install a clean version of Doom Emacs, edit the
init.el
file to remove modeline
:
modeline ; snazzy, Atom-inspired modeline, plus API
Start Emacs and run
nerd-icons-install-fonts
Or M-x nerd-icons-install-fonts
. After this the normal configuration can be
applied to Doom Emacs and doom sync
should bring it all back to normal.
Appendix
GDB cheatsheet
C-x C-a
switch betweentui
(ncurses
) mode and console.C-x C-o
switch between the buffers.frame N
drop to the Nth frame of the stackprint
prints a variable, you can cast it to the specific type as exampleprint *((struct face *)face)
n
steps,c
continues the execution,r
runs (or restart) the programinfo breakpoints
shows the list of all breakpointsb
sets a breakpoint, then useinfo breakpoints
to get the id to set conditionscondition 4 it->face_id==20
is an example of condition
bt
shows the backtrace of the execution