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 <legouguec@adacore.com>
This commit is contained in:
Tom Tromey
2024-11-20 13:04:27 -07:00
parent 4baa278955
commit 6b9efd5c1a
36 changed files with 338 additions and 161 deletions

View File

@@ -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

View File

@@ -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)
# 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")
@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)

View File

@@ -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 \

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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

View File

@@ -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 attach_id [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"
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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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 <http://www.gnu.org/licenses/>.
# 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -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]
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 {} {
dap_wait_for_event_and_check "stopped at function breakpoint" stopped \
"body reason" breakpoint
set obj [dap_check_request_and_response loadedSources loadedSources]
if { $obj == "" } {
return

View File

@@ -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

View File

@@ -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 \

View File

@@ -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

View File

@@ -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

View File

@@ -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.