aboutsummaryrefslogtreecommitdiff
path: root/gdb/python
AgeCommit message (Collapse)AuthorFilesLines
2024-02-21Don't allow multiple request registrations in DAPTom Tromey1-0/+2
This changes the DAP code to check that a given request or capability is only registered a single time. This is just a precaution against accidentally introducing a second definition of a request somewhere.
2024-02-21[gdb/dap] Join JSON writer thread with DAP threadTom de Vries3-2/+4
The DAP interpreter runs in its own thread, and starts a few threads: - the JSON reader thread, - the JSON writer thread, and - the inferior output reader thread. As part of the DAP shutdown, both the JSON reader thread and the JSON writer thread, as well as the DAP main thread run to exit, but these exits are not ordered in any way. Wait in the main DAP thread for the exit of the JSON writer thread. This makes sure that the responses are flushed to the DAP client before DAP shutdown. An earlier version of this patch used Queue.task_done() to accomplish the same, but that didn't guarantee writing the "<thread name>: terminating" log entry from thread_wrapper before DAP shutdown. Tested on aarch64-linux. Approved-By: Tom Tromey <tom@tromey.com> PR dap/31380 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31380
2024-02-21[gdb/dap] Make dap log printing thread-safeTom de Vries1-12/+16
I read that printing from different python threads is thread-unsafe, and I noticed that the dap log printing is used from different threads, but doesn't take care to make the printing thread-safe. Fix this by using a lock to access LoggingParam.log_file. Tested on aarch64-linux. Approved-By: Tom Tromey <tom@tromey.com>
2024-02-21[gdb/dap] Flush after printing in log_stackTom de Vries1-0/+1
I noticed that function log flushes the dap log file after printing, but that function log_stack doesn't. Fix this by also flushing the dap log file in log_stack. Tested on aarch64-linux. Approved-By: Tom Tromey <tom@tromey.com>
2024-02-20gdb: pass frames as `const frame_info_ptr &`Simon Marchi7-17/+17
We currently pass frames to function by value, as `frame_info_ptr`. This is somewhat expensive: - the size of `frame_info_ptr` is 64 bytes, which is a bit big to pass by value - the constructors and destructor link/unlink the object in the global `frame_info_ptr::frame_list` list. This is an `intrusive_list`, so it's not so bad: it's just assigning a few points, there's no memory allocation as if it was `std::list`, but still it's useless to do that over and over. As suggested by Tom Tromey, change many function signatures to accept `const frame_info_ptr &` instead of `frame_info_ptr`. Some functions reassign their `frame_info_ptr` parameter, like: void the_func (frame_info_ptr frame) { for (; frame != nullptr; frame = get_prev_frame (frame)) { ... } } I wondered what to do about them, do I leave them as-is or change them (and need to introduce a separate local variable that can be re-assigned). I opted for the later for consistency. It might not be clear why some functions take `const frame_info_ptr &` while others take `frame_info_ptr`. Also, if a function took a `frame_info_ptr` because it did re-assign its parameter, I doubt that we would think to change it to `const frame_info_ptr &` should the implementation change such that it doesn't need to take `frame_info_ptr` anymore. It seems better to have a simple rule and apply it everywhere. Change-Id: I59d10addef687d157f82ccf4d54f5dde9a963fd0 Approved-By: Andrew Burgess <aburgess@redhat.com>
2024-02-19gdb, python: selectively omit enabling stdin in gdb.executeTankut Baris Aktemur1-0/+29
From the Python API, we can execute GDB commands via gdb.execute. If the command gives an exception, however, we need to recover the GDB prompt and enable stdin, because the exception does not reach top-level GDB or normal_stop. This was done in commit commit 1ba1ac88011703abcd0271e4f5d00927dc69a09a Author: Andrew Burgess <andrew.burgess@embecosm.com> Date: Tue Nov 19 11:17:20 2019 +0000 gdb: Enable stdin on exception in execute_gdb_command with the following code: catch (const gdb_exception &except) { /* If an exception occurred then we won't hit normal_stop (), or have an exception reach the top level of the event loop, which are the two usual places in which stdin would be re-enabled. So, before we convert the exception and continue back in Python, we should re-enable stdin here. */ async_enable_stdin (); GDB_PY_HANDLE_EXCEPTION (except); } In this patch, we explain what happens when we run a GDB command in the context of a synchronous command, e.g. via Python observer notifications. As an example, suppose we have the following objfile event listener, specified in a file named file.py: ~~~ import gdb class MyListener: def __init__(self): gdb.events.new_objfile.connect(self.handle_new_objfile_event) self.processed_objfile = False def handle_new_objfile_event(self, event): if self.processed_objfile: return print("loading " + event.new_objfile.filename) self.processed_objfile = True gdb.execute('add-inferior -no-connection') gdb.execute('inferior 2') gdb.execute('target remote | gdbserver - /tmp/a.out') gdb.execute('inferior 1') the_listener = MyListener() ~~~ Using this Python file, we see the behavior below: $ gdb -q -ex "source file.py" -ex "run" --args a.out Reading symbols from a.out... Starting program: /tmp/a.out loading /lib64/ld-linux-x86-64.so.2 [New inferior 2] Added inferior 2 [Switching to inferior 2 [<null>] (<noexec>)] stdin/stdout redirected Process /tmp/a.out created; pid = 3075406 Remote debugging using stdio Reading /tmp/a.out from remote target... ... [Switching to inferior 1 [process 3075400] (/tmp/a.out)] [Switching to thread 1.1 (process 3075400)] #0 0x00007ffff7fe3290 in ?? () from /lib64/ld-linux-x86-64.so.2 (gdb) [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [Inferior 1 (process 3075400) exited normally] Note how the GDB prompt comes in-between the debugger output. We have this obscure behavior, because the executed command, "target remote", triggers an invocation of `normal_stop` that enables stdin. After that, however, the Python notification context completes and GDB continues with its normal flow of executing the 'run' command. This can be seen in the call stack below: (top-gdb) bt #0 async_enable_stdin () at src/gdb/event-top.c:523 #1 0x00005555561c3acd in normal_stop () at src/gdb/infrun.c:9432 #2 0x00005555561b328e in start_remote (from_tty=0) at src/gdb/infrun.c:3801 #3 0x0000555556441224 in remote_target::start_remote_1 (this=0x5555587882e0, from_tty=0, extended_p=0) at src/gdb/remote.c:5225 #4 0x000055555644166c in remote_target::start_remote (this=0x5555587882e0, from_tty=0, extended_p=0) at src/gdb/remote.c:5316 #5 0x00005555564430cf in remote_target::open_1 (name=0x55555878525e "| gdbserver - /tmp/a.out", from_tty=0, extended_p=0) at src/gdb/remote.c:6175 #6 0x0000555556441707 in remote_target::open (name=0x55555878525e "| gdbserver - /tmp/a.out", from_tty=0) at src/gdb/remote.c:5338 #7 0x00005555565ea63f in open_target (args=0x55555878525e "| gdbserver - /tmp/a.out", from_tty=0, command=0x555558589280) at src/gdb/target.c:824 #8 0x0000555555f0d89a in cmd_func (cmd=0x555558589280, args=0x55555878525e "| gdbserver - /tmp/a.out", from_tty=0) at src/gdb/cli/cli-decode.c:2735 #9 0x000055555661fb42 in execute_command (p=0x55555878529e "t", from_tty=0) at src/gdb/top.c:575 #10 0x0000555555f1a506 in execute_control_command_1 (cmd=0x555558756f00, from_tty=0) at src/gdb/cli/cli-script.c:529 #11 0x0000555555f1abea in execute_control_command (cmd=0x555558756f00, from_tty=0) at src/gdb/cli/cli-script.c:701 #12 0x0000555555f19fc7 in execute_control_commands (cmdlines=0x555558756f00, from_tty=0) at src/gdb/cli/cli-script.c:411 #13 0x0000555556400d91 in execute_gdb_command (self=0x7ffff43b5d00, args=0x7ffff440ab60, kw=0x0) at src/gdb/python/python.c:700 #14 0x00007ffff7a96023 in ?? () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #15 0x00007ffff7a4dadc in _PyObject_MakeTpCall () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #16 0x00007ffff79e9a1c in _PyEval_EvalFrameDefault () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #17 0x00007ffff7b303af in ?? () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #18 0x00007ffff7a50358 in ?? () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #19 0x00007ffff7a4f3f4 in ?? () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #20 0x00007ffff7a4f883 in PyObject_CallFunctionObjArgs () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #21 0x00005555563a9758 in evpy_emit_event (event=0x7ffff42b5430, registry=0x7ffff42b4690) at src/gdb/python/py-event.c:104 #22 0x00005555563cb874 in emit_new_objfile_event (objfile=0x555558761700) at src/gdb/python/py-newobjfileevent.c:52 #23 0x00005555563b53bc in python_new_objfile (objfile=0x555558761700) at src/gdb/python/py-inferior.c:195 #24 0x0000555555d6dff0 in std::__invoke_impl<void, void (*&)(objfile*), objfile*> (__f=@0x5555585b5860: 0x5555563b5360 <python_new_objfile(objfile*)>) at /usr/include/c++/11/bits/invoke.h:61 #25 0x0000555555d6be18 in std::__invoke_r<void, void (*&)(objfile*), objfile*> (__fn=@0x5555585b5860: 0x5555563b5360 <python_new_objfile(objfile*)>) at /usr/include/c++/11/bits/invoke.h:111 #26 0x0000555555d69661 in std::_Function_handler<void (objfile*), void (*)(objfile*)>::_M_invoke(std::_Any_data const&, objfile*&&) (__functor=..., __args#0=@0x7fffffffd080: 0x555558761700) at /usr/include/c++/11/bits/std_function.h:290 #27 0x0000555556314caf in std::function<void (objfile*)>::operator()(objfile*) const (this=0x5555585b5860, __args#0=0x555558761700) at /usr/include/c++/11/bits/std_function.h:590 #28 0x000055555631444e in gdb::observers::observable<objfile*>::notify (this=0x55555836eea0 <gdb::observers::new_objfile>, args#0=0x555558761700) at src/gdb/../gdbsupport/observable.h:166 #29 0x0000555556599b3f in symbol_file_add_with_addrs (abfd=..., name=0x55555875d310 "/lib64/ld-linux-x86-64.so.2", add_flags=..., addrs=0x7fffffffd2f0, flags=..., parent=0x0) at src/gdb/symfile.c:1125 #30 0x0000555556599ca4 in symbol_file_add_from_bfd (abfd=..., name=0x55555875d310 "/lib64/ld-linux-x86-64.so.2", add_flags=..., addrs=0x7fffffffd2f0, flags=..., parent=0x0) at src/gdb/symfile.c:1160 #31 0x0000555556546371 in solib_read_symbols (so=..., flags=...) at src/gdb/solib.c:692 #32 0x0000555556546f0f in solib_add (pattern=0x0, from_tty=0, readsyms=1) at src/gdb/solib.c:1015 #33 0x0000555556539891 in enable_break (info=0x55555874e180, from_tty=0) at src/gdb/solib-svr4.c:2416 #34 0x000055555653b305 in svr4_solib_create_inferior_hook (from_tty=0) at src/gdb/solib-svr4.c:3058 #35 0x0000555556547cee in solib_create_inferior_hook (from_tty=0) at src/gdb/solib.c:1217 #36 0x0000555556196f6a in post_create_inferior (from_tty=0) at src/gdb/infcmd.c:275 #37 0x0000555556197670 in run_command_1 (args=0x0, from_tty=1, run_how=RUN_NORMAL) at src/gdb/infcmd.c:486 #38 0x000055555619783f in run_command (args=0x0, from_tty=1) at src/gdb/infcmd.c:512 #39 0x0000555555f0798d in do_simple_func (args=0x0, from_tty=1, c=0x555558567510) at src/gdb/cli/cli-decode.c:95 #40 0x0000555555f0d89a in cmd_func (cmd=0x555558567510, args=0x0, from_tty=1) at src/gdb/cli/cli-decode.c:2735 #41 0x000055555661fb42 in execute_command (p=0x7fffffffe2c4 "", from_tty=1) at src/gdb/top.c:575 #42 0x000055555626303b in catch_command_errors (command=0x55555661f4ab <execute_command(char const*, int)>, arg=0x7fffffffe2c1 "run", from_tty=1, do_bp_actions=true) at src/gdb/main.c:513 #43 0x000055555626328a in execute_cmdargs (cmdarg_vec=0x7fffffffdaf0, file_type=CMDARG_FILE, cmd_type=CMDARG_COMMAND, ret=0x7fffffffda3c) at src/gdb/main.c:612 #44 0x0000555556264849 in captured_main_1 (context=0x7fffffffdd40) at src/gdb/main.c:1293 #45 0x0000555556264a7f in captured_main (data=0x7fffffffdd40) at src/gdb/main.c:1314 #46 0x0000555556264b2e in gdb_main (args=0x7fffffffdd40) at src/gdb/main.c:1343 #47 0x0000555555ceccab in main (argc=9, argv=0x7fffffffde78) at src/gdb/gdb.c:39 (top-gdb) The use of the "target remote" command here is just an example. In principle, we would reproduce the problem with any command that triggers an invocation of `normal_stop`. To omit enabling the stdin in `normal_stop`, we would have to check the context we are in. Since we cannot do that, we add a new field to `struct ui` to track whether the prompt was already blocked, and set the tracker flag in the Python context before executing a GDB command. After applying this patch, the output becomes ... Reading symbols from a.out... Starting program: /tmp/a.out loading /lib64/ld-linux-x86-64.so.2 [New inferior 2] Added inferior 2 [Switching to inferior 2 [<null>] (<noexec>)] stdin/stdout redirected Process /tmp/a.out created; pid = 3032261 Remote debugging using stdio Reading /tmp/a.out from remote target... ... [Switching to inferior 1 [process 3032255] (/tmp/a.out)] [Switching to thread 1.1 (process 3032255)] #0 0x00007ffff7fe3290 in ?? () from /lib64/ld-linux-x86-64.so.2 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [Inferior 1 (process 3032255) exited normally] (gdb) Let's now consider a secondary scenario, where the command executed from the Python raises an error. As an example, suppose we have the Python file below: def handle_new_objfile_event(self, event): ... print("loading " + event.new_objfile.filename) self.processed_objfile = True gdb.execute('print a') The executed command, "print a", gives an error because "a" is not defined. Without this patch, we see the behavior below, where the prompt is again placed incorrectly: ... Reading symbols from /tmp/a.out... Starting program: /tmp/a.out loading /lib64/ld-linux-x86-64.so.2 Python Exception <class 'gdb.error'>: No symbol "a" in current context. (gdb) [Inferior 1 (process 3980401) exited normally] This time, `async_enable_stdin` is called from the 'catch' block in `execute_gdb_command`: (top-gdb) bt #0 async_enable_stdin () at src/gdb/event-top.c:523 #1 0x0000555556400f0a in execute_gdb_command (self=0x7ffff43b5d00, args=0x7ffff440ab60, kw=0x0) at src/gdb/python/python.c:713 #2 0x00007ffff7a96023 in ?? () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #3 0x00007ffff7a4dadc in _PyObject_MakeTpCall () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #4 0x00007ffff79e9a1c in _PyEval_EvalFrameDefault () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #5 0x00007ffff7b303af in ?? () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #6 0x00007ffff7a50358 in ?? () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #7 0x00007ffff7a4f3f4 in ?? () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #8 0x00007ffff7a4f883 in PyObject_CallFunctionObjArgs () from /lib/x86_64-linux-gnu/libpython3.10.so.1.0 #9 0x00005555563a9758 in evpy_emit_event (event=0x7ffff42b5430, registry=0x7ffff42b4690) at src/gdb/python/py-event.c:104 #10 0x00005555563cb874 in emit_new_objfile_event (objfile=0x555558761410) at src/gdb/python/py-newobjfileevent.c:52 #11 0x00005555563b53bc in python_new_objfile (objfile=0x555558761410) at src/gdb/python/py-inferior.c:195 #12 0x0000555555d6dff0 in std::__invoke_impl<void, void (*&)(objfile*), objfile*> (__f=@0x5555585b5860: 0x5555563b5360 <python_new_objfile(objfile*)>) at /usr/include/c++/11/bits/invoke.h:61 #13 0x0000555555d6be18 in std::__invoke_r<void, void (*&)(objfile*), objfile*> (__fn=@0x5555585b5860: 0x5555563b5360 <python_new_objfile(objfile*)>) at /usr/include/c++/11/bits/invoke.h:111 #14 0x0000555555d69661 in std::_Function_handler<void (objfile*), void (*)(objfile*)>::_M_invoke(std::_Any_data const&, objfile*&&) (__functor=..., __args#0=@0x7fffffffd080: 0x555558761410) at /usr/include/c++/11/bits/std_function.h:290 #15 0x0000555556314caf in std::function<void (objfile*)>::operator()(objfile*) const (this=0x5555585b5860, __args#0=0x555558761410) at /usr/include/c++/11/bits/std_function.h:590 #16 0x000055555631444e in gdb::observers::observable<objfile*>::notify (this=0x55555836eea0 <gdb::observers::new_objfile>, args#0=0x555558761410) at src/gdb/../gdbsupport/observable.h:166 #17 0x0000555556599b3f in symbol_file_add_with_addrs (abfd=..., name=0x55555875d020 "/lib64/ld-linux-x86-64.so.2", add_flags=..., addrs=0x7fffffffd2f0, flags=..., parent=0x0) at src/gdb/symfile.c:1125 #18 0x0000555556599ca4 in symbol_file_add_from_bfd (abfd=..., name=0x55555875d020 "/lib64/ld-linux-x86-64.so.2", add_flags=..., addrs=0x7fffffffd2f0, flags=..., parent=0x0) at src/gdb/symfile.c:1160 #19 0x0000555556546371 in solib_read_symbols (so=..., flags=...) at src/gdb/solib.c:692 #20 0x0000555556546f0f in solib_add (pattern=0x0, from_tty=0, readsyms=1) at src/gdb/solib.c:1015 #21 0x0000555556539891 in enable_break (info=0x55555874a670, from_tty=0) at src/gdb/solib-svr4.c:2416 #22 0x000055555653b305 in svr4_solib_create_inferior_hook (from_tty=0) at src/gdb/solib-svr4.c:3058 #23 0x0000555556547cee in solib_create_inferior_hook (from_tty=0) at src/gdb/solib.c:1217 #24 0x0000555556196f6a in post_create_inferior (from_tty=0) at src/gdb/infcmd.c:275 #25 0x0000555556197670 in run_command_1 (args=0x0, from_tty=1, run_how=RUN_NORMAL) at src/gdb/infcmd.c:486 #26 0x000055555619783f in run_command (args=0x0, from_tty=1) at src/gdb/infcmd.c:512 #27 0x0000555555f0798d in do_simple_func (args=0x0, from_tty=1, c=0x555558567510) at src/gdb/cli/cli-decode.c:95 #28 0x0000555555f0d89a in cmd_func (cmd=0x555558567510, args=0x0, from_tty=1) at src/gdb/cli/cli-decode.c:2735 #29 0x000055555661fb42 in execute_command (p=0x7fffffffe2c4 "", from_tty=1) at src/gdb/top.c:575 #30 0x000055555626303b in catch_command_errors (command=0x55555661f4ab <execute_command(char const*, int)>, arg=0x7fffffffe2c1 "run", from_tty=1, do_bp_actions=true) at src/gdb/main.c:513 #31 0x000055555626328a in execute_cmdargs (cmdarg_vec=0x7fffffffdaf0, file_type=CMDARG_FILE, cmd_type=CMDARG_COMMAND, ret=0x7fffffffda3c) at src/gdb/main.c:612 #32 0x0000555556264849 in captured_main_1 (context=0x7fffffffdd40) at src/gdb/main.c:1293 #33 0x0000555556264a7f in captured_main (data=0x7fffffffdd40) at src/gdb/main.c:1314 #34 0x0000555556264b2e in gdb_main (args=0x7fffffffdd40) at src/gdb/main.c:1343 #35 0x0000555555ceccab in main (argc=9, argv=0x7fffffffde78) at src/gdb/gdb.c:39 (top-gdb) Again, after we enable stdin, GDB continues with its normal flow of the 'run' command and receives the inferior's exit event, where it would have enabled stdin, if we had not done it prematurely. (top-gdb) bt #0 async_enable_stdin () at src/gdb/event-top.c:523 #1 0x00005555561c3acd in normal_stop () at src/gdb/infrun.c:9432 #2 0x00005555561b5bf1 in fetch_inferior_event () at src/gdb/infrun.c:4700 #3 0x000055555618d6a7 in inferior_event_handler (event_type=INF_REG_EVENT) at src/gdb/inf-loop.c:42 #4 0x000055555620ecdb in handle_target_event (error=0, client_data=0x0) at src/gdb/linux-nat.c:4316 #5 0x0000555556f33035 in handle_file_event (file_ptr=0x5555587024e0, ready_mask=1) at src/gdbsupport/event-loop.cc:573 #6 0x0000555556f3362f in gdb_wait_for_event (block=0) at src/gdbsupport/event-loop.cc:694 #7 0x0000555556f322cd in gdb_do_one_event (mstimeout=-1) at src/gdbsupport/event-loop.cc:217 #8 0x0000555556262df8 in start_event_loop () at src/gdb/main.c:407 #9 0x0000555556262f85 in captured_command_loop () at src/gdb/main.c:471 #10 0x0000555556264a84 in captured_main (data=0x7fffffffdd40) at src/gdb/main.c:1324 #11 0x0000555556264b2e in gdb_main (args=0x7fffffffdd40) at src/gdb/main.c:1343 #12 0x0000555555ceccab in main (argc=9, argv=0x7fffffffde78) at src/gdb/gdb.c:39 (top-gdb) The solution implemented by this patch addresses the problem. After applying the patch, the output becomes $ gdb -q -ex "source file.py" -ex "run" --args a.out Reading symbols from /tmp/a.out... Starting program: /tmp/a.out loading /lib64/ld-linux-x86-64.so.2 Python Exception <class 'gdb.error'>: No symbol "a" in current context. [Inferior 1 (process 3984511) exited normally] (gdb) Regression-tested on X86_64 Linux using the default board file (i.e. unix). Co-Authored-By: Oguzhan Karakaya <oguzhan.karakaya@intel.com> Reviewed-By: Guinevere Larsen <blarsen@redhat.com> Approved-By: Tom Tromey <tom@tromey.com>
2024-02-14[gdb/dap] Fix exit raceTom de Vries1-1/+0
When running test-case gdb.dap/eof.exp, we're likely to get a coredump due to a segfault in new_threadstate. At the point of the core dump, the gdb main thread looks like: ... (gdb) bt #0 0x0000fffee30d2280 in __pthread_kill_implementation () from /lib64/libc.so.6 #1 0x0000fffee3085800 [PAC] in raise () from /lib64/libc.so.6 #2 0x00000000007b03e8 [PAC] in handle_fatal_signal (sig=11) at gdb/event-top.c:926 #3 0x00000000007b0470 in handle_sigsegv (sig=11) at gdb/event-top.c:976 #4 <signal handler called> #5 0x0000fffee3a4db14 in new_threadstate () from /lib64/libpython3.12.so.1.0 #6 0x0000fffee3ab0548 [PAC] in PyGILState_Ensure () from /lib64/libpython3.12.so.1.0 #7 0x0000000000a6d034 [PAC] in gdbpy_gil::gdbpy_gil (this=0xffffcb279738) at gdb/python/python-internal.h:787 #8 0x0000000000ab87ac in gdbpy_event::~gdbpy_event (this=0xfffea8001ee0, __in_chrg=<optimized out>) at gdb/python/python.c:1051 #9 0x0000000000ab9460 in std::_Function_base::_Base_manager<...>::_M_destroy (__victim=...) at /usr/include/c++/13/bits/std_function.h:175 #10 0x0000000000ab92dc in std::_Function_base::_Base_manager<...>::_M_manager (__dest=..., __source=..., __op=std::__destroy_functor) at /usr/include/c++/13/bits/std_function.h:203 #11 0x0000000000ab8f14 in std::_Function_handler<...>::_M_manager(...) (...) at /usr/include/c++/13/bits/std_function.h:282 #12 0x000000000042dd9c in std::_Function_base::~_Function_base (this=0xfffea8001c10, __in_chrg=<optimized out>) at /usr/include/c++/13/bits/std_function.h:244 #13 0x000000000042e654 in std::function<void ()>::~function() (this=0xfffea8001c10, __in_chrg=<optimized out>) at /usr/include/c++/13/bits/std_function.h:334 #14 0x0000000000b68e60 in std::_Destroy<std::function<void ()> >(...) (...) at /usr/include/c++/13/bits/stl_construct.h:151 #15 0x0000000000b68cd0 in std::_Destroy_aux<false>::__destroy<...>(...) (...) at /usr/include/c++/13/bits/stl_construct.h:163 #16 0x0000000000b689d8 in std::_Destroy<...>(...) (...) at /usr/include/c++/13/bits/stl_construct.h:196 #17 0x0000000000b68414 in std::_Destroy<...>(...) (...) at /usr/include/c++/13/bits/alloc_traits.h:948 #18 std::vector<...>::~vector() (this=0x2a183c8 <runnables>) at /usr/include/c++/13/bits/stl_vector.h:732 #19 0x0000fffee3088370 in __run_exit_handlers () from /lib64/libc.so.6 #20 0x0000fffee3088450 [PAC] in exit () from /lib64/libc.so.6 #21 0x0000000000c95600 [PAC] in quit_force (exit_arg=0x0, from_tty=0) at gdb/top.c:1822 #22 0x0000000000609140 in quit_command (args=0x0, from_tty=0) at gdb/cli/cli-cmds.c:508 #23 0x0000000000c926a4 in quit_cover () at gdb/top.c:300 #24 0x00000000007b09d4 in async_disconnect (arg=0x0) at gdb/event-top.c:1230 #25 0x0000000000548acc in invoke_async_signal_handlers () at gdb/async-event.c:234 #26 0x000000000157d2d4 in gdb_do_one_event (mstimeout=-1) at gdbsupport/event-loop.cc:199 #27 0x0000000000943a84 in start_event_loop () at gdb/main.c:401 #28 0x0000000000943bfc in captured_command_loop () at gdb/main.c:465 #29 0x000000000094567c in captured_main (data=0xffffcb279d08) at gdb/main.c:1335 #30 0x0000000000945700 in gdb_main (args=0xffffcb279d08) at gdb/main.c:1354 #31 0x0000000000423ab4 in main (argc=14, argv=0xffffcb279e98) at gdb/gdb.c:39 ... The direct cause of the segfault is calling PyGILState_Ensure after calling Py_Finalize. AFAICT the problem is a race between the gdb main thread and DAP's JSON writer thread. On one side, we have the following events: - DAP's JSON reader thread reads an EOF, and lets DAP's main thread known by writing None into read_queue - DAP's main thread lets DAP's JSON writer thread known by writing None into write_queue - DAP's JSON writer thread sees the None in its queue, and calls send_gdb("quit") - a corresponding gdbpy_event is deposited in the runnables vector, to be run by the gdb main thread On the other side, we have the following events: - the gdb main thread receives a SIGHUP - the corresponding handler calls quit_force, which calls do_final_cleanups - one of the final cleanups is finalize_python, which calls Py_Finalize - quit_force calls exit, which triggers the exit handlers - one of the exit handlers is the destructor of the runnables vector - destruction of the vector triggers destruction of the remaining element - the remaining element is a gdbpy_event, and the destructor (indirectly) calls PyGILState_Ensure It's good to note that both events (EOF and SIGHUP) are caused by this line in the test-case: ... catch "close -i $gdb_spawn_id" ... where "expect close" closes the stdin and stdout file descriptors, which causes the SIGHUP to be send. So, for the system I'm running this on, the send_gdb("quit") is actually not needed. I'm not sure if we support any systems where it's actually needed. Fix this by removing the send_gdb("quit"). Tested on aarch64-linux. PR dap/31306 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31306
2024-02-12[gdb] Re-format dap/startup.py with blackTom de Vries1-2/+2
Commit 433ae2c2458 ("[gdb/dap] Catch and log exceptions in dap threads") made some changes to gdb/python/lib/gdb/dap/startup.py. Re-format it with black.
2024-02-12[gdb/dap] Catch and log exceptions in dap threadsTom de Vries1-2/+18
When running test-case gdb.dap/eof.exp, it occasionally coredumps. The thread triggering the coredump is: ... #0 0x0000ffff42bb2280 in __pthread_kill_implementation () from /lib64/libc.so.6 #1 0x0000ffff42b65800 [PAC] in raise () from /lib64/libc.so.6 #2 0x00000000007b03e8 [PAC] in handle_fatal_signal (sig=11) at gdb/event-top.c:926 #3 0x00000000007b0470 in handle_sigsegv (sig=11) at gdb/event-top.c:976 #4 <signal handler called> #5 0x0000000000606080 in cli_ui_out::do_message (this=0xffff2f7ed728, style=..., format=0xffff0c002af1 "%s", args=...) at gdb/cli-out.c:232 #6 0x0000000000ce6358 in ui_out::call_do_message (this=0xffff2f7ed728, style=..., format=0xffff0c002af1 "%s") at gdb/ui-out.c:584 #7 0x0000000000ce6610 in ui_out::vmessage (this=0xffff2f7ed728, in_style=..., format=0x16f93ea "", args=...) at gdb/ui-out.c:621 #8 0x0000000000ce3a9c in ui_file::vprintf (this=0xfffffbea1b18, ...) at gdb/ui-file.c:74 #9 0x0000000000d2b148 in gdb_vprintf (stream=0xfffffbea1b18, format=0x16f93e8 "%s", args=...) at gdb/utils.c:1898 #10 0x0000000000d2b23c in gdb_printf (stream=0xfffffbea1b18, format=0x16f93e8 "%s") at gdb/utils.c:1913 #11 0x0000000000ab5208 in gdbpy_write (self=0x33fe35d0, args=0x342ec280, kw=0x345c08b0) at gdb/python/python.c:1464 #12 0x0000ffff434acedc in cfunction_call () from /lib64/libpython3.12.so.1.0 #13 0x0000ffff4347c500 [PAC] in _PyObject_MakeTpCall () from /lib64/libpython3.12.so.1.0 #14 0x0000ffff43488b64 [PAC] in _PyEval_EvalFrameDefault () from /lib64/libpython3.12.so.1.0 #15 0x0000ffff434d8cd0 [PAC] in method_vectorcall () from /lib64/libpython3.12.so.1.0 #16 0x0000ffff434b9824 [PAC] in PyObject_CallOneArg () from /lib64/libpython3.12.so.1.0 #17 0x0000ffff43557674 [PAC] in PyFile_WriteObject () from /lib64/libpython3.12.so.1.0 #18 0x0000ffff435577a0 [PAC] in PyFile_WriteString () from /lib64/libpython3.12.so.1.0 #19 0x0000ffff43465354 [PAC] in thread_excepthook () from /lib64/libpython3.12.so.1.0 #20 0x0000ffff434ac6e0 [PAC] in cfunction_vectorcall_O () from /lib64/libpython3.12.so.1.0 #21 0x0000ffff434a32d8 [PAC] in PyObject_Vectorcall () from /lib64/libpython3.12.so.1.0 #22 0x0000ffff43488b64 [PAC] in _PyEval_EvalFrameDefault () from /lib64/libpython3.12.so.1.0 #23 0x0000ffff434d8d88 [PAC] in method_vectorcall () from /lib64/libpython3.12.so.1.0 #24 0x0000ffff435e0ef4 [PAC] in thread_run () from /lib64/libpython3.12.so.1.0 #25 0x0000ffff43591ec0 [PAC] in pythread_wrapper () from /lib64/libpython3.12.so.1.0 #26 0x0000ffff42bb0584 [PAC] in start_thread () from /lib64/libc.so.6 #27 0x0000ffff42c1fd4c [PAC] in thread_start () from /lib64/libc.so.6 ... The direct cause for the coredump seems to be that cli_ui_out::do_message is trying to write to a stream variable which does not look sound: ... (gdb) p *stream $8 = {_vptr.ui_file = 0x0, m_applied_style = {m_foreground = {m_simple = true, { m_value = 0, {m_red = 0 '\000', m_green = 0 '\000', m_blue = 0 '\000'}}}, m_background = {m_simple = 32, {m_value = 65535, {m_red = 255 '\377', m_green = 255 '\377', m_blue = 0 '\000'}}}, m_intensity = (unknown: 0x438fe710), m_reverse = 255}} ... The string that is being printed is: ... (gdb) p str $9 = "Exception in thread " ... so AFAICT this is a DAP thread running into an exception and trying to print it. If we look at the state of gdb's main thread, we have: ... #0 0x0000ffff42bac914 in __futex_abstimed_wait_cancelable64 () from /lib64/libc.so.6 #1 0x0000ffff42bafb44 [PAC] in pthread_cond_timedwait@@GLIBC_2.17 () from /lib64/libc.so.6 #2 0x0000ffff43466e9c [PAC] in take_gil () from /lib64/libpython3.12.so.1.0 #3 0x0000ffff43484fe0 [PAC] in PyEval_RestoreThread () from /lib64/libpython3.12.so.1.0 #4 0x0000000000ab8698 [PAC] in gdbpy_allow_threads::~gdbpy_allow_threads ( this=0xfffffbea1cf8, __in_chrg=<optimized out>) at gdb/python/python-internal.h:769 #5 0x0000000000ab2fec in execute_gdb_command (self=0x33fe35d0, args=0x34297b60, kw=0x34553d20) at gdb/python/python.c:681 #6 0x0000ffff434acedc in cfunction_call () from /lib64/libpython3.12.so.1.0 #7 0x0000ffff4347c500 [PAC] in _PyObject_MakeTpCall () from /lib64/libpython3.12.so.1.0 #8 0x0000ffff43488b64 [PAC] in _PyEval_EvalFrameDefault () from /lib64/libpython3.12.so.1.0 #9 0x0000ffff4353bce8 [PAC] in _PyObject_VectorcallTstate.lto_priv.3 () from /lib64/libpython3.12.so.1.0 #10 0x0000000000ab87fc [PAC] in gdbpy_event::operator() (this=0xffff14005900) at gdb/python/python.c:1061 #11 0x0000000000ab93e8 in std::__invoke_impl<void, gdbpy_event&> (__f=...) at /usr/include/c++/13/bits/invoke.h:61 #12 0x0000000000ab9204 in std::__invoke_r<void, gdbpy_event&> (__fn=...) at /usr/include/c++/13/bits/invoke.h:111 #13 0x0000000000ab8e90 in std::_Function_handler<..>::_M_invoke(...) (...) at /usr/include/c++/13/bits/std_function.h:290 #14 0x000000000062e0d0 in std::function<void ()>::operator()() const ( this=0xffff14005830) at /usr/include/c++/13/bits/std_function.h:591 #15 0x0000000000b67f14 in run_events (error=0, client_data=0x0) at gdb/run-on-main-thread.c:76 #16 0x000000000157e290 in handle_file_event (file_ptr=0x33dae3a0, ready_mask=1) at gdbsupport/event-loop.cc:573 #17 0x000000000157e760 in gdb_wait_for_event (block=1) at gdbsupport/event-loop.cc:694 #18 0x000000000157d464 in gdb_do_one_event (mstimeout=-1) at gdbsupport/event-loop.cc:264 #19 0x0000000000943a84 in start_event_loop () at gdb/main.c:401 #20 0x0000000000943bfc in captured_command_loop () at gdb/main.c:465 #21 0x000000000094567c in captured_main (data=0xfffffbea23e8) at gdb/main.c:1335 #22 0x0000000000945700 in gdb_main (args=0xfffffbea23e8) at gdb/main.c:1354 #23 0x0000000000423ab4 in main (argc=14, argv=0xfffffbea2578) at gdb/gdb.c:39 ... AFAIU, there's a race between the two threads on gdb_stderr: - the DAP thread samples the gdb_stderr value, and uses it a bit later to print to - the gdb main thread changes the gdb_stderr value forth and back, using a temporary value for string capture purposes The non-sound stream value is caused by gdb_stderr being sampled while pointing to a str_file object, and used once the str_file object is already destroyed. The error here is that the DAP thread attempts to print to gdb_stderr. Fix this by adding a thread_wrapper that: - catches all exceptions and logs them to dap.log, and - while we're at it, logs when exiting and using the thread_wrapper for each DAP thread. Tested on aarch64-linux. Approved-By: Tom Tromey <tom@tromey.com>
2024-02-12Fix DAP launch and configurationDone requestsTom Tromey2-88/+75
Co-workers at AdaCore pointed out that gdb incorrectly implements the DAP launch and configurationDone requests. It's somewhat strange to me, but the spec does in fact say that configuration requests should occur before the executable is known to gdb. This was clarified in this bug report against the spec: https://github.com/microsoft/debug-adapter-protocol/issues/452 Fixing 'launch' to start the inferior was straightforward, but this then required some changes to how breakpoints are handled. In particular, now gdb will emit the "pending" reason on a breakpoint, and will suppress breakpoint events during breakpoint setting.
2024-02-12Clean up suppress_new_breakpoint_eventTom Tromey1-1/+2
Kévin pointed out that suppress_new_breakpoint_event would do the wrong thing if it happened to be used reentrantly. While I don't think this can happen, it's also easy and clearly better to make it robust.
2024-02-12gdb: re-format Python files with black 24.1.1Simon Marchi2-2/+0
New year, new black version. Change-Id: I664601e6dd255358063e15f6d73bc5f02c8f2b9d
2024-02-10gdb/python: fix 'set python ignore-environment' white spaceAndrew Burgess1-4/+4
I noticed that the help text for set/show python ignore-environment was messed up, some lines had unwanted leading white space, like this: (gdb) help set python ignore-environment Set whether the Python interpreter should ignore environment variables. When enabled GDB's Python interpreter will ignore any Python related flags in the environment. This is equivalent to passing `-E' to a python executable. (gdb) This has been present since the ignore-environment setting was added in commit: commit edeaceda7b2f33b2c3bf78c732e67f3188e7f0b9 Date: Thu Aug 27 16:53:13 2020 +0100 gdb: startup commands to control Python extension language Fixed in this commit.
2024-02-08Allow calling of C++ methods from pythonHannes Domani1-2/+3
Currently it's not possible to call C++ methods from python. Using this example: ``` class B { static int static_func (); int arg0_func (); int arg1_func (int arg1); int arg2_func (int arg1, int arg2); }; B *b_obj = new B; ``` Trying to call B::static_func gives this error: ``` (gdb) py b_obj = gdb.parse_and_eval('b_obj') (gdb) py print(b_obj['static_func']()) Traceback (most recent call last): File "<string>", line 1, in <module> RuntimeError: Value is not callable (not TYPE_CODE_FUNC). Error while executing Python code. ``` TYPE_CODE_METHOD was simply missing as a possible type in valpy_call, now the same is possible: ``` (gdb) py b_obj = gdb.parse_and_eval('b_obj') (gdb) py print(b_obj['static_func']()) 1111 ``` Note that it's necessary to explicitely add the this pointer as the first argument in a call of non-static methods: ``` (gdb) py print(b_obj['arg0_func']()) Traceback (most recent call last): File "<string>", line 1, in <module> gdb.error: Too few arguments in function call. Error while executing Python code. (gdb) py print(b_obj['arg0_func'](b_obj)) 198 ``` Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=13326 Approved-By: Tom Tromey <tom@tromey.com>
2024-02-07Raise exception if ambiguous name is used in gdb.parameterHannes Domani1-1/+4
Currently gdb.parameter doesn't raise an exception if an ambiguous name is used, it instead returns the value of the last partly matching parameter: ``` (gdb) show print sym Ambiguous show print command "sym": symbol, symbol-filename, symbol-loading. (gdb) show print symbol-loading Printing of symbol loading messages is "full". (gdb) py print(gdb.parameter("print sym")) full ``` It's because lookup_cmd_composition_1 tries to detect ambigous names by checking the return value of find_cmd for CMD_LIST_AMBIGUOUS, which never happens, since only lookup_cmd_1 returns CMD_LIST_AMBIGUOUS. Instead the nfound argument contains the number of found matches. By using it instead, and by setting *CMD to the special value CMD_LIST_AMBIGUOUS in this case, gdbpy_parameter can now show the appropriate error message: ``` (gdb) py print(gdb.parameter("print sym")) Traceback (most recent call last): File "<string>", line 1, in <module> RuntimeError: Parameter `print sym' is ambiguous. Error while executing Python code. (gdb) py print(gdb.parameter("print symbol")) True (gdb) py print(gdb.parameter("print symbol-")) Traceback (most recent call last): File "<string>", line 1, in <module> RuntimeError: Parameter `print symbol-' is ambiguous. Error while executing Python code. (gdb) py print(gdb.parameter("print symbol-load")) full ``` Since the document command also uses lookup_cmd_composition, it needed to check for CMD_LIST_AMBIGUOUS as well, so it now also shows an "Ambiguous command" error message in this case. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=14639 Approved-By: Tom Tromey <tom@tromey.com>
2024-02-07Fix raw-frame-arguments in combination with frame-filtersHannes Domani1-7/+15
Currently, if frame-filters are active, raw-values is used instead of raw-frame-arguments to decide if a pretty-printer should be invoked for frame arguments in a backtrace. In this example, "super struct" is the output of the pretty-printer: (gdb) disable frame-filter global BasicFrameFilter (gdb) bt #0 foo (x=42, ss=super struct = {...}) at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:47 #1 0x004016aa in main () at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:57 If no frame-filter is active, then the raw-values print option does not affect the backtrace output: (gdb) set print raw-values on (gdb) bt #0 foo (x=42, ss=super struct = {...}) at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:47 #1 0x004016aa in main () at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:57 (gdb) set print raw-values off Instead, the raw-frame-arguments option disables the pretty-printer in the backtrace: (gdb) bt -raw-frame-arguments on #0 foo (x=42, ss=...) at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:47 #1 0x004016aa in main () at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:57 But if a frame-filter is active, the same rules don't apply. The option raw-frame-arguments is ignored, but raw-values decides if the pretty-printer is used: (gdb) enable frame-filter global BasicFrameFilter (gdb) bt #0 foo (x=42, ss=super struct = {...}) at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:47 #1 0x004016aa in main () at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:57 (gdb) set print raw-values on (gdb) bt #0 foo (x=42, ss=...) at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:47 #1 0x004016aa in main () at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:57 (gdb) set print raw-values off (gdb) bt -raw-frame-arguments on #0 foo (x=42, ss=super struct = {...}) at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:47 #1 0x004016aa in main () at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:57 So this adds the PRINT_RAW_FRAME_ARGUMENTS flag to frame_filter_flag, which is then used in the frame-filter to override the raw flag in enumerate_args. Then the output is the same if a frame-filter is active, the pretty-printer for backtraces is only disabled with the raw-frame-arguments option: (gdb) enable frame-filter global BasicFrameFilter (gdb) bt #0 foo (x=42, ss=super struct = {...}) at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:47 #1 0x004016aa in main () at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:57 (gdb) set print raw-values on (gdb) bt #0 foo (x=42, ss=super struct = {...}) at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:47 #1 0x004016aa in main () at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:57 (gdb) set print raw-values off (gdb) bt -raw-frame-arguments on #0 foo (x=42, ss=...) at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:47 #1 0x004016aa in main () at C:/src/repos/gdb-testsuite/gdb/testsuite/gdb.python/py-frame-args.c:57 Co-Authored-By: Andrew Burgess <aburgess@redhat.com> Approved-By: Tom Tromey <tom@tromey.com>
2024-02-01Rename SEARCH_ALLTom Tromey1-1/+1
The constant SEARCH_ALL conflicts with a define in a Windows header. This patch renames the constant to SEARCH_ALL_DOMAINS to avoid the conflict. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31307
2024-01-29gdb: Use SYM_DOMAIN instead of DOMAIN when calling sym-domains.defLancelot SIX1-2/+2
Since commit 6771fc6f1d9 "Use a .def file for domain_enum", the sym-domains.def file has been introduced, and requires the user to define the DOMAIN(x) macro. On older systems (centos-7 with glibc-2.17 for example), this DOMAIN macro conflicts with another macro defined in /usr/include/math.h. Fix this conflict by changing sym-domains.def to use a macro named SYM_DOMAIN instead of DOMAIN. Change-Id: I679df30e2bd2f4333343f16bbd2a3511a37550a3 Approved-By: Tom Tromey <tom@tromey.com>
2024-01-28Remove some obsolete Python constantsTom Tromey1-14/+0
The Python code has exported some constants, but they are no longer documented, and were never useful. This patch removes them.
2024-01-28Use domain_search_flags in lookup_symbol et alTom Tromey3-11/+20
This changes lookup_symbol and associated APIs to accept domain_search_flags rather than a domain_enum. Note that this introduces some new constants to Python and Guile. I chose to break out the documentation patch for this, because the internals here do not change until a later patch, and it seemed simpler to patch the docs just once, rather than twice.
2024-01-28Replace search_domain with domain_search_flagsTom Tromey2-2/+2
This patch changes gdb to replace search_domain with domain_search_flags everywhere. search_domain is removed.
2024-01-28Use a .def file for domain_enumTom Tromey1-13/+8
Future patches will change and reuse the names from domain_enum. This patch makes this less error-prone by having a single point to define these names, using the typical gdb ".def" file.
2024-01-24Emit stopped event for DAP attach requestTom Tromey2-17/+15
In an earlier patch, I wrote: ... It also adds some machinery so that attach stops can be suppressed, which I think is the right thing to do. However, after some discussions here at AdaCore, I now believe this to be incorrect -- while DAP says that expected "continue" events should be suppressed, there is no corresponding language for expected "stop" events, and indeed "stop" events explicitly mention cases like "step". This patch arranges for the stop event to be emitted again.
2024-01-22Handle EOF more gracefully in DAPTom Tromey2-20/+41
A user pointed out that gdb will print a Python exception when it gets an EOF in DAP mode. And, it turns out that an EOF like this also causes gdb not to exit. This is due to the refactoring that moved the JSON reader to its own thread -- previously this caused an exception to propagate and cause an exit, but now it just leaves the reader hung. This patch fixes these problems by arranging to handle EOF more gracefully. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31217
2024-01-19gdb: Buffer output streams during events that might download debuginfoAaron Merey1-0/+3
Introduce new ui_file buffering_file to temporarily collect output written to gdb_std* output streams during print_thread, print_frame_info and print_stop_event. This ensures that output during these functions is not interrupted by debuginfod progress messages. With the addition of deferred debuginfo downloading it is possible for download progress messages to print during these events. Without any intervention we can end up with poorly formatted output: (gdb) backtrace [...] #8 0x00007fbe8af7d7cf in pygi_invoke_c_callable (Downloading separate debug info for /lib64/libpython3.11.so.1.0 function_cache=0x561221b224d0, state=<optimized out>... To fix this we buffer writes to gdb_std* output streams while allowing debuginfod progress messages to skip the buffers and print to the underlying output streams immediately. Buffered output is then written to the output streams. This ensures that progress messages print first, followed by uninterrupted frame/thread/stop info: (gdb) backtrace [...] Downloading separate debug info for /lib64/libpython3.11.so.1.0 #8 0x00007fbe8af7d7cf in pygi_invoke_c_callable (function_cache=0x561221b224d0, state=<optimized out>... Co-Authored-By: Andrew Burgess <aburgess@redhat.com> Approved-By: Andrew Burgess <aburgess@redhat.com>
2024-01-12Update copyright year range in header of all files managed by GDBAndrew Burgess112-112/+112
This commit is the result of the following actions: - Running gdb/copyright.py to update all of the copyright headers to include 2024, - Manually updating a few files the copyright.py script told me to update, these files had copyright headers embedded within the file, - Regenerating gdbsupport/Makefile.in to refresh it's copyright date, - Using grep to find other files that still mentioned 2023. If these files were updated last year from 2022 to 2023 then I've updated them this year to 2024. I'm sure I've probably missed some dates. Feel free to fix them up as you spot them.
2024-01-12gdb/python: Add gdb.InferiorThread.__dict__ attributeAndrew Burgess2-2/+17
The gdb.Objfile, gdb.Progspace, gdb.Type, and gdb.Inferior Python types already have a __dict__ attribute, which allows users to create user defined attributes within the objects. This is useful if the user wants to cache information within an object. This commit adds the same functionality to the gdb.InferiorThread type. After this commit there is a new gdb.InferiorThread.__dict__ attribute, which is a dictionary. A user can, for example, do this: (gdb) pi >>> t = gdb.selected_thread() >>> t._user_attribute = 123 >>> t._user_attribute 123 >>> There's a new test included. Reviewed-By: Eli Zaretskii <eliz@gnu.org> Approved-By: Tom Tromey <tom@tromey.com>
2024-01-12gdb/python: Add gdb.Inferior.__dict__ attributeAndrew Burgess1-1/+12
The gdb.Objfile, gdb.Progspace, and gdb.Type Python types already have a __dict__ attribute, which allows users to create user defined attributes within the objects. This is useful if the user wants to cache information within an object. This commit adds the same functionality to the gdb.Inferior type. After this commit there is a new gdb.Inferior.__dict__ attribute, which is a dictionary. A user can, for example, do this: (gdb) pi >>> i = gdb.selected_inferior() >>> i._user_attribute = 123 >>> i._user_attribute 123 >>> There's a new test included. Reviewed-By: Eli Zaretskii <eliz@gnu.org> Approved-By: Tom Tromey <tom@tromey.com>
2024-01-12gdb/python: remove users ability to create gdb.Progspace objectsAndrew Burgess1-15/+1
I noticed that it is possible for the user to create a new gdb.Progspace object, like this: (gdb) pi >>> p = gdb.Progspace() >>> p <gdb.Progspace object at 0x7ffad4219c10> >>> p.is_valid() False As the new gdb.Progspace object is not associated with an actual C++ program_space object within GDB core, then the new gdb.Progspace is created invalid, and there is no way in which the new object can ever become valid. Nor do I believe there's anywhere in the Python API where it makes sense to consume an invalid gdb.Progspace created in this way, for example, the gdb.Progspace could be passed as the locus to register_type_printer, but all that would happen is that the registered printer would never be used. In this commit I propose to remove the ability to create new gdb.Progspace objects. Attempting to do so now gives an error, like this: (gdb) pi >>> gdb.Progspace() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: cannot create 'gdb.Progspace' instances Of course, there is a small risk here that some existing user code might break ... but if that happens I don't believe the user code can have been doing anything useful, so I see this as a small risk. Reviewed-By: Eli Zaretskii <eliz@gnu.org> Approved-By: Tom Tromey <tom@tromey.com>
2024-01-12gdb/python: add gdb.Frame.__repr__() methodAndrew Burgess1-1/+18
Add a gdb.Frame.__repr__() method. Before this patch we would see output like this: (gdb) pi >>> gdb.selected_frame() <gdb.Frame object at 0x7fa8cc2df270> After this patch, we now see: (gdb) pi >>> gdb.selected_frame() <gdb.Frame level=0 frame-id={stack=0x7ffff7da0ed0,code=0x000000000040115d,!special}> More verbose, but, I hope, more useful. If the gdb.Frame becomes invalid, then we will see: (gdb) pi >>> invalid_frame_variable <gdb.Frame (invalid)> which is inline with how other invalid objects are displayed. Approved-By: Tom Tromey <tom@tromey.com>
2024-01-12gdb/python: add gdb.InferiorThread.__repr__() methodAndrew Burgess1-1/+18
Add a gdb.InferiorThread.__repr__() method. Before this patch we would see output like this: (gdb) pi >>> gdb.selected_thread() <gdb.InferiorThread object at 0x7f4dcc49b970> After this patch, we now see: (gdb) pi >>> gdb.selected_thread() <gdb.InferiorThread id=1.2 target-id="Thread 0x7ffff7da1700 (LWP 458134)"> More verbose, but, I hope, more useful. If the gdb.InferiorThread becomes invalid, then we will see: (gdb) pi >>> invalid_thread_variable <gdb.InferiorThread (invalid)> Which is inline with how other invalid objects are displayed. Approved-By: Tom Tromey <tom@tromey.com>
2024-01-12gdb/python: hoist common invalid object repr code into py-utils.cAndrew Burgess11-11/+27
Many object types now have a __repr__() function implementation. A common pattern is that, if an object is invalid, we print its representation as: <TYPENAME (invalid)>. I thought it might be a good idea to move the formatting of this specific representation into a utility function, and then update all of our existing code to call the new function. The only place where I haven't made use of the new function is in unwind_infopy_repr, where we currently return a different string. This case is a little different as the UnwindInfo is invalid because it references a frame, and it is the frame itself which is invalid. That said, I think it would be fine to switch to using the standard format; if the UnwindInfo references an invalid frame, then the UnwindInfo is itself invalid. But changing this would be an actual change in behaviour, while all the other changes in this commit are just refactoring. Approved-By: Tom Tromey <tom@tromey.com>
2024-01-12gdb/python: New InferiorThread.ptid_string attributeAndrew Burgess1-0/+27
This commit adds a new InferiorThread.ptid_string attribute. This read-only attribute contains the string returned by target_pid_to_str, which actually converts a ptid (not pid) to a string. This is the string that appears (at least in part) in the output of 'info threads' in the 'Target Id' column, but also in the thread exited message that GDB prints. Having access to this string from Python is useful for allowing extensions identify threads in a similar way to how GDB core would identify the thread. Reviewed-By: Eli Zaretskii <eliz@gnu.org> Approved-By: Tom Tromey <tom@tromey.com>
2023-12-30Add keywords to TuiWindow.writeTom Tromey1-3/+6
The gdb docs promise that methods with more than two or more arguments will accept keywords. However, I found that TuiWindow.write didn't allow them. This patch adds the missing support.
2023-12-24gdb: remove VALUE_REGNUM, add value::regnumSimon Marchi1-1/+1
Remove VALUE_REGNUM, replace it with a method on struct value. Set `m_location.reg.regnum` directly from value::allocate_register_lazy, which is fine because allocate_register_lazy is a static creation function for struct value. Change-Id: Id632502357da971617d9dce1e2eab9b56dbcf52d
2023-12-22Avoid exception from attach in DAPTom Tromey2-6/+30
I noticed that the DAP attach test case (and similarly remoted-dap.exp) had a rogue exception stack trace in the log. It turns out that an attach will generate a stop that does not have a reason. This patch fixes the problem in the _on_stop event listener by making it a bit more careful when examining the event reason. It also adds some machinery so that attach stops can be suppressed, which I think is the right thing to do. Reviewed-By: Kévin Le Gouguec <legouguec@adacore.com>
2023-12-22Add DAP log level parameterTom Tromey3-6/+41
This adds a new parameter to control the DAP logging level. By default, "expected" exceptions are not logged, but the parameter lets the user change this when more logging is desired. This also changes a couple of spots to avoid logging the stack trace for a DAPException. This patch also documents the existing DAP logging parameter. I forgot to document this before. Reviewed-By: Eli Zaretskii <eliz@gnu.org> Reviewed-By: Kévin Le Gouguec <legouguec@adacore.com>
2023-12-22Introduce and use DAPExceptionTom Tromey6-17/+37
This introduces a new DAPException class, and then changes various spots in the DAP implementation to wrap "expected" exceptions in this. This class will help detect rogue exceptions caused by bugs in the implementation. Reviewed-By: Kévin Le Gouguec <legouguec@adacore.com>
2023-12-22Add 'program' to DAP 'attach' requestTom Tromey1-4/+21
In many cases, it's not possible for gdb to discover the executable when a DAP 'attach' request is used. This patch lets the IDE supply this information. Reviewed-By: Eli Zaretskii <eliz@gnu.org>
2023-12-21Fix Clang build issue with flexible array member and non-trivial dtorPedro Alves1-1/+9
Commit d5cebea18e7a ("Make cached_reg_t own its data") added a destructor to cached_reg_t. That caused a build problem with Clang, which errors out like so: > CXX python/py-unwind.o > gdb/python/py-unwind.c:126:16: error: flexible array member 'reg' of type 'cached_reg_t[]' with non-trivial destruction > 126 | cached_reg_t reg[]; > | ^ This is is not really a problem for our code, which allocates the whole structure with xmalloc, and then initializes the array elements with in-place new, and then takes care to call the destructor manually. Like, commit d5cebea18e7a did: @@ -928,7 +927,7 @@ pyuw_dealloc_cache (frame_info *this_frame, void *cache) cached_frame_info *cached_frame = (cached_frame_info *) cache; for (int i = 0; i < cached_frame->reg_count; i++) - xfree (cached_frame->reg[i].data); + cached_frame->reg[i].~cached_reg_t (); Maybe we should get rid of the flexible array member and use a bog standard std::vector. I doubt this would cause any visible performance issue. Meanwhile, to unbreak the build, this commit switches from C99-style flexible array member to 0-length array. It behaves the same, and Clang doesn't complain. I got the idea from here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70932#c11 GCC 9, our oldest support version, already supported this: https://gcc.gnu.org/onlinedocs/gcc-9.1.0/gcc/Zero-Length.html but the extension is actually much older than that. Note that C99-style flexible array members are not standard C++ either. Change-Id: I37dda18f367e238a41d610619935b2a0f2acacce
2023-12-20Make cached_reg_t own its dataPedro Alves1-7/+6
struct cached_reg_t owns its data buffer, but currently that is managed manually. Convert it to use a unique_xmalloc_ptr. Approved-By: Tom Tromey <tom@tromey.com> Change-Id: I05a107098b717299e76de76aaba00d7fbaeac77b
2023-12-14gdb: change value_of_register and value_of_register_lazy to take the next frameSimon Marchi2-3/+4
Some functions related to the handling of registers in frames accept "this frame", for which we want to read or write the register values, while other functions accept "the next frame", which is the frame next to that. The later is needed because we sometimes need to read register values for a frame that does not exist yet (usually when trying to unwind that frame-to-be). value_of_register and value_of_register_lazy both take "this frame", even if what they ultimately want internally is "the next frame". This is annoying if you are in a spot that currently has "the next frame" and need to call one of these functions (which happens later in this series). You need to get the previous frame only for those functions to get the next frame again. This is more manipulations, more chances of mistake. I propose to change these functions (and a few more functions in the subsequent patches) to operate on "the next frame". Things become a bit less awkward when all these functions agree on which frame they take. So, in this patch, change value_of_register_lazy and value_of_register to take "the next frame" instead of "this frame". This adds a lot of get_next_frame_sentinel_okay, but if we convert the user registers API to also use "the next frame" instead of "this frame", it will get simple again. Change-Id: Iaa24815e648fbe5ae3c214c738758890a91819cd Reviewed-By: John Baldwin <jhb@FreeBSD.org>
2023-12-13Use unique_xmalloc_ptr in explicit_location_specTom Tromey1-6/+6
This changes explicit_location_spec to use unique_xmalloc_ptr, removing some manual memory management. Reviewed-By: John Baldwin <jhb@FreeBSD.org>
2023-12-13gdb/python: avoid use of _PyOS_ReadlineTStateAndrew Burgess1-6/+4
In python/py-gdb-readline.c we make use of _PyOS_ReadlineTState, however, this variable is no longer public in Python 3.13, and so GDB no longer builds. We are making use of _PyOS_ReadlineTState in order to re-acquire the Python Global Interpreter Lock (GIL). The _PyOS_ReadlineTState variable is set in Python's outer readline code prior to calling the user (GDB) supplied readline callback function, which for us is gdbpy_readline_wrapper. The gdbpy_readline_wrapper function is called without the GIL held. Instead of using _PyOS_ReadlineTState, I propose that we switch to calling PyGILState_Ensure() and PyGILState_Release(). These functions will acquire the GIL based on the current thread. I think this should be sufficient; I can't imagine why we'd be running gdbpy_readline_wrapper on one thread on behalf of a different Python thread.... that would be unexpected I think. Approved-By: Tom Tromey <tom@tromey.com>
2023-12-13gdb: move gdbpy_gil into python-internal.hAlexandra Hájková2-24/+24
Move gdbpy_gil class into python-internal.h, the next commit wants to make use of this class from a file other than python.c. Approved-By: Tom Tromey <tom@tromey.com>
2023-12-12Fix gdb.FinishBreakpoint when returning to an inlined functionHannes Domani1-1/+2
Currently, when creating a gdb.FinishBreakpoint in a function called from an inline frame, it will never be hit: ``` (gdb) py fb=gdb.FinishBreakpoint() Temporary breakpoint 1 at 0x13f1917b4: file C:/src/repos/binutils-gdb.git/gdb/testsuite/gdb.python/py-finish-breakpoint.c, line 47. (gdb) c Continuing. Thread-specific breakpoint 1 deleted - thread 1 no longer in the thread list. [Inferior 1 (process 1208) exited normally] ``` The reason is that the frame_id of a breakpoint has to be the ID of a real frame, ignoring any inline frames. With this fixed, it's working correctly: ``` (gdb) py fb=gdb.FinishBreakpoint() Temporary breakpoint 1 at 0x13f5617b4: file C:/src/repos/binutils-gdb.git/gdb/testsuite/gdb.python/py-finish-breakpoint.c, line 47. (gdb) c Continuing. Breakpoint 1, increase_inlined (a=0x40fa5c) at C:/src/repos/binutils-gdb.git/gdb/testsuite/gdb.python/py-finish-breakpoint.c:47 (gdb) py print(fb.return_value) -8 ``` Approved-By: Tom Tromey <tom@tromey.com>
2023-12-11Implement DAP cancellationTom Tromey1-3/+88
This implements DAP cancellation. A new object is introduced that handles the details of cancellation. While cancellation is inherently racy, this code attempts to make it so that gdb doesn't inadvertently cancel the wrong request. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30472 Approved-By: Eli Zaretskii <eliz@gnu.org> Reviewed-By: Kévin Le Gouguec <legouguec@adacore.com>
2023-12-11Catch KeyboardInterrupt in send_gdb_with_responseTom Tromey1-2/+2
Cancellation will generally be seen by the DAP code as a KeyboardInterrupt. However, this derives from BaseException and not Exception, so a small change is needed to send_gdb_with_response, to forward the exception to the DAP server thread. Reviewed-By: Kévin Le Gouguec <legouguec@adacore.com>
2023-12-11Introduce gdb.interruptTom Tromey1-0/+19
DAP cancellation needs a way to interrupt whatever is happening on gdb's main thread -- whether that is the inferior, a gdb CLI command, or Python code. This patch adds a new gdb.interrupt() function for this purpose. It simply sets the quit flag and lets gdb do the rest. No tests in this patch -- instead this is tested via the DAP cancellation tests. Reviewed-By: Eli Zaretskii <eliz@gnu.org> Reviewed-By: Kévin Le Gouguec <legouguec@adacore.com>
2023-12-11Move DAP JSON reader to its own threadTom Tromey1-2/+13
This changes the DAP server to move the JSON reader to a new thread. This is key to implementing request cancellation, as now requests can be read while an earlier one is being serviced. Reviewed-By: Kévin Le Gouguec <legouguec@adacore.com>