From: Tom Tromey Date: Wed, 20 Nov 2024 20:04:27 +0000 (-0700) Subject: Defer DAP launch command until after configurationDone X-Git-Tag: gdb-16-branchpoint~149 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6b9efd5c1a6ead15845ef5d40b0a4bdc2cb33480;p=thirdparty%2Fbinutils-gdb.git Defer DAP launch command until after configurationDone PR dap/32090 points out that gdb's DAP "launch" sequencing is incorrect. The current approach (which is itself a 2nd implementation...) was based on a misreading of the spec. The spec has since been clarified here: https://github.com/microsoft/debug-adapter-protocol/issues/497 The clarification here is that a client is free to send the "launch" (or "attach") request at any point after the "initialized" event has been sent by gdb. However, the "launch" does not cause any action to be taken -- and does not send a response -- until after "configurationDone" has been seen. This patch implements this by arranging for the launch and attach commands to return a DeferredRequest object. All the tests needed updates. I've also added a new test that checks that the deferred "launch" request can be cancelled. (Note that the cancellation is lazy -- it also waits until configurationDone is seen. This could be fixed, but I was not sure whether it is important to do so.) Finally, the "launch" command has a somewhat funny sequencing now. Simply sending the command and waiting for a response yielded strange results if the inferior did not stop -- in this case, the repsonse was never sent. So now, the command is split into two parts, with some setup being done synchronously (for better error propagation) and the actual "run" being done async. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32090 Reviewed-by: Kévin Le Gouguec --- diff --git a/gdb/NEWS b/gdb/NEWS index 361d7726ba0..047f8ad835a 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -98,6 +98,12 @@ the return value from the latest "stepOut" command, when appropriate. + ** The "launch" and "attach" requests were rewritten in accordance + with some clarifications to the spec. Now they can be sent at + any time after the "initialized" event, but will not take effect + (or send a response) until after the "configurationDone" request + has been sent. + * New commands show jit-reader-directory diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py index 6444c8b7b67..fc1890c5a43 100644 --- a/gdb/python/lib/gdb/dap/launch.py +++ b/gdb/python/lib/gdb/dap/launch.py @@ -21,8 +21,42 @@ from typing import Mapping, Optional, Sequence import gdb from .events import exec_and_expect_stop, expect_process, expect_stop -from .server import capability, request -from .startup import DAPException, exec_and_log, in_gdb_thread +from .server import ( + DeferredRequest, + call_function_later, + capability, + request, + send_gdb, + send_gdb_with_response, +) +from .startup import DAPException, exec_and_log, in_dap_thread, in_gdb_thread + +# A launch or attach promise that that will be fulfilled after a +# configurationDone request has been processed. +_launch_or_attach_promise = None + + +# A DeferredRequest that handles either a "launch" or "attach" +# request. +class _LaunchOrAttachDeferredRequest(DeferredRequest): + def __init__(self, callback): + self._callback = callback + global _launch_or_attach_promise + if _launch_or_attach_promise is not None: + raise DAPException("launch or attach already specified") + _launch_or_attach_promise = self + + # Invoke the callback and return the result. + @in_dap_thread + def invoke(self): + return self._callback() + + # Override this so we can clear the global when rescheduling. + @in_dap_thread + def reschedule(self): + global _launch_or_attach_promise + _launch_or_attach_promise = None + super().reschedule() # A wrapper for the 'file' command that correctly quotes its argument. @@ -37,7 +71,7 @@ def file_command(program): # Any parameters here are necessarily extensions -- DAP requires this # from implementations. Any additions or changes here should be # documented in the gdb manual. -@request("launch", response=False) +@request("launch", on_dap_thread=True) def launch( *, program: Optional[str] = None, @@ -48,27 +82,51 @@ def launch( stopOnEntry: bool = False, **extra, ): - if cwd is not None: - exec_and_log("cd " + cwd) - if program is not None: - file_command(program) - inf = gdb.selected_inferior() - inf.arguments = args - if env is not None: - inf.clear_env() - for name, value in env.items(): - inf.set_env(name, value) - expect_process("process") - if stopAtBeginningOfMainSubprogram: - cmd = "start" - elif stopOnEntry: - cmd = "starti" - else: - cmd = "run" - exec_and_expect_stop(cmd) - - -@request("attach") + # Launch setup is handled here. This is done synchronously so + # that errors can be reported in a natural way. + @in_gdb_thread + def _setup_launch(): + if cwd is not None: + exec_and_log("cd " + cwd) + if program is not None: + file_command(program) + inf = gdb.selected_inferior() + inf.arguments = args + if env is not None: + inf.clear_env() + for name, value in env.items(): + inf.set_env(name, value) + + # Actual launching done here. See below for more info. + @in_gdb_thread + def _do_launch(): + expect_process("process") + if stopAtBeginningOfMainSubprogram: + cmd = "start" + elif stopOnEntry: + cmd = "starti" + else: + cmd = "run" + exec_and_expect_stop(cmd) + + @in_dap_thread + def _launch_impl(): + send_gdb_with_response(_setup_launch) + # We do not wait for the result here. It might be a little + # nicer if we did -- perhaps the various thread events would + # occur in a more logical sequence -- but if the inferior does + # not stop, then the launch response will not be seen either, + # which seems worse. + send_gdb(_do_launch) + # Launch response does not have a body. + return None + + # The launch itself is deferred until the configurationDone + # request. + return _LaunchOrAttachDeferredRequest(_launch_impl) + + +@request("attach", on_dap_thread=True) def attach( *, program: Optional[str] = None, @@ -76,21 +134,39 @@ def attach( target: Optional[str] = None, **args, ): - if program is not None: - file_command(program) - if pid is not None: - cmd = "attach " + str(pid) - elif target is not None: - cmd = "target remote " + target - else: - raise DAPException("attach requires either 'pid' or 'target'") - expect_process("attach") - expect_stop("attach") - exec_and_log(cmd) + # The actual attach is handled by this function. + @in_gdb_thread + def _do_attach(): + if program is not None: + file_command(program) + if pid is not None: + cmd = "attach " + str(pid) + elif target is not None: + cmd = "target remote " + target + else: + raise DAPException("attach requires either 'pid' or 'target'") + expect_process("attach") + expect_stop("attach") + exec_and_log(cmd) + # Attach response does not have a body. + return None + + @in_dap_thread + def _attach_impl(): + return send_gdb_with_response(_do_attach) + + # The attach itself is deferred until the configurationDone + # request. + return _LaunchOrAttachDeferredRequest(_attach_impl) @capability("supportsConfigurationDoneRequest") -@request("configurationDone") +@request("configurationDone", on_dap_thread=True) def config_done(**args): - # Nothing to do. - return None + # Handle the launch or attach. + global _launch_or_attach_promise + if _launch_or_attach_promise is None: + raise DAPException("launch or attach not specified") + # Resolve the launch or attach, but only after the + # configurationDone response has been sent. + call_function_later(_launch_or_attach_promise.reschedule) diff --git a/gdb/testsuite/gdb.dap/ada-arrays.exp b/gdb/testsuite/gdb.dap/ada-arrays.exp index 0de361f96bd..64c6eb28c89 100644 --- a/gdb/testsuite/gdb.dap/ada-arrays.exp +++ b/gdb/testsuite/gdb.dap/ada-arrays.exp @@ -33,6 +33,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + # Stop in a C frame, but examine values in an Ada frame, to make sure # cross-language scenarios work correctly. set line [gdb_get_line_number "STOP" $testdir/cstuff.c] @@ -44,9 +46,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/ada-nested.exp b/gdb/testsuite/gdb.dap/ada-nested.exp index 3415da3e9cb..7f28d27e365 100644 --- a/gdb/testsuite/gdb.dap/ada-nested.exp +++ b/gdb/testsuite/gdb.dap/ada-nested.exp @@ -31,6 +31,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "STOP"] set obj [dap_check_request_and_response "set breakpoint" \ setBreakpoints \ @@ -41,9 +43,8 @@ set fn_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $binfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at breakpoint" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" $fn_bpno diff --git a/gdb/testsuite/gdb.dap/ada-scopes.exp b/gdb/testsuite/gdb.dap/ada-scopes.exp index 4d895a5270d..ef1302e0644 100644 --- a/gdb/testsuite/gdb.dap/ada-scopes.exp +++ b/gdb/testsuite/gdb.dap/ada-scopes.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "STOP"] set obj [dap_check_request_and_response "set breakpoint" \ setBreakpoints \ @@ -39,9 +41,8 @@ set fn_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $binfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at breakpoint" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" $fn_bpno diff --git a/gdb/testsuite/gdb.dap/args-env.exp b/gdb/testsuite/gdb.dap/args-env.exp index d6511733348..5f02b00dd5a 100644 --- a/gdb/testsuite/gdb.dap/args-env.exp +++ b/gdb/testsuite/gdb.dap/args-env.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile arguments {a "b c"} env {{DEI something}}] + set line [gdb_get_line_number "BREAK"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -38,9 +40,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile arguments {a "b c"} env {{DEI something}}] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/assign.exp b/gdb/testsuite/gdb.dap/assign.exp index 6703a97bb7a..6665b6a6a31 100644 --- a/gdb/testsuite/gdb.dap/assign.exp +++ b/gdb/testsuite/gdb.dap/assign.exp @@ -36,6 +36,8 @@ save_vars GDBFLAGS { } } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "STOP"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -45,9 +47,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" $line_bpno diff --git a/gdb/testsuite/gdb.dap/attach.exp b/gdb/testsuite/gdb.dap/attach.exp index ce2a16c1193..e0cd57b2009 100644 --- a/gdb/testsuite/gdb.dap/attach.exp +++ b/gdb/testsuite/gdb.dap/attach.exp @@ -29,17 +29,14 @@ set test_spawn_id [spawn_wait_for_attach $binfile] set testpid [spawn_id_get_pid $test_spawn_id] # Test that attaching works at all. -set result [dap_attach $testpid $binfile] - -set found 0 -foreach ev [lindex $result 1] { - if {[dict get $ev type] == "event" - && [dict get $ev event] == "stopped" - && [dict get $ev body reason] == "attach"} { - set found 1 - } -} -gdb_assert {$found} "saw stopped event for attach" +set attach_id [dap_attach $testpid $binfile] + +dap_check_request_and_response "configurationDone" configurationDone + +dap_wait_for_event_and_check "stopped" stopped \ + "body reason" attach + +dap_check_response "attach response" attach $attach_id dap_shutdown true diff --git a/gdb/testsuite/gdb.dap/basic-dap.exp b/gdb/testsuite/gdb.dap/basic-dap.exp index 6ef9a5b0f6e..dd785ef5364 100644 --- a/gdb/testsuite/gdb.dap/basic-dap.exp +++ b/gdb/testsuite/gdb.dap/basic-dap.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set obj [dap_check_request_and_response "set breakpoint on two functions" \ setFunctionBreakpoints \ {o breakpoints [a [o name [s function_breakpoint_here]] \ @@ -86,9 +88,8 @@ gdb_assert {$new_line_bpno == $line_bpno} "re-setting kept same breakpoint numbe dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started # While waiting for the stopped event, we might receive breakpoint changed diff --git a/gdb/testsuite/gdb.dap/bt-nodebug.exp b/gdb/testsuite/gdb.dap/bt-nodebug.exp index 550b9c5c5ac..319dcf0c583 100644 --- a/gdb/testsuite/gdb.dap/bt-nodebug.exp +++ b/gdb/testsuite/gdb.dap/bt-nodebug.exp @@ -31,6 +31,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set obj [dap_check_request_and_response "set breakpoint on inner" \ setFunctionBreakpoints \ {o breakpoints [a [o name [s function_breakpoint_here]]]}] @@ -38,9 +40,8 @@ set fn_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started lassign [dap_wait_for_event_and_check "stopped at function breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/cancel-launch.exp b/gdb/testsuite/gdb.dap/cancel-launch.exp new file mode 100644 index 00000000000..7ef18d1f137 --- /dev/null +++ b/gdb/testsuite/gdb.dap/cancel-launch.exp @@ -0,0 +1,60 @@ +# Copyright 2024 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 . + +# Test cancellation of a "launch" command. + +require allow_dap_tests + +load_lib dap-support.exp + +# Anything will work, we aren't going to run it. +standard_testfile sources.c + +if {[build_executable ${testfile}.exp $testfile $srcfile] == -1} { + return +} + +if {[dap_initialize] == ""} { + return +} + +set launch_id [dap_launch $testfile] + +# Set a breakpoint. This is done to ensure that the launch request is +# definitely in the deferred state when we try to cancel it. +set line [gdb_get_line_number "Distinguishing comment"] +dap_check_request_and_response "set breakpoint by line number" \ + setBreakpoints \ + [format {o source [o path [%s]] breakpoints [a [o line [i %d]]]} \ + [list s $srcfile] $line] + +set cancel_id [dap_send_request cancel \ + [format {o requestId [i %d]} $launch_id]] + +dap_read_response cancel $cancel_id + +# The cancellation isn't actually processed until configurationDone is +# sent. While this seems fine, it's unclear if gdb should be more +# eager here and try to cancel a deferred task before it is +# rescheduled. +dap_check_request_and_response "configurationDone" configurationDone + +set resp [lindex [dap_read_response launch $launch_id] 0] +gdb_assert {[dict get $resp success] == "false"} \ + "launch failed" +gdb_assert {[dict get $resp message] == "cancelled"} \ + "launch cancelled" + +dap_shutdown diff --git a/gdb/testsuite/gdb.dap/catch-exception.exp b/gdb/testsuite/gdb.dap/catch-exception.exp index a1ced06cad2..97937466c40 100644 --- a/gdb/testsuite/gdb.dap/catch-exception.exp +++ b/gdb/testsuite/gdb.dap/catch-exception.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set obj [dap_check_request_and_response "set exception catchpoints" \ setExceptionBreakpoints \ {o filters [a [s nosuchfilter] [s assert]] \ @@ -69,9 +71,8 @@ foreach spec $bps { dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $binfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at first raise" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" 2 diff --git a/gdb/testsuite/gdb.dap/children.exp b/gdb/testsuite/gdb.dap/children.exp index f5dfe2cbb91..783fff98ce9 100644 --- a/gdb/testsuite/gdb.dap/children.exp +++ b/gdb/testsuite/gdb.dap/children.exp @@ -36,6 +36,8 @@ save_vars GDBFLAGS { } } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "STOP"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -45,9 +47,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" $line_bpno diff --git a/gdb/testsuite/gdb.dap/cond-bp.exp b/gdb/testsuite/gdb.dap/cond-bp.exp index 2bd52ba83a0..28d4b8e8ff3 100644 --- a/gdb/testsuite/gdb.dap/cond-bp.exp +++ b/gdb/testsuite/gdb.dap/cond-bp.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "STOP"] # Test some breakpoint-setting failure modes. @@ -65,9 +67,8 @@ set fn_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at function breakpoint" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" $fn_bpno diff --git a/gdb/testsuite/gdb.dap/cwd.exp b/gdb/testsuite/gdb.dap/cwd.exp index 6b8829965c5..b3c9f9c21de 100644 --- a/gdb/testsuite/gdb.dap/cwd.exp +++ b/gdb/testsuite/gdb.dap/cwd.exp @@ -29,14 +29,14 @@ if {[dap_initialize] == ""} { return } -dap_check_request_and_response "configurationDone" configurationDone - # Starting the inferior will fail if the change of cwd does not work. set the_dir [file dirname $testfile] set the_file [file tail $testfile] -if {[dap_launch $the_file cwd $the_dir stop_at_main 1] == ""} { - return -} +set launch_id [dap_launch $the_file cwd $the_dir stop_at_main 1] + +dap_check_request_and_response "configurationDone" configurationDone + +dap_check_response "launch response" launch $launch_id # We didn't explicitly set a breakpoint, so if we hit one, it worked. dap_wait_for_event_and_check "stopped at function breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/cxx-exception.exp b/gdb/testsuite/gdb.dap/cxx-exception.exp index 13320439b6d..c79c5dba6a7 100644 --- a/gdb/testsuite/gdb.dap/cxx-exception.exp +++ b/gdb/testsuite/gdb.dap/cxx-exception.exp @@ -28,6 +28,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set obj [dap_check_request_and_response "set exception catchpoints" \ setExceptionBreakpoints \ {o filters [a [s throw] [s rethrow] [s catch]]}] @@ -49,9 +51,8 @@ foreach bp $bps { dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at throw" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" 1 diff --git a/gdb/testsuite/gdb.dap/disassem.exp b/gdb/testsuite/gdb.dap/disassem.exp index 87fb516931a..6fdfc6358cf 100644 --- a/gdb/testsuite/gdb.dap/disassem.exp +++ b/gdb/testsuite/gdb.dap/disassem.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set obj [dap_check_request_and_response "set breakpoint" \ setFunctionBreakpoints \ {o breakpoints [a [o name [s main]]]}] @@ -36,9 +38,8 @@ set fn_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/frameless.exp b/gdb/testsuite/gdb.dap/frameless.exp index 63ee521af65..6963eed62c4 100644 --- a/gdb/testsuite/gdb.dap/frameless.exp +++ b/gdb/testsuite/gdb.dap/frameless.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "BREAK"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -38,9 +40,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ "body reason" breakpoint \ diff --git a/gdb/testsuite/gdb.dap/global.exp b/gdb/testsuite/gdb.dap/global.exp index 79f7f2facf5..2704f46cfaf 100644 --- a/gdb/testsuite/gdb.dap/global.exp +++ b/gdb/testsuite/gdb.dap/global.exp @@ -27,6 +27,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "BREAK"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -36,9 +38,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/hover.exp b/gdb/testsuite/gdb.dap/hover.exp index 0c80650b152..f637c4a06d1 100644 --- a/gdb/testsuite/gdb.dap/hover.exp +++ b/gdb/testsuite/gdb.dap/hover.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "BREAK"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -38,9 +40,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/insn-bp.exp b/gdb/testsuite/gdb.dap/insn-bp.exp index 4a4c14484e1..6542262b21d 100644 --- a/gdb/testsuite/gdb.dap/insn-bp.exp +++ b/gdb/testsuite/gdb.dap/insn-bp.exp @@ -39,6 +39,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set obj [dap_check_request_and_response "set breakpoint on main" \ setFunctionBreakpoints \ {o breakpoints [a [o name [s main]]]}] @@ -56,14 +58,12 @@ gdb_assert {[dict get $bp verified] == "false"} \ dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} - # The event we're looking for should occur during startup, but we want # to leave open the possibility that it occurs when waiting for the -# stopped event. So, keep both event lists around and search them +# stopped event. So, keep all event lists around and search them # once below. +lassign [dap_check_response "launch response" launch $launch_id] \ + unused objs0 lassign [dap_wait_for_event_and_check "inferior started" \ thread "body reason" started] \ unused objs1 @@ -72,7 +72,7 @@ lassign [dap_wait_for_event_and_check "stopped at breakpoint" stopped \ "body hitBreakpointIds" $fn_bpno] unused objs2 set found_bp_event 0 -foreach obj [concat $objs1 $objs2] { +foreach obj [concat $objs0 $objs1 $objs2] { if { [dict get $obj "type"] != "event" } { continue } diff --git a/gdb/testsuite/gdb.dap/lazy-string.exp b/gdb/testsuite/gdb.dap/lazy-string.exp index 54422209fda..d070347e760 100644 --- a/gdb/testsuite/gdb.dap/lazy-string.exp +++ b/gdb/testsuite/gdb.dap/lazy-string.exp @@ -36,6 +36,8 @@ save_vars GDBFLAGS { } } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "STOP"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -45,9 +47,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" $line_bpno diff --git a/gdb/testsuite/gdb.dap/log-message.exp b/gdb/testsuite/gdb.dap/log-message.exp index e966b962a71..a0070afdb2f 100644 --- a/gdb/testsuite/gdb.dap/log-message.exp +++ b/gdb/testsuite/gdb.dap/log-message.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "HERE"] set obj [dap_check_request_and_response "set breakpoint" \ setBreakpoints \ @@ -40,9 +42,8 @@ set fn_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "logging output" output \ diff --git a/gdb/testsuite/gdb.dap/memory.exp b/gdb/testsuite/gdb.dap/memory.exp index c4e4fb3238b..89ae8bb7b1a 100644 --- a/gdb/testsuite/gdb.dap/memory.exp +++ b/gdb/testsuite/gdb.dap/memory.exp @@ -37,6 +37,8 @@ save_vars { env(ASAN_OPTIONS) env(TSAN_OPTIONS) } { } } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "BREAK"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -46,9 +48,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/modules.exp b/gdb/testsuite/gdb.dap/modules.exp index 87cebda7131..2e41679e691 100644 --- a/gdb/testsuite/gdb.dap/modules.exp +++ b/gdb/testsuite/gdb.dap/modules.exp @@ -42,6 +42,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set obj [dap_check_request_and_response "set breakpoint on stop function" \ setFunctionBreakpoints \ {o breakpoints [a [o name [s stop]]]}] @@ -49,9 +51,8 @@ set fn_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at function breakpoint" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" $fn_bpno diff --git a/gdb/testsuite/gdb.dap/pause.exp b/gdb/testsuite/gdb.dap/pause.exp index 9038d0f037f..c74fea3ebfd 100644 --- a/gdb/testsuite/gdb.dap/pause.exp +++ b/gdb/testsuite/gdb.dap/pause.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + # 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 @@ -43,9 +45,8 @@ dap_check_request_and_response "set conditional breakpoint" \ dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "process event generated" process \ "body startMethod" process dap_wait_for_event_and_check "inferior started" thread "body reason" started diff --git a/gdb/testsuite/gdb.dap/ptrref.exp b/gdb/testsuite/gdb.dap/ptrref.exp index 236ffae12d5..2a972aca045 100644 --- a/gdb/testsuite/gdb.dap/ptrref.exp +++ b/gdb/testsuite/gdb.dap/ptrref.exp @@ -27,6 +27,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "BREAK"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -36,9 +38,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/remote-dap.exp b/gdb/testsuite/gdb.dap/remote-dap.exp index 5c28a045e70..bca66dd1055 100644 --- a/gdb/testsuite/gdb.dap/remote-dap.exp +++ b/gdb/testsuite/gdb.dap/remote-dap.exp @@ -42,8 +42,12 @@ lassign [gdbserver_start "" $target_exec] protocol port gdb_assert {$protocol == "remote"} # We just want to test that attaching works at all. -if {[dap_target_remote $port] != ""} { - dap_shutdown true -} +set attach_id [dap_target_remote $port] + +dap_check_request_and_response "configurationDone" configurationDone + +dap_check_response "attach response" attach $attach_id + +dap_shutdown true close_gdbserver diff --git a/gdb/testsuite/gdb.dap/rust-slices.exp b/gdb/testsuite/gdb.dap/rust-slices.exp index 4af8c11e684..1ed640a9af1 100644 --- a/gdb/testsuite/gdb.dap/rust-slices.exp +++ b/gdb/testsuite/gdb.dap/rust-slices.exp @@ -32,6 +32,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "STOP"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -41,9 +43,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/scopes.exp b/gdb/testsuite/gdb.dap/scopes.exp index aa3bb688c0e..a8a0c01690e 100644 --- a/gdb/testsuite/gdb.dap/scopes.exp +++ b/gdb/testsuite/gdb.dap/scopes.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "BREAK"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -38,9 +40,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/sources.exp b/gdb/testsuite/gdb.dap/sources.exp index ee853cc68aa..305a27c7261 100644 --- a/gdb/testsuite/gdb.dap/sources.exp +++ b/gdb/testsuite/gdb.dap/sources.exp @@ -29,14 +29,16 @@ if {[dap_initialize] == ""} { return } -if {[dap_launch $testfile stop_at_main 1] == ""} { - return -} +set launch_id [dap_launch $testfile stop_at_main 1] -proc do_tests {} { - dap_wait_for_event_and_check "stopped at function breakpoint" stopped \ - "body reason" breakpoint +dap_check_request_and_response "configurationDone" configurationDone +dap_check_response "launch response" launch $launch_id + +dap_wait_for_event_and_check "stopped at function breakpoint" stopped \ + "body reason" breakpoint + +proc do_tests {} { set obj [dap_check_request_and_response loadedSources loadedSources] if { $obj == "" } { return diff --git a/gdb/testsuite/gdb.dap/stack-format.exp b/gdb/testsuite/gdb.dap/stack-format.exp index b81183a016e..4056e1f1f68 100644 --- a/gdb/testsuite/gdb.dap/stack-format.exp +++ b/gdb/testsuite/gdb.dap/stack-format.exp @@ -36,6 +36,8 @@ save_vars GDBFLAGS { } } +set launch_id [dap_launch $testfile] + set line [gdb_get_line_number "BREAK"] set obj [dap_check_request_and_response "set breakpoint by line number" \ setBreakpoints \ @@ -45,9 +47,8 @@ set line_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ "body reason" breakpoint \ "body hitBreakpointIds" $line_bpno diff --git a/gdb/testsuite/gdb.dap/step-out.exp b/gdb/testsuite/gdb.dap/step-out.exp index 193264fa250..cfe730e03f1 100644 --- a/gdb/testsuite/gdb.dap/step-out.exp +++ b/gdb/testsuite/gdb.dap/step-out.exp @@ -29,6 +29,8 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + set obj [dap_check_request_and_response "set breakpoint on function" \ setFunctionBreakpoints \ {o breakpoints [a [o name [s function_breakpoint_here]]]}] @@ -36,9 +38,8 @@ set fn_bpno [dap_get_breakpoint_number $obj] dap_check_request_and_response "configurationDone" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "stopped at function breakpoint" stopped \ diff --git a/gdb/testsuite/gdb.dap/stop-at-main.exp b/gdb/testsuite/gdb.dap/stop-at-main.exp index 4c3e57a23e4..52b94f40c67 100644 --- a/gdb/testsuite/gdb.dap/stop-at-main.exp +++ b/gdb/testsuite/gdb.dap/stop-at-main.exp @@ -29,11 +29,12 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile stop_at_main 1] + dap_check_request_and_response "start inferior" configurationDone -if {[dap_launch $testfile stop_at_main 1] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + # We didn't explicitly set a breakpoint, so if we hit one, it worked. dap_wait_for_event_and_check "stopped at function breakpoint" stopped \ "body reason" breakpoint diff --git a/gdb/testsuite/gdb.dap/terminate.exp b/gdb/testsuite/gdb.dap/terminate.exp index 90d01945a64..9872351a292 100644 --- a/gdb/testsuite/gdb.dap/terminate.exp +++ b/gdb/testsuite/gdb.dap/terminate.exp @@ -31,11 +31,12 @@ if {[dap_initialize] == ""} { return } +set launch_id [dap_launch $testfile] + dap_check_request_and_response "start inferior" configurationDone -if {[dap_launch $testfile] == ""} { - return -} +dap_check_response "launch response" launch $launch_id + dap_wait_for_event_and_check "inferior started" thread "body reason" started dap_wait_for_event_and_check "terminated event" terminated diff --git a/gdb/testsuite/lib/dap-support.exp b/gdb/testsuite/lib/dap-support.exp index 61355b56fee..0e481ac083d 100644 --- a/gdb/testsuite/lib/dap-support.exp +++ b/gdb/testsuite/lib/dap-support.exp @@ -248,10 +248,10 @@ proc dap_request_and_response {command {obj {}}} { return [dap_read_response $command $seq] } -# Like dap_request_and_response, but also checks that the response -# indicates success. NAME is used to issue a test result. -proc dap_check_request_and_response {name command {obj {}}} { - set response_and_events [dap_request_and_response $command $obj] +# Wait for a response to the given request, and issue a pass/fail. +# Returns the response and events like dap_request_and_response. +proc dap_check_response {name cmd request} { + set response_and_events [dap_read_response $cmd $request] set response [lindex $response_and_events 0] if {[dict get $response success] != "true"} { verbose "request failure: $response" @@ -262,6 +262,13 @@ proc dap_check_request_and_response {name command {obj {}}} { return $response_and_events } +# Like dap_request_and_response, but also checks that the response +# indicates success. NAME is used to issue a test result. +proc dap_check_request_and_response {name command {obj {}}} { + set seq [dap_send_request $command $obj] + return [dap_check_response $name $command $seq] +} + # Start gdb, send a DAP initialization request and return the # response. This approach lets the caller check the feature list, if # desired. Returns the empty string on failure. NAME is used as the @@ -278,10 +285,9 @@ proc dap_initialize {{name "initialize"}} { } # Send a launch request specifying FILE as the program to use for the -# inferior. Returns the empty string on failure, or the response -# object from the launch request. If specified, ARGS is a dictionary -# of key-value pairs, each passed to the launch request. Valid keys -# are: +# inferior. Returns the request ID. If specified, ARGS is a +# dictionary of key-value pairs, each passed to the launch request. +# Valid keys are: # # * arguments - value is a list of strings passed as command-line # arguments to the inferior @@ -334,12 +340,12 @@ proc dap_launch {file {args {}}} { } } - return [dap_check_request_and_response "startup - launch" launch $params] + return [dap_send_request launch $params] } # Start gdb, send a DAP initialize request, and then an attach request # specifying PID as the inferior process ID. Returns the empty string -# on failure, or the response object from the attach request. +# on failure, or the attach request sequence ID. proc dap_attach {pid {prog ""}} { if {[dap_initialize "startup - initialize"] == ""} { return "" @@ -350,18 +356,17 @@ proc dap_attach {pid {prog ""}} { append args [format { program [s %s]} $prog] } - return [dap_check_request_and_response "startup - attach" attach $args] + return [dap_send_request attach $args] } # Start gdb, send a DAP initialize request, and then an attach request # specifying TARGET as the remote target. Returns the empty string on -# failure, or the response object from the attach request. +# failure, or the attach request sequence ID. proc dap_target_remote {target} { if {[dap_initialize "startup - initialize"] == ""} { return "" } - return [dap_check_request_and_response "startup - target" attach \ - [format {o target [s %s]} $target]] + return [dap_send_request attach [format {o target [s %s]} $target]] } # Read the most recent DAP log file and check it for exceptions.