diff options
author | Tom Tromey <tromey@adacore.com> | 2023-11-17 10:08:50 -0700 |
---|---|---|
committer | Tom Tromey <tromey@adacore.com> | 2023-11-27 09:16:46 -0700 |
commit | fb2d9542d0b22b31171bafe87153328cb743c28f (patch) | |
tree | 47edb4a6649822ccc0b5d3ff3bc1ef7202b3905f /gdb | |
parent | 5a37e5855cd8ce0833eca7d1ab914b9046a06429 (diff) | |
download | gdb-fb2d9542d0b22b31171bafe87153328cb743c28f.zip gdb-fb2d9542d0b22b31171bafe87153328cb743c28f.tar.gz gdb-fb2d9542d0b22b31171bafe87153328cb743c28f.tar.bz2 |
Fix bug in DAP handling of 'pause' requests
While working on cancellation, I noticed that a DAP 'pause' request
would set the "do not emit the continue" flag. This meant that a
subsequent request that should provoke a 'continue' event would
instead suppress the event.
I then tried writing a more obvious test case for this, involving an
inferior call -- and discovered that gdb.events.cont does not fire for
an inferior call.
This patch installs a new event listener for gdb.events.inferior_call
and arranges for this to emit continue and stop events when
appropriate. It also fixes the original bug, by adding a check to
exec_and_expect_stop.
(cherry picked from commit c618a1c548193d2a6a8c3d909a3d1c620a156b5d)
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/python/lib/gdb/dap/events.py | 26 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dap/pause.c | 44 | ||||
-rw-r--r-- | gdb/testsuite/gdb.dap/pause.exp | 30 |
3 files changed, 97 insertions, 3 deletions
diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py index bfc3f9e..663b67e 100644 --- a/gdb/python/lib/gdb/dap/events.py +++ b/gdb/python/lib/gdb/dap/events.py @@ -128,8 +128,9 @@ def exec_and_expect_stop(cmd, reason): """Indicate that a stop is expected, then execute CMD""" global _expected_stop _expected_stop = reason - global _suppress_cont - _suppress_cont = True + if reason != StopKinds.PAUSE: + global _suppress_cont + _suppress_cont = True # FIXME if the call fails should we clear _suppress_cont? exec_and_log(cmd) @@ -156,6 +157,26 @@ def _on_stop(event): send_event("stopped", obj) +# This keeps a bit of state between the start of an inferior call and +# the end. If the inferior was already running when the call started +# (as can happen if a breakpoint condition calls a function), then we +# do not want to emit 'continued' or 'stop' events for the call. Note +# that, for some reason, gdb.events.cont does not fire for an infcall. +_infcall_was_running = False + + +@in_gdb_thread +def _on_inferior_call(event): + global _infcall_was_running + if isinstance(event, gdb.InferiorCallPreEvent): + _infcall_was_running = inferior_running + if not _infcall_was_running: + _cont(None) + else: + if not _infcall_was_running: + _on_stop(None) + + gdb.events.stop.connect(_on_stop) gdb.events.exited.connect(_on_exit) gdb.events.new_thread.connect(_new_thread) @@ -163,3 +184,4 @@ gdb.events.thread_exited.connect(_thread_exited) gdb.events.cont.connect(_cont) gdb.events.new_objfile.connect(_new_objfile) gdb.events.free_objfile.connect(_objfile_removed) +gdb.events.inferior_call.connect(_on_inferior_call) diff --git a/gdb/testsuite/gdb.dap/pause.c b/gdb/testsuite/gdb.dap/pause.c new file mode 100644 index 0000000..2395ca6 --- /dev/null +++ b/gdb/testsuite/gdb.dap/pause.c @@ -0,0 +1,44 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011-2023 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <unistd.h> + +int +do_nothing () +{ + return 91; +} + +int +return_false () +{ + return 0; +} + +void +sleep_a_bit () +{ + sleep (1); /* STOP */ +} + +int +main () +{ + while (1) + sleep_a_bit (); + return 0; +} diff --git a/gdb/testsuite/gdb.dap/pause.exp b/gdb/testsuite/gdb.dap/pause.exp index 558ede9..30ce609 100644 --- a/gdb/testsuite/gdb.dap/pause.exp +++ b/gdb/testsuite/gdb.dap/pause.exp @@ -19,7 +19,7 @@ require allow_dap_tests load_lib dap-support.exp -standard_testfile attach.c +standard_testfile if {[build_executable ${testfile}.exp $testfile $srcfile] == -1} { return @@ -29,6 +29,18 @@ if {[dap_launch $testfile] == ""} { return } +# Set a conditional breakpoint that will never fire. This is done to +# test the state-tracking in events -- an inferior call from a +# breakpoint condition should not cause any sort of stop or continue +# events. +set line [gdb_get_line_number "STOP"] +dap_check_request_and_response "set conditional breakpoint" \ + setBreakpoints \ + [format {o source [o path [%s]] \ + breakpoints [a [o line [i %d] \ + condition [s "return_false()"]]]} \ + [list s $srcfile] $line] + dap_check_request_and_response "start inferior" configurationDone dap_wait_for_event_and_check "inferior started" thread "body reason" started @@ -45,4 +57,20 @@ dap_check_request_and_response pause pause \ dap_wait_for_event_and_check "stopped by pause" stopped \ "body reason" pause +set result [dap_request_and_response evaluate {o expression [s do_nothing()]}] +gdb_assert {[dict get [lindex $result 0] body result] == 91} \ + "check result of evaluation" + +set seen fail +foreach event [lindex $result 1] { + if {[dict get $event type] != "event"} { + continue + } + if {[dict get $event event] == "continued"} { + set seen pass + break + } +} +gdb_assert {$seen == "pass"} "continue event from inferior call" + dap_shutdown |