forked from Imagelibrary/binutils-gdb
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:
6
gdb/NEWS
6
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
60
gdb/testsuite/gdb.dap/cancel-launch.exp
Normal file
60
gdb/testsuite/gdb.dap/cancel-launch.exp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user