Bug 26877 - gdb crashes in tui mode in a multi-process setting at process exit
Summary: gdb crashes in tui mode in a multi-process setting at process exit
Alias: None
Product: gdb
Classification: Unclassified
Component: tui (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Depends on:
Reported: 2020-11-13 13:02 UTC by Baris Aktemur
Modified: 2020-11-20 14:53 UTC (History)
1 user (show)

See Also:
Last reconfirmed:


Note You need to log in before you can comment on or make changes to this bug.
Description Baris Aktemur 2020-11-13 13:02:57 UTC
Suppose we have the following simple C program:

    1  int main() {
    2    int a = 42;
    3    return 0;
    4  }

Create a two-inferior debug session using the following commands:

    file ./simple
    b 3
    add-inferior -exec ./simple
    inferior 2
    set schedule-multiple on

After starting GDB with the script above, run "continue".  This will
resume both inferiors, and both will exit at about the same time.
GDB receives two exit event, but will show only one of them.  Switch
to the inferior whose exit event was not shown:

    (gdb) continue
    [Inferior 1 (process 21483) exited normally]
    (gdb) inferior 2
    [Switching to inferior 2 [process 21487](simple)]
    [Switching to thread 2.1 (process 21487)]
    Couldn't get registers: No such process.
    (gdb) i inferiors
      Num  Description       Connection           Executable
      1    <null>                                 /nfs/iul/disks/iul_team2/taktemur/temp/simple
    * 2    process 21487     1 (native)           /nfs/iul/disks/iul_team2/taktemur/temp/simple
    Couldn't get registers: No such process.

Note the "Couldn't get registers: No such process." messages above.
This hints that Inferior 2 is also dead, but the event hasn't been
shown to the user, yet.

If we stay in the CLI mode, we can remove inferior 1:

    (gdb) remove-inferiors 1
    Couldn't get registers: No such process.
    (gdb) info inferiors
      Num  Description       Connection           Executable
    * 2    process 21487     1 (native)           /nfs/iul/disks/iul_team2/taktemur/temp/simple
    Couldn't get registers: No such process.

But, if we were to remove inferior 1 in TUI mode, GDB crashes.
Suppose we run the commands above in tui mode:

    (gdb) tui enable
    (gdb) remove-inferiors 1
    terminate called after throwing an instance of 'gdb_exception_error'

Here is the backtrace of the crash:

(top-gdb) bt
During symbol reading: incomplete CFI data; unspecified registers (e.g., rax) at 0x7ffff59aff64
#0  0x00007ffff59aff47 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff59b18b1 in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x00007ffff6014257 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff601f606 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff601e649 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff601eff1 in __gxx_personality_v0 () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007ffff5d71eff in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1
#7  0x00007ffff5d728a6 in _Unwind_Resume () from /lib/x86_64-linux-gnu/libgcc_s.so.1
#8  0x0000555555ed7cba in get_frame_address_in_block_if_available (this_frame=0x555558298bd0, pc=0x7fffffffcbb8) at gdb/frame.c:2706
#9  0x0000555555ed6535 in select_frame (fi=0x555558298bd0) at gdb/frame.c:1926
#10 0x0000555555ed6157 in lookup_selected_frame (a_frame_id=..., frame_level=-1) at gdb/frame.c:1756
#11 0x0000555555ed63ef in get_selected_frame (message=0x0) at gdb/frame.c:1846
#12 0x0000555555c59c58 in get_current_arch () at gdb/arch-utils.c:804
#13 0x00005555563486fc in tui_get_begin_asm_address (gdbarch_p=0x7fffffffcd50, addr_p=0x7fffffffcd60) at gdb/tui/tui-disasm.c:388
#14 0x000055555636baab in tui_display_main () at gdb/tui/tui-winsource.c:54
#15 0x000055555634acc7 in tui_new_objfile_hook (objfile=0x0) at gdb/tui/tui-hooks.c:56
#16 0x0000555555c1d7dc in std::_Function_handler<void (objfile*), void (*)(objfile*)>::_M_invoke(std::_Any_data const&, objfile*&&) (__functor=..., __args#0=@0x7fffffffcde0: 0x0)
    at /usr/include/c++/7/bits/std_function.h:316
#17 0x00005555560af023 in std::function<void (objfile*)>::operator()(objfile*) const (this=0x5555582a93c8, __args#0=0x0) at /usr/include/c++/7/bits/std_function.h:706
#18 0x00005555560aea61 in gdb::observers::observable<objfile*>::notify (this=0x555557cff260 <gdb::observers::new_objfile>, args#0=0x0)
    at gdb/../gdbsupport/observable.h:106
#19 0x00005555562b5109 in clear_symtab_users (add_flags=...) at gdb/symfile.c:2864
#20 0x00005555561128b9 in program_space::~program_space (this=0x55555830a070, __in_chrg=<optimized out>) at gdb/progspace.c:153
#21 0x0000555555f7e2b0 in delete_inferior (todel=0x555557f12fb0) at gdb/inferior.c:179
#22 0x0000555555f80104 in remove_inferior_command (args=0x555557d5bf81 "1", from_tty=1) at gdb/inferior.c:711
#23 0x0000555555d45ddb in do_const_cfunc (c=0x55555830b3d0, args=0x555557d5bf81 "1", from_tty=1) at gdb/cli/cli-decode.c:95
#24 0x0000555555d499b3 in cmd_func (cmd=0x55555830b3d0, args=0x555557d5bf81 "1", from_tty=1) at gdb/cli/cli-decode.c:2181
#25 0x0000555556327745 in execute_command (p=0x555557d5bf81 "1", from_tty=1) at gdb/top.c:668
#26 0x0000555555eb04aa in command_handler (command=0x555557d5bf70 "remove-inferiors 1") at gdb/event-top.c:589
#27 0x0000555555eb0922 in command_line_handler (rl=...) at gdb/event-top.c:774
#28 0x0000555555eafb95 in gdb_rl_callback_handler (rl=0x55555838d070 "remove-inferiors 1") at gdb/event-top.c:219
#29 0x00005555564238ab in rl_callback_read_char () at readline/readline/callback.c:281
#30 0x0000555555eaf9bd in gdb_rl_callback_read_char_wrapper_noexcept () at gdb/event-top.c:177
#31 0x0000555555eafa67 in gdb_rl_callback_read_char_wrapper (client_data=0x555557d576e0) at gdb/event-top.c:194
#32 0x0000555555eb02c2 in stdin_event_handler (error=0, client_data=0x555557d576e0) at gdb/event-top.c:516
#33 0x0000555556ae75a0 in handle_file_event (file_ptr=0x55555841e560, ready_mask=1) at gdbsupport/event-loop.cc:575
#34 0x0000555556ae7b4e in gdb_wait_for_event (block=1) at gdbsupport/event-loop.cc:701
#35 0x0000555556ae6909 in gdb_do_one_event () at gdbsupport/event-loop.cc:237
#36 0x0000555556010992 in start_event_loop () at gdb/main.c:347
#37 0x0000555556010acd in captured_command_loop () at gdb/main.c:407
#38 0x000055555601233a in captured_main (data=0x7fffffffd5d0) at gdb/main.c:1234
#39 0x00005555560123a0 in gdb_main (args=0x7fffffffd5d0) at gdb/main.c:1249
#40 0x0000555555bde2a3 in main (argc=5, argv=0x7fffffffd6d8) at gdb/gdb.c:32

The problem is at:

#20 0x00005555561128b9 in program_space::~program_space (this=0x55555830a070, __in_chrg=<optimized out>) at gdb/progspace.c:153

While inside a destructor, GDB wanted to access the frame information
of Inferior 2 in a series of calls.  But because the process is dead, its
registers cannot be read.  This raises an error inside a destructor, leading
to termination of GDB.
Comment 1 Tom Tromey 2020-11-15 03:02:46 UTC
tui_new_objfile_hook should probably not do anything
when objfile is null:

diff --git a/gdb/tui/tui-hooks.c b/gdb/tui/tui-hooks.c
index 793ca0e4469..a18ddc25249 100644
--- a/gdb/tui/tui-hooks.c
+++ b/gdb/tui/tui-hooks.c
@@ -50,9 +50,9 @@
 #include "gdb_curses.h"
 static void
-tui_new_objfile_hook (struct objfile* objfile)
+tui_new_objfile_hook (struct objfile *objfile)
-  if (tui_active)
+  if (tui_active && objfile != nullptr)
     tui_display_main ();

However even with this change I see some weird behavior.
So, this is partly a TUI bug but also I think a bug in the core.
Comment 2 Baris Aktemur 2020-11-20 14:53:30 UTC
This bug has made me propose the following series of patches for GDB: