mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
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.
This commit is contained in:
@@ -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)
|
||||
|
||||
44
gdb/testsuite/gdb.dap/pause.c
Normal file
44
gdb/testsuite/gdb.dap/pause.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user