| # Copyright 2022-2025 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/>. |
| |
| # Basic DAP test. |
| |
| require allow_dap_tests |
| |
| load_lib dap-support.exp |
| |
| standard_testfile |
| |
| if {[build_executable ${testfile}.exp $testfile] == -1} { |
| return |
| } |
| |
| 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]] \ |
| [o name [s do_not_stop_here]]]}] |
| set fn_bpno [dap_get_breakpoint_number $obj] |
| |
| # This also tests that the previous do_not_stop_here breakpoint is |
| # cleared. |
| set obj [dap_check_request_and_response "set breakpoint on function" \ |
| setFunctionBreakpoints \ |
| {o breakpoints [a [o name [s function_breakpoint_here]]]}] |
| set fn_bpno [dap_get_breakpoint_number $obj] |
| |
| set obj [dap_check_request_and_response "set breakpoint with invalid filename" \ |
| setBreakpoints \ |
| [format {o source [o path [s nosuchfilename.c]] breakpoints [a [o line [i 29]]]}]] |
| |
| set line [gdb_get_line_number "BREAK"] |
| set obj [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 line_bpno [dap_get_breakpoint_number $obj] |
| |
| # Check the new breakpoint event. |
| set ok 1 |
| foreach d [lindex $obj 1] { |
| if {[dict get $d type] != "event" |
| || [dict get $d event] != "breakpoint"} { |
| continue |
| } |
| if {[dict get $d body reason] == "new" |
| && [dict get $d body breakpoint verified] == "true"} { |
| set ok 0 |
| break |
| } |
| } |
| if {$ok} { |
| pass "check lack of new breakpoint event" |
| } else { |
| fail "check lack of new breakpoint event" |
| } |
| |
| # 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 obj [dap_check_request_and_response "reset breakpoint by line number" \ |
| setBreakpoints \ |
| [format {o source [o path [%s]] \ |
| breakpoints [a [o source [o path [%s]] \ |
| line [i %d]]]} \ |
| [list s $srcfile] [list s $srcfile] $line]] |
| set new_line_bpno [dap_get_breakpoint_number $obj] |
| gdb_assert {$new_line_bpno == $line_bpno} "re-setting kept same breakpoint number" |
| |
| dap_check_request_and_response "configurationDone" configurationDone |
| |
| 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 |
| # 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 |
| foreach obj $objs { |
| if { [dict get $obj "type"] != "event" } { |
| continue |
| } |
| |
| if { [dict get $obj "event"] != "breakpoint" } { |
| continue |
| } |
| |
| set body [dict get $obj "body"] |
| |
| if { [dict get $body "reason"] != "changed" } { |
| continue |
| } |
| |
| set breakpoint [dict get $body "breakpoint"] |
| set breakpoint_id [dict get $breakpoint "id"] |
| } |
| |
| # 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] |
| |
| # 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" \ |
| evaluate {o expression [s global_variable]}] |
| dap_match_values "global value in function" [lindex $obj 0] \ |
| "body result" 23 |
| |
| dap_check_request_and_response step stepIn {o threadId [i 1]} |
| dap_wait_for_event_and_check "stopped after step" stopped "body reason" step |
| |
| set obj [dap_check_request_and_response "evaluate global second time" \ |
| evaluate {o expression [s global_variable]}] |
| dap_match_values "global value after step" [lindex $obj 0] \ |
| "body result" 24 |
| |
| dap_check_request_and_response "continue to address" continue \ |
| {o threadId [i 1]} |
| dap_wait_for_event_and_check "stopped at address breakpoint" stopped \ |
| "body reason" breakpoint \ |
| "body hitBreakpointIds" $insn_bpno |
| |
| dap_check_request_and_response "continue to line" continue \ |
| {o threadId [i 1]} |
| dap_wait_for_event_and_check "stopped at line breakpoint" stopped \ |
| "body reason" breakpoint \ |
| "body hitBreakpointIds" $line_bpno \ |
| "body allThreadsStopped" true |
| |
| dap_check_request_and_response "return from function" stepOut \ |
| {o threadId [i 1]} |
| dap_wait_for_event_and_check "stopped after return" stopped \ |
| "body reason" step |
| |
| set obj [dap_check_request_and_response "evaluate global in main" \ |
| evaluate {o expression [s global_variable]}] |
| dap_match_values "global value in main" [lindex $obj 0] \ |
| "body result" 25 |
| |
| set obj [dap_check_request_and_response "set global in main" \ |
| setExpression {o expression [s global_variable] value [s 23]}] |
| dap_match_values "global value in main after set" [lindex $obj 0] \ |
| "body value" 23 \ |
| "body type" int |
| |
| set obj [dap_request_and_response \ |
| evaluate {o expression [s nosuchvariable]}] |
| set response [lindex $obj 0] |
| gdb_assert { [dict get $response success] == "false" } "result of invalid request" |
| |
| set obj [dap_check_request_and_response "disassemble one instruction" \ |
| disassemble \ |
| [format {o memoryReference [s %s] instructionCount [i 1]} \ |
| $insn_pc]] |
| set response [lindex $obj 0] |
| gdb_assert { [dict exists $response body instructions] } "instructions in disassemble output" |
| foreach insn [dict get $response body instructions] { |
| gdb_assert {[dict exists $insn instructionBytes]} \ |
| "instruction bytes in disassemble output" |
| set bytes [dict get $insn instructionBytes] |
| gdb_assert {[string length $bytes] % 2 == 0} \ |
| "even number of digits" |
| gdb_assert {[regexp "^\[0-9A-Fa-f\]+\$" $bytes]} \ |
| "instructionBytes is hex" |
| } |
| |
| set obj [dap_check_request_and_response "command repl" \ |
| evaluate {o expression [s {output 23}] context [s repl]}] |
| set response [lindex $obj 0] |
| gdb_assert {[dict get $response body result] == 23} |
| |
| dap_shutdown |