[3/3] Fix DAP launch and configurationDone requests

Message ID 20240124-dap-launch-fix-v1-3-0d47f87fa9f0@adacore.com
State New
Headers
Series Fix ordering of configurationDone and launch |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 fail Testing failed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Testing passed

Commit Message

Tom Tromey Jan. 24, 2024, 3:48 p.m. UTC
  Co-workers at AdaCore pointed out that gdb incorrectly implements the
DAP launch and configurationDone requests.  It's somewhat strange to
me, but the spec does in fact say that configuration requests should
occur before the executable is known to gdb.  This was clarified in
this bug report against the spec:

    https://github.com/microsoft/debug-adapter-protocol/issues/452

Fixing 'launch' to start the inferior was straightforward, but this
then required some changes to how breakpoints are handled.  In
particular, now gdb will emit the "pending" reason on a breakpoint,
and will suppress breakpoint events during breakpoint setting.
---
 gdb/python/lib/gdb/dap/breakpoint.py      | 132 +++++++++++++++---------------
 gdb/python/lib/gdb/dap/launch.py          |  29 ++-----
 gdb/testsuite/gdb.dap/ada-arrays.exp      |   8 +-
 gdb/testsuite/gdb.dap/ada-nested.exp      |   7 +-
 gdb/testsuite/gdb.dap/ada-scopes.exp      |   7 +-
 gdb/testsuite/gdb.dap/args-env.exp        |   8 +-
 gdb/testsuite/gdb.dap/assign.exp          |   7 +-
 gdb/testsuite/gdb.dap/basic-dap.exp       |  63 +++++++-------
 gdb/testsuite/gdb.dap/bt-nodebug.exp      |   8 +-
 gdb/testsuite/gdb.dap/catch-exception.exp |   7 +-
 gdb/testsuite/gdb.dap/children.exp        |   7 +-
 gdb/testsuite/gdb.dap/cond-bp.exp         |  16 +++-
 gdb/testsuite/gdb.dap/cwd.exp             |   7 +-
 gdb/testsuite/gdb.dap/cxx-exception.exp   |   7 +-
 gdb/testsuite/gdb.dap/eof.exp             |   2 +-
 gdb/testsuite/gdb.dap/frameless.exp       |   9 +-
 gdb/testsuite/gdb.dap/hover.exp           |   8 +-
 gdb/testsuite/gdb.dap/lazy-string.exp     |   7 +-
 gdb/testsuite/gdb.dap/log-message.exp     |   8 +-
 gdb/testsuite/gdb.dap/memory.exp          |   8 +-
 gdb/testsuite/gdb.dap/modules.exp         |   7 +-
 gdb/testsuite/gdb.dap/pause.exp           |   8 +-
 gdb/testsuite/gdb.dap/ptrref.exp          |   8 +-
 gdb/testsuite/gdb.dap/rust-slices.exp     |   8 +-
 gdb/testsuite/gdb.dap/scopes.exp          |   8 +-
 gdb/testsuite/gdb.dap/sources.exp         |   4 +
 gdb/testsuite/gdb.dap/stack-format.exp    |   7 +-
 gdb/testsuite/gdb.dap/stop-at-main.exp    |   6 +-
 gdb/testsuite/gdb.dap/terminate.exp       |   6 +-
 gdb/testsuite/lib/dap-support.exp         |  21 ++---
 30 files changed, 256 insertions(+), 182 deletions(-)
  

Patch

diff --git a/gdb/python/lib/gdb/dap/breakpoint.py b/gdb/python/lib/gdb/dap/breakpoint.py
index 87e746472fb..dd6f6fb1827 100644
--- a/gdb/python/lib/gdb/dap/breakpoint.py
+++ b/gdb/python/lib/gdb/dap/breakpoint.py
@@ -28,17 +28,6 @@  from .startup import in_gdb_thread, log_stack, parse_and_eval, LogLevel, DAPExce
 from .typecheck import type_check
 
 
-@in_gdb_thread
-def _bp_modified(event):
-    send_event(
-        "breakpoint",
-        {
-            "reason": "changed",
-            "breakpoint": _breakpoint_descriptor(event),
-        },
-    )
-
-
 # True when suppressing new breakpoint events.
 _suppress_bp = False
 
@@ -55,6 +44,19 @@  def suppress_new_breakpoint_event():
         _suppress_bp = saved
 
 
+@in_gdb_thread
+def _bp_modified(event):
+    global _suppress_bp
+    if not _suppress_bp:
+        send_event(
+            "breakpoint",
+            {
+                "reason": "changed",
+                "breakpoint": _breakpoint_descriptor(event),
+            },
+        )
+
+
 @in_gdb_thread
 def _bp_created(event):
     global _suppress_bp
@@ -70,13 +72,15 @@  def _bp_created(event):
 
 @in_gdb_thread
 def _bp_deleted(event):
-    send_event(
-        "breakpoint",
-        {
-            "reason": "removed",
-            "breakpoint": _breakpoint_descriptor(event),
-        },
-    )
+    global _suppress_bp
+    if not _suppress_bp:
+        send_event(
+            "breakpoint",
+            {
+                "reason": "removed",
+                "breakpoint": _breakpoint_descriptor(event),
+            },
+        )
 
 
 gdb.events.breakpoint_created.connect(_bp_created)
@@ -97,11 +101,10 @@  def _breakpoint_descriptor(bp):
     "Return the Breakpoint object descriptor given a gdb Breakpoint."
     result = {
         "id": bp.number,
-        # We always use True here, because this field just indicates
-        # that breakpoint creation was successful -- and if we have a
-        # breakpoint, the creation succeeded.
-        "verified": True,
+        "verified": not bp.pending,
     }
+    if bp.pending:
+        result["reason"] = "pending"
     if bp.locations:
         # Just choose the first location, because DAP doesn't allow
         # multiple locations.  See
@@ -146,52 +149,53 @@  def _set_breakpoints_callback(kind, specs, creator):
         saved_map = {}
     breakpoint_map[kind] = {}
     result = []
-    for spec in specs:
-        # It makes sense to reuse a breakpoint even if the condition
-        # or ignore count differs, so remove these entries from the
-        # spec first.
-        (condition, hit_condition) = _remove_entries(spec, "condition", "hitCondition")
-        keyspec = frozenset(spec.items())
-
-        # Create or reuse a breakpoint.  If asked, set the condition
-        # or the ignore count.  Catch errors coming from gdb and
-        # report these as an "unverified" breakpoint.
-        bp = None
-        try:
-            if keyspec in saved_map:
-                bp = saved_map.pop(keyspec)
-            else:
-                with suppress_new_breakpoint_event():
+    with suppress_new_breakpoint_event():
+        for spec in specs:
+            # It makes sense to reuse a breakpoint even if the condition
+            # or ignore count differs, so remove these entries from the
+            # spec first.
+            (condition, hit_condition) = _remove_entries(spec, "condition", "hitCondition")
+            keyspec = frozenset(spec.items())
+
+            # Create or reuse a breakpoint.  If asked, set the condition
+            # or the ignore count.  Catch errors coming from gdb and
+            # report these as an "unverified" breakpoint.
+            bp = None
+            try:
+                if keyspec in saved_map:
+                    bp = saved_map.pop(keyspec)
+                else:
                     bp = creator(**spec)
 
-            bp.condition = condition
-            if hit_condition is None:
-                bp.ignore_count = 0
-            else:
-                bp.ignore_count = int(
-                    parse_and_eval(hit_condition, global_context=True)
+                bp.condition = condition
+                if hit_condition is None:
+                    bp.ignore_count = 0
+                else:
+                    bp.ignore_count = int(
+                        parse_and_eval(hit_condition, global_context=True)
+                    )
+
+                # Reaching this spot means success.
+                breakpoint_map[kind][keyspec] = bp
+                result.append(_breakpoint_descriptor(bp))
+            # Exceptions other than gdb.error are possible here.
+            except Exception as e:
+                # Don't normally want to see this, as it interferes with
+                # the test suite.
+                log_stack(LogLevel.FULL)
+                # Maybe the breakpoint was made but setting an attribute
+                # failed.  We still want this to fail.
+                if bp is not None:
+                    bp.delete()
+                # Breakpoint creation failed.
+                result.append(
+                    {
+                        "verified": False,
+                        "reason": "failed",
+                        "message": str(e),
+                    }
                 )
 
-            # Reaching this spot means success.
-            breakpoint_map[kind][keyspec] = bp
-            result.append(_breakpoint_descriptor(bp))
-        # Exceptions other than gdb.error are possible here.
-        except Exception as e:
-            # Don't normally want to see this, as it interferes with
-            # the test suite.
-            log_stack(LogLevel.FULL)
-            # Maybe the breakpoint was made but setting an attribute
-            # failed.  We still want this to fail.
-            if bp is not None:
-                bp.delete()
-            # Breakpoint creation failed.
-            result.append(
-                {
-                    "verified": False,
-                    "message": str(e),
-                }
-            )
-
     # Delete any breakpoints that were not reused.
     for entry in saved_map.values():
         entry.delete()
diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py
index bf9640a4d92..19e8faa6704 100644
--- a/gdb/python/lib/gdb/dap/launch.py
+++ b/gdb/python/lib/gdb/dap/launch.py
@@ -23,16 +23,6 @@  from .server import request, capability
 from .startup import exec_and_log, DAPException
 
 
-# The program being launched, or None.  This should only be accessed
-# from the gdb thread.
-_program = None
-
-
-# True if the program was attached, False otherwise.  This should only
-# be accessed from the gdb thread.
-_attach = False
-
-
 # Any parameters here are necessarily extensions -- DAP requires this
 # from implementations.  Any additions or changes here should be
 # documented in the gdb manual.
@@ -46,10 +36,6 @@  def launch(
     stopAtBeginningOfMainSubprogram: bool = False,
     **extra,
 ):
-    global _program
-    _program = program
-    global _attach
-    _attach = False
     if cwd is not None:
         exec_and_log("cd " + cwd)
     if program is not None:
@@ -64,6 +50,8 @@  def launch(
         inf.clear_env()
         for name, value in env.items():
             inf.set_env(name, value)
+    expect_process("process")
+    exec_and_expect_stop("run")
 
 
 @request("attach")
@@ -74,11 +62,6 @@  def attach(
     target: Optional[str] = None,
     **args,
 ):
-    # Ensure configurationDone does not try to run.
-    global _attach
-    _attach = True
-    global _program
-    _program = program
     if program is not None:
         exec_and_log("file " + program)
     if pid is not None:
@@ -93,9 +76,7 @@  def attach(
 
 
 @capability("supportsConfigurationDoneRequest")
-@request("configurationDone", response=False)
+@request("configurationDone")
 def config_done(**args):
-    global _attach
-    if not _attach:
-        expect_process("process")
-        exec_and_expect_stop("run")
+    # Nothing to do.
+    return None
diff --git a/gdb/testsuite/gdb.dap/ada-arrays.exp b/gdb/testsuite/gdb.dap/ada-arrays.exp
index 7928aa19424..0de361f96bd 100644
--- a/gdb/testsuite/gdb.dap/ada-arrays.exp
+++ b/gdb/testsuite/gdb.dap/ada-arrays.exp
@@ -29,7 +29,7 @@  if {[gdb_compile_ada "${srcfile}" "${binfile}" executable debug] != ""} {
   return -1
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -42,7 +42,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s cstuff.c] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 f543fef47ef..3415da3e9cb 100644
--- a/gdb/testsuite/gdb.dap/ada-nested.exp
+++ b/gdb/testsuite/gdb.dap/ada-nested.exp
@@ -27,7 +27,7 @@  if {[gdb_compile_ada "${srcfile}" "${binfile}" executable \
     return -1
 }
 
-if {[dap_launch $binfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -39,8 +39,11 @@  set obj [dap_check_request_and_response "set breakpoint" \
 		  [list s $srcfile] $line]]
 set fn_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $binfile] == ""} {
+    return
+}
 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 070deadb3a1..12004f8fc32 100644
--- a/gdb/testsuite/gdb.dap/ada-scopes.exp
+++ b/gdb/testsuite/gdb.dap/ada-scopes.exp
@@ -25,7 +25,7 @@  if {[gdb_compile_ada "${srcfile}" "${binfile}" executable \
     return -1
 }
 
-if {[dap_launch $binfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -37,8 +37,11 @@  set obj [dap_check_request_and_response "set breakpoint" \
 		  [list s $srcfile] $line]]
 set fn_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $binfile] == ""} {
+    return
+}
 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 0f07fd25aae..d6511733348 100644
--- a/gdb/testsuite/gdb.dap/args-env.exp
+++ b/gdb/testsuite/gdb.dap/args-env.exp
@@ -25,7 +25,7 @@  if {[build_executable ${testfile}.exp $testfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile arguments {a "b c"} env {{DEI something}}] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -36,7 +36,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile arguments {a "b c"} env {{DEI something}}] == ""} {
+    return
+}
 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 386bf033f34..6703a97bb7a 100644
--- a/gdb/testsuite/gdb.dap/assign.exp
+++ b/gdb/testsuite/gdb.dap/assign.exp
@@ -31,7 +31,7 @@  set remote_python_file [gdb_remote_download host \
 save_vars GDBFLAGS {
     append GDBFLAGS " -iex \"source $remote_python_file\""
 
-    if {[dap_launch $testfile] == ""} {
+    if {[dap_initialize] == ""} {
 	return
     }
 }
@@ -43,8 +43,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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/basic-dap.exp b/gdb/testsuite/gdb.dap/basic-dap.exp
index 431ea3d213d..6ef9a5b0f6e 100644
--- a/gdb/testsuite/gdb.dap/basic-dap.exp
+++ b/gdb/testsuite/gdb.dap/basic-dap.exp
@@ -25,7 +25,7 @@  if {[build_executable ${testfile}.exp $testfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -72,21 +72,6 @@  if {$ok} {
     fail "check lack of new breakpoint event"
 }
 
-# Check that there are breakpoint locations on each line between FIRST
-# and BREAK.
-set first_line [gdb_get_line_number "FIRST"]
-set last_line [expr {$line - 1}]
-set obj [dap_check_request_and_response "breakpoint locations" \
-	     breakpointLocations \
-	     [format {o source [o path [%s]] line [i %d] endLine [i %d]} \
-		  [list s $srcfile] $first_line $last_line]]
-# We know gdb returns the lines in sorted order.
-foreach entry [dict get [lindex $obj 0] body breakpoints] {
-    gdb_assert {[dict get $entry line] == $first_line} \
-	"line $first_line in result"
-    incr first_line
-}
-
 # Note that in this request, we add a 'source' field to the
 # SourceBreakpoint object.  This isn't in the spec but it once caused
 # an incorrect exception in the Python code.  See PR dap/30820.
@@ -99,23 +84,15 @@  set obj [dap_check_request_and_response "reset breakpoint by line number" \
 set new_line_bpno [dap_get_breakpoint_number $obj]
 gdb_assert {$new_line_bpno == $line_bpno} "re-setting kept same breakpoint number"
 
-# This uses "&address_breakpoint_here" as the address -- this is a
-# hack because we know how this is implemented under the hood.
-set obj [dap_check_request_and_response "set breakpoint by address" \
-	     setInstructionBreakpoints \
-	     {o breakpoints [a [o instructionReference [s &address_breakpoint_here]]]}]
-set insn_bpno [dap_get_breakpoint_number $obj]
-
-set response [lindex $obj 0]
-set bplist [dict get $response body breakpoints]
-set insn_pc [dict get [lindex $bplist 0] instructionReference]
+dap_check_request_and_response "configurationDone" configurationDone
 
-dap_check_request_and_response "start inferior" configurationDone
+if {[dap_launch $testfile] == ""} {
+    return
+}
 dap_wait_for_event_and_check "inferior started" thread "body reason" started
 
 # While waiting for the stopped event, we might receive breakpoint changed
-# events indicating some breakpoint addresses were relocated.  Update INSN_PC
-# if necessary.
+# events indicating some breakpoint addresses were relocated.
 lassign [dap_wait_for_event_and_check "stopped at function breakpoint" stopped \
 	    "body reason" breakpoint \
 	    "body hitBreakpointIds" $fn_bpno] unused objs
@@ -136,12 +113,32 @@  foreach obj $objs {
 
     set breakpoint [dict get $body "breakpoint"]
     set breakpoint_id [dict get $breakpoint "id"]
+}
 
-    if { $breakpoint_id != $insn_bpno } {
-	continue
-    }
+# This uses "&address_breakpoint_here" as the address -- this is a
+# hack because we know how this is implemented under the hood.
+set obj [dap_check_request_and_response "set breakpoint by address" \
+	     setInstructionBreakpoints \
+	     {o breakpoints [a [o instructionReference [s &address_breakpoint_here]]]}]
+set insn_bpno [dap_get_breakpoint_number $obj]
+
+set response [lindex $obj 0]
+set bplist [dict get $response body breakpoints]
+set insn_pc [dict get [lindex $bplist 0] instructionReference]
 
-    set insn_pc [dict get $breakpoint "instructionReference"]
+# Check that there are breakpoint locations on each line between FIRST
+# and BREAK.
+set first_line [gdb_get_line_number "FIRST"]
+set last_line [expr {$line - 1}]
+set obj [dap_check_request_and_response "breakpoint locations" \
+	     breakpointLocations \
+	     [format {o source [o path [%s]] line [i %d] endLine [i %d]} \
+		  [list s $srcfile] $first_line $last_line]]
+# We know gdb returns the lines in sorted order.
+foreach entry [dict get [lindex $obj 0] body breakpoints] {
+    gdb_assert {[dict get $entry line] == $first_line} \
+	"line $first_line in result"
+    incr first_line
 }
 
 set obj [dap_check_request_and_response "evaluate global in function" \
diff --git a/gdb/testsuite/gdb.dap/bt-nodebug.exp b/gdb/testsuite/gdb.dap/bt-nodebug.exp
index 57d4dacaedc..550b9c5c5ac 100644
--- a/gdb/testsuite/gdb.dap/bt-nodebug.exp
+++ b/gdb/testsuite/gdb.dap/bt-nodebug.exp
@@ -27,7 +27,7 @@  if {[build_executable_from_specs $testfile.exp $testfile {} \
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -36,7 +36,11 @@  set obj [dap_check_request_and_response "set breakpoint on inner" \
 	     {o breakpoints [a [o name [s function_breakpoint_here]]]}]
 set fn_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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/catch-exception.exp b/gdb/testsuite/gdb.dap/catch-exception.exp
index 8ca0a83ec71..166b862f9a7 100644
--- a/gdb/testsuite/gdb.dap/catch-exception.exp
+++ b/gdb/testsuite/gdb.dap/catch-exception.exp
@@ -25,7 +25,7 @@  if {[gdb_compile_ada "${srcfile}" "${binfile}" executable \
     return -1
 }
 
-if {[dap_launch $binfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -61,8 +61,11 @@  foreach spec $bps {
     incr i
 }
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $binfile] == ""} {
+    return
+}
 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 b2444e06fb6..f5dfe2cbb91 100644
--- a/gdb/testsuite/gdb.dap/children.exp
+++ b/gdb/testsuite/gdb.dap/children.exp
@@ -31,7 +31,7 @@  set remote_python_file [gdb_remote_download host \
 save_vars GDBFLAGS {
     append GDBFLAGS " -iex \"source $remote_python_file\""
 
-    if {[dap_launch $testfile] == ""} {
+    if {[dap_initialize] == ""} {
 	return
     }
 }
@@ -43,8 +43,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 427776a5cbb..2bd52ba83a0 100644
--- a/gdb/testsuite/gdb.dap/cond-bp.exp
+++ b/gdb/testsuite/gdb.dap/cond-bp.exp
@@ -25,7 +25,7 @@  if {[build_executable ${testfile}.exp $testfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -44,8 +44,13 @@  set i 1
 foreach bp [dict get [lindex $obj 0] body breakpoints] {
     gdb_assert {[dict get $bp verified] == "false"} \
 	"breakpoint $i invalid"
-    gdb_assert {[dict get $bp message] != ""} \
-	"breakpoint $i has message"
+    if {$i == 1} {
+	gdb_assert {[dict get $bp reason] == "pending"} \
+	    "breakpoint $i pending"
+    } else {
+	gdb_assert {[dict get $bp message] != ""} \
+	    "breakpoint $i has message"
+    }
     incr i
 }
 
@@ -58,8 +63,11 @@  set obj [dap_check_request_and_response "set conditional breakpoint" \
 		  [list s $srcfile] $line]]
 set fn_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 9e1d070435b..6b8829965c5 100644
--- a/gdb/testsuite/gdb.dap/cwd.exp
+++ b/gdb/testsuite/gdb.dap/cwd.exp
@@ -25,6 +25,12 @@  if {[build_executable ${testfile}.exp $testfile $srcfile] == -1} {
     return
 }
 
+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]
@@ -32,7 +38,6 @@  if {[dap_launch $the_file cwd $the_dir stop_at_main 1] == ""} {
     return
 }
 
-dap_check_request_and_response "start inferior" configurationDone
 # 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/cxx-exception.exp b/gdb/testsuite/gdb.dap/cxx-exception.exp
index 284aef40009..b54b11a2c9a 100644
--- a/gdb/testsuite/gdb.dap/cxx-exception.exp
+++ b/gdb/testsuite/gdb.dap/cxx-exception.exp
@@ -24,7 +24,7 @@  if {[build_executable ${testfile}.exp $testfile $srcfile {debug c++}] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -37,8 +37,11 @@  set bps [dict get [lindex $obj 0] body breakpoints]
 # breakpoints.
 gdb_assert {[llength $bps] == 3} "three breakpoints"
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $testfile] == ""} {
+    return
+}
 dap_wait_for_event_and_check "stopped at throw" stopped \
     "body reason" breakpoint \
     "body hitBreakpointIds" 1
diff --git a/gdb/testsuite/gdb.dap/eof.exp b/gdb/testsuite/gdb.dap/eof.exp
index 139c17ad335..9c17725c0d0 100644
--- a/gdb/testsuite/gdb.dap/eof.exp
+++ b/gdb/testsuite/gdb.dap/eof.exp
@@ -26,7 +26,7 @@  if {[build_executable ${testfile}.exp $testfile $srcfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
diff --git a/gdb/testsuite/gdb.dap/frameless.exp b/gdb/testsuite/gdb.dap/frameless.exp
index 9d25fc5902d..63ee521af65 100644
--- a/gdb/testsuite/gdb.dap/frameless.exp
+++ b/gdb/testsuite/gdb.dap/frameless.exp
@@ -25,7 +25,7 @@  if {[build_executable ${testfile}.exp $testfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -36,9 +36,12 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
-dap_wait_for_event_and_check "inferior started" thread "body reason" started
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $testfile] == ""} {
+    return
+}
+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 \
     "body hitBreakpointIds" $line_bpno
diff --git a/gdb/testsuite/gdb.dap/hover.exp b/gdb/testsuite/gdb.dap/hover.exp
index f7b9fd702ad..0c80650b152 100644
--- a/gdb/testsuite/gdb.dap/hover.exp
+++ b/gdb/testsuite/gdb.dap/hover.exp
@@ -25,7 +25,7 @@  if {[build_executable ${testfile}.exp $testfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -36,7 +36,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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/lazy-string.exp b/gdb/testsuite/gdb.dap/lazy-string.exp
index 0249f142a6e..54422209fda 100644
--- a/gdb/testsuite/gdb.dap/lazy-string.exp
+++ b/gdb/testsuite/gdb.dap/lazy-string.exp
@@ -31,7 +31,7 @@  set remote_python_file [gdb_remote_download host \
 save_vars GDBFLAGS {
     append GDBFLAGS " -iex \"source $remote_python_file\""
 
-    if {[dap_launch $testfile] == ""} {
+    if {[dap_initialize] == ""} {
 	return
     }
 }
@@ -43,8 +43,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 4e3ecb7807b..e966b962a71 100644
--- a/gdb/testsuite/gdb.dap/log-message.exp
+++ b/gdb/testsuite/gdb.dap/log-message.exp
@@ -25,7 +25,7 @@  if {[build_executable ${testfile}.exp $testfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -38,7 +38,11 @@  set obj [dap_check_request_and_response "set breakpoint" \
 		  [list s $srcfile] $line]]
 set fn_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 481ab628be9..2e911f4dc77 100644
--- a/gdb/testsuite/gdb.dap/memory.exp
+++ b/gdb/testsuite/gdb.dap/memory.exp
@@ -25,7 +25,7 @@  if {[build_executable ${testfile}.exp $testfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -36,7 +36,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 4d53b90fa96..87cebda7131 100644
--- a/gdb/testsuite/gdb.dap/modules.exp
+++ b/gdb/testsuite/gdb.dap/modules.exp
@@ -38,7 +38,7 @@  if {[build_executable ${testfile}.exp $testfile $srcfile \
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -47,8 +47,11 @@  set obj [dap_check_request_and_response "set breakpoint on stop function" \
 	     {o breakpoints [a [o name [s stop]]]}]
 set fn_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 e1e0d957fc6..4d13dadd3ad 100644
--- a/gdb/testsuite/gdb.dap/pause.exp
+++ b/gdb/testsuite/gdb.dap/pause.exp
@@ -25,7 +25,7 @@  if {[build_executable ${testfile}.exp $testfile $srcfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -41,7 +41,11 @@  dap_check_request_and_response "set conditional breakpoint" \
 				     condition [s "return_false()"]]]} \
 	 [list s $srcfile] $line]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 bcdbc5bb56c..0552c3b9815 100644
--- a/gdb/testsuite/gdb.dap/ptrref.exp
+++ b/gdb/testsuite/gdb.dap/ptrref.exp
@@ -23,7 +23,7 @@  if {[build_executable ${testfile}.exp $testfile $srcfile {debug c++}] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -34,7 +34,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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/rust-slices.exp b/gdb/testsuite/gdb.dap/rust-slices.exp
index 8a8c79c7088..c85568d69ea 100644
--- a/gdb/testsuite/gdb.dap/rust-slices.exp
+++ b/gdb/testsuite/gdb.dap/rust-slices.exp
@@ -28,7 +28,7 @@  if {[build_executable ${testfile}.exp $testfile $srcfile {debug rust}] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -39,7 +39,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 0b0727cd875..aa3bb688c0e 100644
--- a/gdb/testsuite/gdb.dap/scopes.exp
+++ b/gdb/testsuite/gdb.dap/scopes.exp
@@ -25,7 +25,7 @@  if {[build_executable ${testfile}.exp $testfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
@@ -36,7 +36,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 670084e56cf..b60681160af 100644
--- a/gdb/testsuite/gdb.dap/sources.exp
+++ b/gdb/testsuite/gdb.dap/sources.exp
@@ -25,6 +25,10 @@  if {[build_executable ${testfile}.exp $testfile] == -1} {
     return
 }
 
+if {[dap_initialize] == ""} {
+    return
+}
+
 if {[dap_launch $testfile stop_at_main 1] == ""} {
     return
 }
diff --git a/gdb/testsuite/gdb.dap/stack-format.exp b/gdb/testsuite/gdb.dap/stack-format.exp
index af9d6d02efa..b81183a016e 100644
--- a/gdb/testsuite/gdb.dap/stack-format.exp
+++ b/gdb/testsuite/gdb.dap/stack-format.exp
@@ -31,7 +31,7 @@  set remote_python_file [gdb_remote_download host \
 save_vars GDBFLAGS {
     append GDBFLAGS " -iex \"source $remote_python_file\""
 
-    if {[dap_launch $testfile] == ""} {
+    if {[dap_initialize] == ""} {
 	return
     }
 }
@@ -43,8 +43,11 @@  set obj [dap_check_request_and_response "set breakpoint by line number" \
 		  [list s $srcfile] $line]]
 set line_bpno [dap_get_breakpoint_number $obj]
 
-dap_check_request_and_response "start inferior" configurationDone
+dap_check_request_and_response "configurationDone" configurationDone
 
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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/stop-at-main.exp b/gdb/testsuite/gdb.dap/stop-at-main.exp
index f7eb9ac7a0d..4c3e57a23e4 100644
--- a/gdb/testsuite/gdb.dap/stop-at-main.exp
+++ b/gdb/testsuite/gdb.dap/stop-at-main.exp
@@ -25,11 +25,15 @@  if {[build_executable ${testfile}.exp $testfile $srcfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile stop_at_main 1] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
 dap_check_request_and_response "start inferior" configurationDone
+
+if {[dap_launch $testfile stop_at_main 1] == ""} {
+    return
+}
 # 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 9c3706447dc..90d01945a64 100644
--- a/gdb/testsuite/gdb.dap/terminate.exp
+++ b/gdb/testsuite/gdb.dap/terminate.exp
@@ -27,11 +27,15 @@  if {[build_executable ${testfile}.exp $testfile $srcfile] == -1} {
     return
 }
 
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
     return
 }
 
 dap_check_request_and_response "start inferior" configurationDone
+
+if {[dap_launch $testfile] == ""} {
+    return
+}
 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 07259862b18..979dfa2cd73 100644
--- a/gdb/testsuite/lib/dap-support.exp
+++ b/gdb/testsuite/lib/dap-support.exp
@@ -235,10 +235,9 @@  proc dap_check_request_and_response {name command {obj {}}} {
 
 # Start gdb, send a DAP initialization request and return the
 # response.  This approach lets the caller check the feature list, if
-# desired.  Callers not caring about this should probably use
-# dap_launch.  Returns the empty string on failure.  NAME is used as
-# the test name.
-proc dap_initialize {name} {
+# desired.  Returns the empty string on failure.  NAME is used as the
+# test name.
+proc dap_initialize {{name "initialize"}} {
     if {[dap_gdb_start]} {
 	return ""
     }
@@ -249,11 +248,12 @@  proc dap_initialize {name} {
 		     supportsMemoryReferences [l true]}]
 }
 
-# Start gdb, send a DAP initialize request, and then 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:
+# 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:
+#
 # * arguments - value is a list of strings passed as command-line
 #   arguments to the inferior
 # * env - value is a list of pairs of the form {VAR VALUE} that is
@@ -266,9 +266,6 @@  proc dap_initialize {name} {
 # After this proc is called, gdb will be ready to accept breakpoint
 # requests.
 proc dap_launch {file {args {}}} {
-    if {[dap_initialize "startup - initialize"] == ""} {
-	return ""
-    }
     set params "o program"
     append params " [format {[%s]} [list s [standard_output_file $file]]]"