Add DAP support for C++ exceptions
[binutils-gdb.git] / gdb / testsuite / lib / dap-support.exp
1 # Copyright 2022-2023 Free Software Foundation, Inc.
2
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.
7 #
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.
12 #
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/>.
15
16 # The JSON parser.
17 load_lib ton.tcl
18
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
21 # is restarted.
22 set dap_seq 1
23
24 # Start gdb using the DAP interpreter.
25 proc dap_gdb_start {} {
26 # Keep track of the number of times GDB has been launched.
27 global gdb_instances
28 incr gdb_instances
29
30 gdb_stdin_log_init
31
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"
37 set res [gdb_spawn]
38 if {$res != 0} {
39 return $res
40 }
41 }
42
43 # Reset the counter.
44 set ::dap_seq 1
45
46 return 0
47 }
48
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} {
53 return 0
54 }
55 foreach {key value} $l {
56 if {![string is alpha $key]} {
57 return 0
58 }
59 }
60 return 1
61 }
62
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
68 # true.
69 #
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'
74 # representation.
75 proc dap_to_ton {obj} {
76 if {[string is list $obj] && [llength $obj] > 1} {
77 if {[_dap_is_obj $obj]} {
78 set result o
79 foreach {key value} $obj {
80 lappend result $key \[[dap_to_ton $value]\]
81 }
82 } else {
83 set result a
84 foreach val $obj {
85 lappend result \[[dap_to_ton $val]\]
86 }
87 }
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]
94 } else {
95 set result [list s $obj]
96 }
97 return $result
98 }
99
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"
107 }
108
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
113 # TON form.
114 proc _dap_send_request {command {obj {}}} {
115 # We can construct this directly as a TON object.
116 set result $::dap_seq
117 incr ::dap_seq
118 set req [format {o seq [i %d] type [s request] command [%s]} \
119 $result [list s $command]]
120 if {$obj != ""} {
121 append req " arguments \[$obj\]"
122 }
123 _dap_send_ton $req
124 return $result
125 }
126
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 {} {
130 set length ""
131 gdb_expect {
132 -re "^Content-Length: (\[0-9\]+)\r\n" {
133 set length $expect_out(1,string)
134 exp_continue
135 }
136 -re "^(\[^\r\n\]+)\r\n" {
137 # Any other header field.
138 exp_continue
139 }
140 -re "^\r\n" {
141 # Done.
142 }
143 timeout {
144 error "timeout reading json header"
145 }
146 eof {
147 error "eof reading json header"
148 }
149 }
150
151 if {$length == ""} {
152 error "didn't find content-length"
153 }
154
155 set json ""
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)}]
160 gdb_expect {
161 -re "^.{$this_len}" {
162 append json $expect_out(0,string)
163 }
164 timeout {
165 error "timeout reading json body"
166 }
167 eof {
168 error "eof reading json body"
169 }
170 }
171 incr length -$this_len
172 }
173
174 set ton [ton::json2ton $json]
175 return [namespace eval ton::2dict $ton]
176 }
177
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
184
185 proc _dap_read_response {cmd num} {
186 set result {}
187 while 1 {
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"
194 } else {
195 return [list $d $result]
196 }
197 } else {
198 lappend result $d
199 }
200 }
201 }
202
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]
208 }
209
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"
217 fail "$name success"
218 return ""
219 }
220 pass "$name success"
221 return $response_and_events
222 }
223
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
228 # the test name.
229 proc _dap_initialize {name} {
230 if {[dap_gdb_start]} {
231 return ""
232 }
233 return [dap_check_request_and_response $name initialize \
234 {o clientID [s "gdb testsuite"] \
235 supportsVariableType [l true] \
236 supportsVariablePaging [l true]}]
237 }
238
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
250 # request.
251 # * cwd - value is the working directory to use.
252 #
253 # After this proc is called, gdb will be ready to accept breakpoint
254 # requests.
255 proc dap_launch {file {args {}}} {
256 if {[_dap_initialize "startup - initialize"] == ""} {
257 return ""
258 }
259 set params "o program"
260 append params " [format {[%s]} [list s [standard_output_file $file]]]"
261
262 foreach {key value} $args {
263 switch -exact -- $key {
264 arguments {
265 append params " args"
266 set arglist ""
267 foreach arg $value {
268 append arglist " \[s [list $arg]\]"
269 }
270 append params " \[a $arglist\]"
271 }
272
273 env {
274 append params " env"
275 set envlist ""
276 foreach pair $value {
277 lassign $pair var value
278 append envlist " $var"
279 append envlist " [format {[%s]} [list s $value]]"
280 }
281 append params " \[o $envlist\]"
282 }
283
284 stop_at_main {
285 append params { stopAtBeginningOfMainSubprogram [l true]}
286 }
287
288 cwd {
289 append envlist " cwd [format {[%s]} [list s $value]]"
290 }
291
292 default {
293 error "unrecognized parameter $key"
294 }
295 }
296 }
297
298 return [dap_check_request_and_response "startup - launch" launch $params]
299 }
300
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"] == ""} {
306 return ""
307 }
308 return [dap_check_request_and_response "startup - attach" attach \
309 [format {o pid [i %s]} $pid]]
310 }
311
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"] == ""} {
317 return ""
318 }
319 return [dap_check_request_and_response "startup - target" attach \
320 [format {o target [s %s]} $target]]
321 }
322
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]
328 }
329
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} {
333 foreach d $events {
334 if {[dict get $d type] != "event"
335 || [dict get $d event] != "output"} {
336 continue
337 }
338 if {[regexp $rx [dict get $d body output]]} {
339 pass $name
340 return
341 }
342 }
343 fail $name
344 }
345
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)"
352 return ""
353 }
354 }
355 pass $name
356 }
357
358 # A helper for dap_wait_for_event_and_check that reads events, looking for one
359 # matching TYPE.
360 #
361 # Return a list of two items:
362 #
363 # - the matched event
364 # - a list of any JSON objects (events or others) seen before the matched
365 # event.
366 proc _dap_wait_for_event { {type ""} } {
367 set preceding [list]
368
369 while 1 {
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]
376 }
377
378 lappend preceding $d
379 }
380 }
381
382 # Read JSON objects looking for an event whose "event" field is TYPE.
383 #
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.
387 #
388 # Return a list of two items:
389 #
390 # - the matched event (regardless of whether it passed the field validation or
391 # not)
392 # - a list of any JSON objects (events or others) seen before the matched
393 # event.
394 proc dap_wait_for_event_and_check {name type args} {
395 if {$name == ""} {
396 set name $type
397 }
398
399 set result [_dap_wait_for_event $type]
400 set event [lindex $result 0]
401 eval dap_match_values [list $name $event] $args
402
403 return $result
404 }
405
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]
413 }