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
_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
@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)
"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
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()
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.
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:
inf.clear_env()
for name, value in env.items():
inf.set_env(name, value)
+ expect_process("process")
+ exec_and_expect_stop("run")
@request("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:
@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
return -1
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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 \
return -1
}
-if {[dap_launch $binfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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
return -1
}
-if {[dap_launch $binfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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
return
}
-if {[dap_launch $testfile arguments {a "b c"} env {{DEI something}}] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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 \
save_vars GDBFLAGS {
append GDBFLAGS " -iex \"source $remote_python_file\""
- if {[dap_launch $testfile] == ""} {
+ if {[dap_initialize] == ""} {
return
}
}
[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
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
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.
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
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" \
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
{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 \
return -1
}
-if {[dap_launch $binfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
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
save_vars GDBFLAGS {
append GDBFLAGS " -iex \"source $remote_python_file\""
- if {[dap_launch $testfile] == ""} {
+ if {[dap_initialize] == ""} {
return
}
}
[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
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
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
}
[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
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]
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
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
# 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
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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 \
save_vars GDBFLAGS {
append GDBFLAGS " -iex \"source $remote_python_file\""
- if {[dap_launch $testfile] == ""} {
+ if {[dap_initialize] == ""} {
return
}
}
[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
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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 \
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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 \
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
{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
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
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
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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 \
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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 \
return
}
-if {[dap_launch $testfile] == ""} {
+if {[dap_initialize] == ""} {
return
}
[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 \
return
}
+if {[dap_initialize] == ""} {
+ return
+}
+
if {[dap_launch $testfile stop_at_main 1] == ""} {
return
}
save_vars GDBFLAGS {
append GDBFLAGS " -iex \"source $remote_python_file\""
- if {[dap_launch $testfile] == ""} {
+ if {[dap_initialize] == ""} {
return
}
}
[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
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
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
# 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 ""
}
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
# 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]]]"