1 # Copyright 2022-2023 Free Software Foundation, Inc.
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 # The sequence number for the next DAP request. This is used by the
20 # automatic sequence-counting code below. It is reset each time GDB
24 # Start gdb using the DAP interpreter.
25 proc dap_gdb_start {} {
26 # Keep track of the number of times GDB has been launched.
32 global GDBFLAGS stty_init
33 save_vars { GDBFLAGS stty_init } {
34 set stty_init "-echo raw"
35 set logfile [standard_output_file "dap.log.$gdb_instances"]
36 append GDBFLAGS " -iex \"set debug dap-log-file $logfile\" -q -i=dap"
49 # A helper for dap_to_ton that decides if the list L is a JSON object
50 # or if it is an array.
51 proc _dap_is_obj {l} {
52 if {[llength $l] % 2 != 0} {
55 foreach {key value} $l {
56 if {![string is alpha $key]} {
63 # The "TON" format is a bit of a pain to write by hand, so this proc
64 # can be used to convert an ordinary Tcl list into TON by guessing at
65 # the correct forms to use. This can't be used in all cases, because
66 # Tcl can't really differentiate between literal forms. For example,
67 # there's no way to decide if "true" should be a string or the literal
70 # JSON objects must be passed in a particular form here -- as a list
71 # with an even number of elements, alternating keys and values. Each
72 # key must consist only of letters, no digits or other non-letter
73 # characters. Note that this is compatible with the Tcl 'dict'
75 proc dap_to_ton {obj} {
76 if {[string is list $obj] && [llength $obj] > 1} {
77 if {[_dap_is_obj $obj]} {
79 foreach {key value} $obj {
80 lappend result $key \[[dap_to_ton $value]\]
85 lappend result \[[dap_to_ton $val]\]
88 } elseif {[string is entier $obj]} {
89 set result [list i $obj]
90 } elseif {[string is double $obj]} {
91 set result [list d $obj]
92 } elseif {$obj == "true" || $obj == "false" || $obj == "null"} {
93 set result [list l $obj]
95 set result [list s $obj]
100 # Format the object OBJ, in TON format, as JSON and send it to gdb.
101 proc _dap_send_ton {obj} {
102 set json [namespace eval ton::2json $obj]
103 # FIXME this is wrong for non-ASCII characters.
104 set len [string length $json]
105 verbose -log ">>> $json"
106 send_gdb "Content-Length: $len\r\n\r\n$json"
109 # Send a DAP request to gdb. COMMAND is the request's "command"
110 # field, and OBJ is the "arguments" field. If OBJ is empty, it is
111 # omitted. The sequence number of the request is automatically added,
112 # and this is also the return value. OBJ is assumed to already be in
114 proc _dap_send_request {command {obj {}}} {
115 # We can construct this directly as a TON object.
116 set result $::dap_seq
118 set req [format {o seq [i %d] type [s request] command [%s]} \
119 $result [list s $command]]
121 append req " arguments \[$obj\]"
127 # Read a JSON response from gdb. This will return a dict on
128 # success, or throw an exception on error.
129 proc _dap_read_json {} {
132 -re "^Content-Length: (\[0-9\]+)\r\n" {
133 set length $expect_out(1,string)
136 -re "^(\[^\r\n\]+)\r\n" {
137 # Any other header field.
144 error "timeout reading json header"
147 error "eof reading json header"
152 error "didn't find content-length"
156 while {$length > 0} {
157 # Tcl only allows up to 255 characters in a {} expression in a
158 # regexp, so we may need to read in chunks.
159 set this_len [expr {min ($length, 255)}]
161 -re "^.{$this_len}" {
162 append json $expect_out(0,string)
165 error "timeout reading json body"
168 error "eof reading json body"
171 incr length -$this_len
174 set ton [ton::json2ton $json]
175 return [namespace eval ton::2dict $ton]
178 # Read a sequence of JSON objects from gdb, until a response object is
179 # seen. If the response object has the request sequence number NUM,
180 # and is for command CMD, return a list of two elements: the response
181 # object and a list of any preceding events, in the order they were
182 # emitted. The objects are dicts. If a response object is seen but has
183 # the wrong sequence number or command, throw an exception
185 proc _dap_read_response {cmd num} {
188 set d [_dap_read_json]
189 if {[dict get $d type] == "response"} {
190 if {[dict get $d request_seq] != $num} {
191 error "saw wrong request_seq in $obj"
192 } elseif {[dict get $d command] != $cmd} {
193 error "saw wrong command in $obj"
195 return [list $d $result]
203 # A wrapper for _dap_send_request and _dap_read_response. This sends a
204 # request to gdb and returns the response as a dict.
205 proc dap_request_and_response {command {obj {}}} {
206 set seq [_dap_send_request $command $obj]
207 return [_dap_read_response $command $seq]
210 # Like dap_request_and_response, but also checks that the response
211 # indicates success. NAME is used to issue a test result.
212 proc dap_check_request_and_response {name command {obj {}}} {
213 set response_and_events [dap_request_and_response $command $obj]
214 set response [lindex $response_and_events 0]
215 if {[dict get $response success] != "true"} {
216 verbose "request failure: $response"
221 return $response_and_events
224 # Start gdb, send a DAP initialization request and return the
225 # response. This approach lets the caller check the feature list, if
226 # desired. Callers not caring about this should probably use
227 # dap_launch. Returns the empty string on failure. NAME is used as
229 proc _dap_initialize {name} {
230 if {[dap_gdb_start]} {
233 return [dap_check_request_and_response $name initialize \
234 {o clientID [s "gdb testsuite"] \
235 supportsVariableType [l true] \
236 supportsVariablePaging [l true]}]
239 # Start gdb, send a DAP initialize request, and then a launch request
240 # specifying FILE as the program to use for the inferior. Returns the
241 # empty string on failure, or the response object from the launch
242 # request. If specified, ARGS is a dictionary of key-value pairs,
243 # each passed to the launch request. Valid keys are:
244 # * arguments - value is a list of strings passed as command-line
245 # arguments to the inferior
246 # * env - value is a list of pairs of the form {VAR VALUE} that is
247 # used to populate the inferior's environment.
248 # * stop_at_main - value is ignored, the presence of this means that
249 # "stopAtBeginningOfMainSubprogram" will be passed to the launch
251 # * cwd - value is the working directory to use.
253 # After this proc is called, gdb will be ready to accept breakpoint
255 proc dap_launch {file {args {}}} {
256 if {[_dap_initialize "startup - initialize"] == ""} {
259 set params "o program"
260 append params " [format {[%s]} [list s [standard_output_file $file]]]"
262 foreach {key value} $args {
263 switch -exact -- $key {
265 append params " args"
268 append arglist " \[s [list $arg]\]"
270 append params " \[a $arglist\]"
276 foreach pair $value {
277 lassign $pair var value
278 append envlist " $var"
279 append envlist " [format {[%s]} [list s $value]]"
281 append params " \[o $envlist\]"
285 append params { stopAtBeginningOfMainSubprogram [l true]}
289 append envlist " cwd [format {[%s]} [list s $value]]"
293 error "unrecognized parameter $key"
298 return [dap_check_request_and_response "startup - launch" launch $params]
301 # Start gdb, send a DAP initialize request, and then an attach request
302 # specifying PID as the inferior process ID. Returns the empty string
303 # on failure, or the response object from the attach request.
304 proc dap_attach {pid} {
305 if {[_dap_initialize "startup - initialize"] == ""} {
308 return [dap_check_request_and_response "startup - attach" attach \
309 [format {o pid [i %s]} $pid]]
312 # Start gdb, send a DAP initialize request, and then an attach request
313 # specifying TARGET as the remote target. Returns the empty string on
314 # failure, or the response object from the attach request.
315 proc dap_target_remote {target} {
316 if {[_dap_initialize "startup - initialize"] == ""} {
319 return [dap_check_request_and_response "startup - target" attach \
320 [format {o target [s %s]} $target]]
323 # Cleanly shut down gdb. TERMINATE is passed as the terminateDebuggee
324 # parameter to the request.
325 proc dap_shutdown {{terminate false}} {
326 dap_check_request_and_response "shutdown" disconnect \
327 [format {o terminateDebuggee [l %s]} $terminate]
330 # Search the event list EVENTS for an output event matching the regexp
331 # RX. Pass the test NAME if found, fail if not.
332 proc dap_search_output {name rx events} {
334 if {[dict get $d type] != "event"
335 || [dict get $d event] != "output"} {
338 if {[regexp $rx [dict get $d body output]]} {
346 # Check that D (a dict object) has values that match the
347 # key/value pairs given in ARGS. NAME is used as the test name.
348 proc dap_match_values {name d args} {
349 foreach {key value} $args {
350 if {[eval dict get [list $d] $key] != $value} {
351 fail "$name (checking $key)"
358 # A helper for dap_wait_for_event_and_check that reads events, looking for one
361 # Return a list of two items:
363 # - the matched event
364 # - a list of any JSON objects (events or others) seen before the matched
366 proc _dap_wait_for_event { {type ""} } {
370 # We don't do any extra error checking here for the time
371 # being; we'll just get a timeout thrown instead.
372 set d [_dap_read_json]
373 if {[dict get $d type] == "event"
374 && ($type == "" || [dict get $d event] == $type)} {
375 return [list $d $preceding]
382 # Read JSON objects looking for an event whose "event" field is TYPE.
384 # NAME is used as the test name; it defaults to TYPE. Extra arguments
385 # are used to check fields of the event; the arguments alternate
386 # between a field name (in "dict get" form) and its expected value.
388 # Return a list of two items:
390 # - the matched event (regardless of whether it passed the field validation or
392 # - a list of any JSON objects (events or others) seen before the matched
394 proc dap_wait_for_event_and_check {name type args} {
399 set result [_dap_wait_for_event $type]
400 set event [lindex $result 0]
401 eval dap_match_values [list $name $event] $args
406 # A convenience function to extract the breakpoint number when a new
407 # breakpoint is created. OBJ is an object as returned by
408 # dap_check_request_and_response.
409 proc dap_get_breakpoint_number {obj} {
410 set d [lindex $obj 0]
411 set bplist [dict get $d body breakpoints]
412 return [dict get [lindex $bplist 0] id]