[SandboxVec][BottomUpVec] Fix packing when PHIs are present (#124206)
[llvm-project.git] / lldb / packages / Python / lldbsuite / test / tools / lldb-dap / dap_server.py
blobc29992ce9c7848e9d8254910b2599758270b11e1
1 #!/usr/bin/env python
3 import binascii
4 import json
5 import optparse
6 import os
7 import pprint
8 import socket
9 import string
10 import subprocess
11 import sys
12 import threading
13 import time
16 def dump_memory(base_addr, data, num_per_line, outfile):
17 data_len = len(data)
18 hex_string = binascii.hexlify(data)
19 addr = base_addr
20 ascii_str = ""
21 i = 0
22 while i < data_len:
23 outfile.write("0x%8.8x: " % (addr + i))
24 bytes_left = data_len - i
25 if bytes_left >= num_per_line:
26 curr_data_len = num_per_line
27 else:
28 curr_data_len = bytes_left
29 hex_start_idx = i * 2
30 hex_end_idx = hex_start_idx + curr_data_len * 2
31 curr_hex_str = hex_string[hex_start_idx:hex_end_idx]
32 # 'curr_hex_str' now contains the hex byte string for the
33 # current line with no spaces between bytes
34 t = iter(curr_hex_str)
35 # Print hex bytes separated by space
36 outfile.write(" ".join(a + b for a, b in zip(t, t)))
37 # Print two spaces
38 outfile.write(" ")
39 # Calculate ASCII string for bytes into 'ascii_str'
40 ascii_str = ""
41 for j in range(i, i + curr_data_len):
42 ch = data[j]
43 if ch in string.printable and ch not in string.whitespace:
44 ascii_str += "%c" % (ch)
45 else:
46 ascii_str += "."
47 # Print ASCII representation and newline
48 outfile.write(ascii_str)
49 i = i + curr_data_len
50 outfile.write("\n")
53 def read_packet(f, verbose=False, trace_file=None):
54 """Decode a JSON packet that starts with the content length and is
55 followed by the JSON bytes from a file 'f'. Returns None on EOF.
56 """
57 line = f.readline().decode("utf-8")
58 if len(line) == 0:
59 return None # EOF.
61 # Watch for line that starts with the prefix
62 prefix = "Content-Length: "
63 if line.startswith(prefix):
64 # Decode length of JSON bytes
65 if verbose:
66 print('content: "%s"' % (line))
67 length = int(line[len(prefix) :])
68 if verbose:
69 print('length: "%u"' % (length))
70 # Skip empty line
71 line = f.readline()
72 if verbose:
73 print('empty: "%s"' % (line))
74 # Read JSON bytes
75 json_str = f.read(length)
76 if verbose:
77 print('json: "%s"' % (json_str))
78 if trace_file:
79 trace_file.write("from adaptor:\n%s\n" % (json_str))
80 # Decode the JSON bytes into a python dictionary
81 return json.loads(json_str)
83 raise Exception("unexpected malformed message from lldb-dap: " + line)
86 def packet_type_is(packet, packet_type):
87 return "type" in packet and packet["type"] == packet_type
90 def dump_dap_log(log_file):
91 print("========= DEBUG ADAPTER PROTOCOL LOGS =========")
92 if log_file is None:
93 print("no log file available")
94 else:
95 with open(log_file, "r") as file:
96 print(file.read())
97 print("========= END =========")
100 def read_packet_thread(vs_comm, log_file):
101 done = False
102 try:
103 while not done:
104 packet = read_packet(vs_comm.recv, trace_file=vs_comm.trace_file)
105 # `packet` will be `None` on EOF. We want to pass it down to
106 # handle_recv_packet anyway so the main thread can handle unexpected
107 # termination of lldb-dap and stop waiting for new packets.
108 done = not vs_comm.handle_recv_packet(packet)
109 finally:
110 dump_dap_log(log_file)
113 class DebugCommunication(object):
114 def __init__(self, recv, send, init_commands, log_file=None):
115 self.trace_file = None
116 self.send = send
117 self.recv = recv
118 self.recv_packets = []
119 self.recv_condition = threading.Condition()
120 self.recv_thread = threading.Thread(
121 target=read_packet_thread, args=(self, log_file)
123 self.process_event_body = None
124 self.exit_status = None
125 self.initialize_body = None
126 self.thread_stop_reasons = {}
127 self.breakpoint_events = []
128 self.progress_events = []
129 self.reverse_requests = []
130 self.sequence = 1
131 self.threads = None
132 self.recv_thread.start()
133 self.output_condition = threading.Condition()
134 self.output = {}
135 self.configuration_done_sent = False
136 self.frame_scopes = {}
137 self.init_commands = init_commands
138 self.disassembled_instructions = {}
140 @classmethod
141 def encode_content(cls, s):
142 return ("Content-Length: %u\r\n\r\n%s" % (len(s), s)).encode("utf-8")
144 @classmethod
145 def validate_response(cls, command, response):
146 if command["command"] != response["command"]:
147 raise ValueError("command mismatch in response")
148 if command["seq"] != response["request_seq"]:
149 raise ValueError("seq mismatch in response")
151 def get_modules(self):
152 module_list = self.request_modules()["body"]["modules"]
153 modules = {}
154 for module in module_list:
155 modules[module["name"]] = module
156 return modules
158 def get_output(self, category, timeout=0.0, clear=True):
159 self.output_condition.acquire()
160 output = None
161 if category in self.output:
162 output = self.output[category]
163 if clear:
164 del self.output[category]
165 elif timeout != 0.0:
166 self.output_condition.wait(timeout)
167 if category in self.output:
168 output = self.output[category]
169 if clear:
170 del self.output[category]
171 self.output_condition.release()
172 return output
174 def collect_output(self, category, timeout_secs, pattern, clear=True):
175 end_time = time.time() + timeout_secs
176 collected_output = ""
177 while end_time > time.time():
178 output = self.get_output(category, timeout=0.25, clear=clear)
179 if output:
180 collected_output += output
181 if pattern is not None and pattern in output:
182 break
183 return collected_output if collected_output else None
185 def enqueue_recv_packet(self, packet):
186 self.recv_condition.acquire()
187 self.recv_packets.append(packet)
188 self.recv_condition.notify()
189 self.recv_condition.release()
191 def handle_recv_packet(self, packet):
192 """Called by the read thread that is waiting for all incoming packets
193 to store the incoming packet in "self.recv_packets" in a thread safe
194 way. This function will then signal the "self.recv_condition" to
195 indicate a new packet is available. Returns True if the caller
196 should keep calling this function for more packets.
198 # If EOF, notify the read thread by enqueuing a None.
199 if not packet:
200 self.enqueue_recv_packet(None)
201 return False
203 # Check the packet to see if is an event packet
204 keepGoing = True
205 packet_type = packet["type"]
206 if packet_type == "event":
207 event = packet["event"]
208 body = None
209 if "body" in packet:
210 body = packet["body"]
211 # Handle the event packet and cache information from these packets
212 # as they come in
213 if event == "output":
214 # Store any output we receive so clients can retrieve it later.
215 category = body["category"]
216 output = body["output"]
217 self.output_condition.acquire()
218 if category in self.output:
219 self.output[category] += output
220 else:
221 self.output[category] = output
222 self.output_condition.notify()
223 self.output_condition.release()
224 # no need to add 'output' event packets to our packets list
225 return keepGoing
226 elif event == "process":
227 # When a new process is attached or launched, remember the
228 # details that are available in the body of the event
229 self.process_event_body = body
230 elif event == "stopped":
231 # Each thread that stops with a reason will send a
232 # 'stopped' event. We need to remember the thread stop
233 # reasons since the 'threads' command doesn't return
234 # that information.
235 self._process_stopped()
236 tid = body["threadId"]
237 self.thread_stop_reasons[tid] = body
238 elif event == "breakpoint":
239 # Breakpoint events come in when a breakpoint has locations
240 # added or removed. Keep track of them so we can look for them
241 # in tests.
242 self.breakpoint_events.append(packet)
243 # no need to add 'breakpoint' event packets to our packets list
244 return keepGoing
245 elif event.startswith("progress"):
246 # Progress events come in as 'progressStart', 'progressUpdate',
247 # and 'progressEnd' events. Keep these around in case test
248 # cases want to verify them.
249 self.progress_events.append(packet)
250 # No need to add 'progress' event packets to our packets list.
251 return keepGoing
253 elif packet_type == "response":
254 if packet["command"] == "disconnect":
255 keepGoing = False
256 self.enqueue_recv_packet(packet)
257 return keepGoing
259 def send_packet(self, command_dict, set_sequence=True):
260 """Take the "command_dict" python dictionary and encode it as a JSON
261 string and send the contents as a packet to the VSCode debug
262 adaptor"""
263 # Set the sequence ID for this command automatically
264 if set_sequence:
265 command_dict["seq"] = self.sequence
266 self.sequence += 1
267 # Encode our command dictionary as a JSON string
268 json_str = json.dumps(command_dict, separators=(",", ":"))
269 if self.trace_file:
270 self.trace_file.write("to adaptor:\n%s\n" % (json_str))
271 length = len(json_str)
272 if length > 0:
273 # Send the encoded JSON packet and flush the 'send' file
274 self.send.write(self.encode_content(json_str))
275 self.send.flush()
277 def recv_packet(self, filter_type=None, filter_event=None, timeout=None):
278 """Get a JSON packet from the VSCode debug adaptor. This function
279 assumes a thread that reads packets is running and will deliver
280 any received packets by calling handle_recv_packet(...). This
281 function will wait for the packet to arrive and return it when
282 it does."""
283 while True:
284 try:
285 self.recv_condition.acquire()
286 packet = None
287 while True:
288 for i, curr_packet in enumerate(self.recv_packets):
289 if not curr_packet:
290 raise EOFError
291 packet_type = curr_packet["type"]
292 if filter_type is None or packet_type in filter_type:
293 if filter_event is None or (
294 packet_type == "event"
295 and curr_packet["event"] in filter_event
297 packet = self.recv_packets.pop(i)
298 break
299 if packet:
300 break
301 # Sleep until packet is received
302 len_before = len(self.recv_packets)
303 self.recv_condition.wait(timeout)
304 len_after = len(self.recv_packets)
305 if len_before == len_after:
306 return None # Timed out
307 return packet
308 except EOFError:
309 return None
310 finally:
311 self.recv_condition.release()
313 return None
315 def send_recv(self, command):
316 """Send a command python dictionary as JSON and receive the JSON
317 response. Validates that the response is the correct sequence and
318 command in the reply. Any events that are received are added to the
319 events list in this object"""
320 self.send_packet(command)
321 done = False
322 while not done:
323 response_or_request = self.recv_packet(filter_type=["response", "request"])
324 if response_or_request is None:
325 desc = 'no response for "%s"' % (command["command"])
326 raise ValueError(desc)
327 if response_or_request["type"] == "response":
328 self.validate_response(command, response_or_request)
329 return response_or_request
330 else:
331 self.reverse_requests.append(response_or_request)
332 if response_or_request["command"] == "runInTerminal":
333 subprocess.Popen(
334 response_or_request["arguments"]["args"],
335 env=response_or_request["arguments"]["env"],
337 self.send_packet(
339 "type": "response",
340 "seq": -1,
341 "request_seq": response_or_request["seq"],
342 "success": True,
343 "command": "runInTerminal",
344 "body": {},
346 set_sequence=False,
348 elif response_or_request["command"] == "startDebugging":
349 self.send_packet(
351 "type": "response",
352 "seq": -1,
353 "request_seq": response_or_request["seq"],
354 "success": True,
355 "command": "startDebugging",
356 "body": {},
358 set_sequence=False,
360 else:
361 desc = 'unknown reverse request "%s"' % (
362 response_or_request["command"]
364 raise ValueError(desc)
366 return None
368 def wait_for_event(self, filter=None, timeout=None):
369 while True:
370 return self.recv_packet(
371 filter_type="event", filter_event=filter, timeout=timeout
373 return None
375 def wait_for_stopped(self, timeout=None):
376 stopped_events = []
377 stopped_event = self.wait_for_event(
378 filter=["stopped", "exited"], timeout=timeout
380 exited = False
381 while stopped_event:
382 stopped_events.append(stopped_event)
383 # If we exited, then we are done
384 if stopped_event["event"] == "exited":
385 self.exit_status = stopped_event["body"]["exitCode"]
386 exited = True
387 break
388 # Otherwise we stopped and there might be one or more 'stopped'
389 # events for each thread that stopped with a reason, so keep
390 # checking for more 'stopped' events and return all of them
391 stopped_event = self.wait_for_event(filter="stopped", timeout=0.25)
392 if exited:
393 self.threads = []
394 return stopped_events
396 def wait_for_exited(self):
397 event_dict = self.wait_for_event("exited")
398 if event_dict is None:
399 raise ValueError("didn't get exited event")
400 return event_dict
402 def wait_for_terminated(self):
403 event_dict = self.wait_for_event("terminated")
404 if event_dict is None:
405 raise ValueError("didn't get terminated event")
406 return event_dict
408 def get_initialize_value(self, key):
409 """Get a value for the given key if it there is a key/value pair in
410 the "initialize" request response body.
412 if self.initialize_body and key in self.initialize_body:
413 return self.initialize_body[key]
414 return None
416 def get_threads(self):
417 if self.threads is None:
418 self.request_threads()
419 return self.threads
421 def get_thread_id(self, threadIndex=0):
422 """Utility function to get the first thread ID in the thread list.
423 If the thread list is empty, then fetch the threads.
425 if self.threads is None:
426 self.request_threads()
427 if self.threads and threadIndex < len(self.threads):
428 return self.threads[threadIndex]["id"]
429 return None
431 def get_stackFrame(self, frameIndex=0, threadId=None):
432 """Get a single "StackFrame" object from a "stackTrace" request and
433 return the "StackFrame" as a python dictionary, or None on failure
435 if threadId is None:
436 threadId = self.get_thread_id()
437 if threadId is None:
438 print("invalid threadId")
439 return None
440 response = self.request_stackTrace(threadId, startFrame=frameIndex, levels=1)
441 if response:
442 return response["body"]["stackFrames"][0]
443 print("invalid response")
444 return None
446 def get_completions(self, text, frameId=None):
447 if frameId is None:
448 stackFrame = self.get_stackFrame()
449 frameId = stackFrame["id"]
450 response = self.request_completions(text, frameId)
451 return response["body"]["targets"]
453 def get_scope_variables(self, scope_name, frameIndex=0, threadId=None, is_hex=None):
454 stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId)
455 if stackFrame is None:
456 return []
457 frameId = stackFrame["id"]
458 if frameId in self.frame_scopes:
459 frame_scopes = self.frame_scopes[frameId]
460 else:
461 scopes_response = self.request_scopes(frameId)
462 frame_scopes = scopes_response["body"]["scopes"]
463 self.frame_scopes[frameId] = frame_scopes
464 for scope in frame_scopes:
465 if scope["name"] == scope_name:
466 varRef = scope["variablesReference"]
467 variables_response = self.request_variables(varRef, is_hex=is_hex)
468 if variables_response:
469 if "body" in variables_response:
470 body = variables_response["body"]
471 if "variables" in body:
472 vars = body["variables"]
473 return vars
474 return []
476 def get_global_variables(self, frameIndex=0, threadId=None):
477 return self.get_scope_variables(
478 "Globals", frameIndex=frameIndex, threadId=threadId
481 def get_local_variables(self, frameIndex=0, threadId=None, is_hex=None):
482 return self.get_scope_variables(
483 "Locals", frameIndex=frameIndex, threadId=threadId, is_hex=is_hex
486 def get_registers(self, frameIndex=0, threadId=None):
487 return self.get_scope_variables(
488 "Registers", frameIndex=frameIndex, threadId=threadId
491 def get_local_variable(self, name, frameIndex=0, threadId=None, is_hex=None):
492 locals = self.get_local_variables(
493 frameIndex=frameIndex, threadId=threadId, is_hex=is_hex
495 for local in locals:
496 if "name" in local and local["name"] == name:
497 return local
498 return None
500 def get_local_variable_value(self, name, frameIndex=0, threadId=None, is_hex=None):
501 variable = self.get_local_variable(
502 name, frameIndex=frameIndex, threadId=threadId, is_hex=is_hex
504 if variable and "value" in variable:
505 return variable["value"]
506 return None
508 def get_local_variable_child(
509 self, name, child_name, frameIndex=0, threadId=None, is_hex=None
511 local = self.get_local_variable(name, frameIndex, threadId)
512 if local["variablesReference"] == 0:
513 return None
514 children = self.request_variables(local["variablesReference"], is_hex=is_hex)[
515 "body"
516 ]["variables"]
517 for child in children:
518 if child["name"] == child_name:
519 return child
520 return None
522 def replay_packets(self, replay_file_path):
523 f = open(replay_file_path, "r")
524 mode = "invalid"
525 set_sequence = False
526 command_dict = None
527 while mode != "eof":
528 if mode == "invalid":
529 line = f.readline()
530 if line.startswith("to adapter:"):
531 mode = "send"
532 elif line.startswith("from adapter:"):
533 mode = "recv"
534 elif mode == "send":
535 command_dict = read_packet(f)
536 # Skip the end of line that follows the JSON
537 f.readline()
538 if command_dict is None:
539 raise ValueError("decode packet failed from replay file")
540 print("Sending:")
541 pprint.PrettyPrinter(indent=2).pprint(command_dict)
542 # raw_input('Press ENTER to send:')
543 self.send_packet(command_dict, set_sequence)
544 mode = "invalid"
545 elif mode == "recv":
546 print("Replay response:")
547 replay_response = read_packet(f)
548 # Skip the end of line that follows the JSON
549 f.readline()
550 pprint.PrettyPrinter(indent=2).pprint(replay_response)
551 actual_response = self.recv_packet()
552 if actual_response:
553 type = actual_response["type"]
554 print("Actual response:")
555 if type == "response":
556 self.validate_response(command_dict, actual_response)
557 pprint.PrettyPrinter(indent=2).pprint(actual_response)
558 else:
559 print("error: didn't get a valid response")
560 mode = "invalid"
562 def request_attach(
563 self,
564 program=None,
565 pid=None,
566 waitFor=None,
567 trace=None,
568 initCommands=None,
569 preRunCommands=None,
570 stopCommands=None,
571 exitCommands=None,
572 attachCommands=None,
573 terminateCommands=None,
574 coreFile=None,
575 postRunCommands=None,
576 sourceMap=None,
577 gdbRemotePort=None,
578 gdbRemoteHostname=None,
580 args_dict = {}
581 if pid is not None:
582 args_dict["pid"] = pid
583 if program is not None:
584 args_dict["program"] = program
585 if waitFor is not None:
586 args_dict["waitFor"] = waitFor
587 if trace:
588 args_dict["trace"] = trace
589 args_dict["initCommands"] = self.init_commands
590 if initCommands:
591 args_dict["initCommands"].extend(initCommands)
592 if preRunCommands:
593 args_dict["preRunCommands"] = preRunCommands
594 if stopCommands:
595 args_dict["stopCommands"] = stopCommands
596 if exitCommands:
597 args_dict["exitCommands"] = exitCommands
598 if terminateCommands:
599 args_dict["terminateCommands"] = terminateCommands
600 if attachCommands:
601 args_dict["attachCommands"] = attachCommands
602 if coreFile:
603 args_dict["coreFile"] = coreFile
604 if postRunCommands:
605 args_dict["postRunCommands"] = postRunCommands
606 if sourceMap:
607 args_dict["sourceMap"] = sourceMap
608 if gdbRemotePort is not None:
609 args_dict["gdb-remote-port"] = gdbRemotePort
610 if gdbRemoteHostname is not None:
611 args_dict["gdb-remote-hostname"] = gdbRemoteHostname
612 command_dict = {"command": "attach", "type": "request", "arguments": args_dict}
613 return self.send_recv(command_dict)
615 def request_configurationDone(self):
616 command_dict = {
617 "command": "configurationDone",
618 "type": "request",
619 "arguments": {},
621 response = self.send_recv(command_dict)
622 if response:
623 self.configuration_done_sent = True
624 return response
626 def _process_stopped(self):
627 self.threads = None
628 self.frame_scopes = {}
630 def request_continue(self, threadId=None):
631 if self.exit_status is not None:
632 raise ValueError("request_continue called after process exited")
633 # If we have launched or attached, then the first continue is done by
634 # sending the 'configurationDone' request
635 if not self.configuration_done_sent:
636 return self.request_configurationDone()
637 args_dict = {}
638 if threadId is None:
639 threadId = self.get_thread_id()
640 args_dict["threadId"] = threadId
641 command_dict = {
642 "command": "continue",
643 "type": "request",
644 "arguments": args_dict,
646 response = self.send_recv(command_dict)
647 # Caller must still call wait_for_stopped.
648 return response
650 def request_restart(self, restartArguments=None):
651 command_dict = {
652 "command": "restart",
653 "type": "request",
655 if restartArguments:
656 command_dict["arguments"] = restartArguments
658 response = self.send_recv(command_dict)
659 # Caller must still call wait_for_stopped.
660 return response
662 def request_disconnect(self, terminateDebuggee=None):
663 args_dict = {}
664 if terminateDebuggee is not None:
665 if terminateDebuggee:
666 args_dict["terminateDebuggee"] = True
667 else:
668 args_dict["terminateDebuggee"] = False
669 command_dict = {
670 "command": "disconnect",
671 "type": "request",
672 "arguments": args_dict,
674 return self.send_recv(command_dict)
676 def request_disassemble(
677 self, memoryReference, offset=-50, instructionCount=200, resolveSymbols=True
679 args_dict = {
680 "memoryReference": memoryReference,
681 "offset": offset,
682 "instructionCount": instructionCount,
683 "resolveSymbols": resolveSymbols,
685 command_dict = {
686 "command": "disassemble",
687 "type": "request",
688 "arguments": args_dict,
690 instructions = self.send_recv(command_dict)["body"]["instructions"]
691 for inst in instructions:
692 self.disassembled_instructions[inst["address"]] = inst
694 def request_readMemory(self, memoryReference, offset, count):
695 args_dict = {
696 "memoryReference": memoryReference,
697 "offset": offset,
698 "count": count,
700 command_dict = {
701 "command": "readMemory",
702 "type": "request",
703 "arguments": args_dict,
705 return self.send_recv(command_dict)
707 def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None):
708 stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId)
709 if stackFrame is None:
710 return []
711 args_dict = {
712 "expression": expression,
713 "context": context,
714 "frameId": stackFrame["id"],
716 command_dict = {
717 "command": "evaluate",
718 "type": "request",
719 "arguments": args_dict,
721 return self.send_recv(command_dict)
723 def request_exceptionInfo(self, threadId=None):
724 if threadId is None:
725 threadId = self.get_thread_id()
726 args_dict = {"threadId": threadId}
727 command_dict = {
728 "command": "exceptionInfo",
729 "type": "request",
730 "arguments": args_dict,
732 return self.send_recv(command_dict)
734 def request_initialize(self, sourceInitFile):
735 command_dict = {
736 "command": "initialize",
737 "type": "request",
738 "arguments": {
739 "adapterID": "lldb-native",
740 "clientID": "vscode",
741 "columnsStartAt1": True,
742 "linesStartAt1": True,
743 "locale": "en-us",
744 "pathFormat": "path",
745 "supportsRunInTerminalRequest": True,
746 "supportsVariablePaging": True,
747 "supportsVariableType": True,
748 "supportsStartDebuggingRequest": True,
749 "sourceInitFile": sourceInitFile,
752 response = self.send_recv(command_dict)
753 if response:
754 if "body" in response:
755 self.initialize_body = response["body"]
756 return response
758 def request_launch(
759 self,
760 program,
761 args=None,
762 cwd=None,
763 env=None,
764 stopOnEntry=False,
765 disableASLR=True,
766 disableSTDIO=False,
767 shellExpandArguments=False,
768 trace=False,
769 initCommands=None,
770 preRunCommands=None,
771 stopCommands=None,
772 exitCommands=None,
773 terminateCommands=None,
774 sourcePath=None,
775 debuggerRoot=None,
776 launchCommands=None,
777 sourceMap=None,
778 runInTerminal=False,
779 postRunCommands=None,
780 enableAutoVariableSummaries=False,
781 displayExtendedBacktrace=False,
782 enableSyntheticChildDebugging=False,
783 commandEscapePrefix=None,
784 customFrameFormat=None,
785 customThreadFormat=None,
787 args_dict = {"program": program}
788 if args:
789 args_dict["args"] = args
790 if cwd:
791 args_dict["cwd"] = cwd
792 if env:
793 args_dict["env"] = env
794 if stopOnEntry:
795 args_dict["stopOnEntry"] = stopOnEntry
796 if disableSTDIO:
797 args_dict["disableSTDIO"] = disableSTDIO
798 if shellExpandArguments:
799 args_dict["shellExpandArguments"] = shellExpandArguments
800 if trace:
801 args_dict["trace"] = trace
802 args_dict["initCommands"] = self.init_commands
803 if initCommands:
804 args_dict["initCommands"].extend(initCommands)
805 if preRunCommands:
806 args_dict["preRunCommands"] = preRunCommands
807 if stopCommands:
808 args_dict["stopCommands"] = stopCommands
809 if exitCommands:
810 args_dict["exitCommands"] = exitCommands
811 if terminateCommands:
812 args_dict["terminateCommands"] = terminateCommands
813 if sourcePath:
814 args_dict["sourcePath"] = sourcePath
815 if debuggerRoot:
816 args_dict["debuggerRoot"] = debuggerRoot
817 if launchCommands:
818 args_dict["launchCommands"] = launchCommands
819 if sourceMap:
820 args_dict["sourceMap"] = sourceMap
821 if runInTerminal:
822 args_dict["runInTerminal"] = runInTerminal
823 if postRunCommands:
824 args_dict["postRunCommands"] = postRunCommands
825 if customFrameFormat:
826 args_dict["customFrameFormat"] = customFrameFormat
827 if customThreadFormat:
828 args_dict["customThreadFormat"] = customThreadFormat
830 args_dict["disableASLR"] = disableASLR
831 args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
832 args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
833 args_dict["displayExtendedBacktrace"] = displayExtendedBacktrace
834 args_dict["commandEscapePrefix"] = commandEscapePrefix
835 command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
836 response = self.send_recv(command_dict)
838 if response["success"]:
839 # Wait for a 'process' and 'initialized' event in any order
840 self.wait_for_event(filter=["process", "initialized"])
841 self.wait_for_event(filter=["process", "initialized"])
842 return response
844 def request_next(self, threadId, granularity="statement"):
845 if self.exit_status is not None:
846 raise ValueError("request_continue called after process exited")
847 args_dict = {"threadId": threadId, "granularity": granularity}
848 command_dict = {"command": "next", "type": "request", "arguments": args_dict}
849 return self.send_recv(command_dict)
851 def request_stepIn(self, threadId, targetId, granularity="statement"):
852 if self.exit_status is not None:
853 raise ValueError("request_stepIn called after process exited")
854 args_dict = {
855 "threadId": threadId,
856 "targetId": targetId,
857 "granularity": granularity,
859 command_dict = {"command": "stepIn", "type": "request", "arguments": args_dict}
860 return self.send_recv(command_dict)
862 def request_stepInTargets(self, frameId):
863 if self.exit_status is not None:
864 raise ValueError("request_stepInTargets called after process exited")
865 args_dict = {"frameId": frameId}
866 command_dict = {
867 "command": "stepInTargets",
868 "type": "request",
869 "arguments": args_dict,
871 return self.send_recv(command_dict)
873 def request_stepOut(self, threadId):
874 if self.exit_status is not None:
875 raise ValueError("request_stepOut called after process exited")
876 args_dict = {"threadId": threadId}
877 command_dict = {"command": "stepOut", "type": "request", "arguments": args_dict}
878 return self.send_recv(command_dict)
880 def request_pause(self, threadId=None):
881 if self.exit_status is not None:
882 raise ValueError("request_pause called after process exited")
883 if threadId is None:
884 threadId = self.get_thread_id()
885 args_dict = {"threadId": threadId}
886 command_dict = {"command": "pause", "type": "request", "arguments": args_dict}
887 return self.send_recv(command_dict)
889 def request_scopes(self, frameId):
890 args_dict = {"frameId": frameId}
891 command_dict = {"command": "scopes", "type": "request", "arguments": args_dict}
892 return self.send_recv(command_dict)
894 def request_setBreakpoints(self, file_path, line_array, data=None):
895 """data is array of parameters for breakpoints in line_array.
896 Each parameter object is 1:1 mapping with entries in line_entry.
897 It contains optional location/hitCondition/logMessage parameters.
899 (dir, base) = os.path.split(file_path)
900 source_dict = {"name": base, "path": file_path}
901 args_dict = {
902 "source": source_dict,
903 "sourceModified": False,
905 if line_array is not None:
906 args_dict["lines"] = "%s" % line_array
907 breakpoints = []
908 for i, line in enumerate(line_array):
909 breakpoint_data = None
910 if data is not None and i < len(data):
911 breakpoint_data = data[i]
912 bp = {"line": line}
913 if breakpoint_data is not None:
914 if "condition" in breakpoint_data and breakpoint_data["condition"]:
915 bp["condition"] = breakpoint_data["condition"]
916 if (
917 "hitCondition" in breakpoint_data
918 and breakpoint_data["hitCondition"]
920 bp["hitCondition"] = breakpoint_data["hitCondition"]
921 if (
922 "logMessage" in breakpoint_data
923 and breakpoint_data["logMessage"]
925 bp["logMessage"] = breakpoint_data["logMessage"]
926 breakpoints.append(bp)
927 args_dict["breakpoints"] = breakpoints
929 command_dict = {
930 "command": "setBreakpoints",
931 "type": "request",
932 "arguments": args_dict,
934 return self.send_recv(command_dict)
936 def request_setExceptionBreakpoints(self, filters):
937 args_dict = {"filters": filters}
938 command_dict = {
939 "command": "setExceptionBreakpoints",
940 "type": "request",
941 "arguments": args_dict,
943 return self.send_recv(command_dict)
945 def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=None):
946 breakpoints = []
947 for name in names:
948 bp = {"name": name}
949 if condition is not None:
950 bp["condition"] = condition
951 if hitCondition is not None:
952 bp["hitCondition"] = hitCondition
953 breakpoints.append(bp)
954 args_dict = {"breakpoints": breakpoints}
955 command_dict = {
956 "command": "setFunctionBreakpoints",
957 "type": "request",
958 "arguments": args_dict,
960 return self.send_recv(command_dict)
962 def request_dataBreakpointInfo(
963 self, variablesReference, name, frameIndex=0, threadId=None
965 stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId)
966 if stackFrame is None:
967 return []
968 args_dict = {
969 "variablesReference": variablesReference,
970 "name": name,
971 "frameId": stackFrame["id"],
973 command_dict = {
974 "command": "dataBreakpointInfo",
975 "type": "request",
976 "arguments": args_dict,
978 return self.send_recv(command_dict)
980 def request_setDataBreakpoint(self, dataBreakpoints):
981 """dataBreakpoints is a list of dictionary with following fields:
983 dataId: (address in hex)/(size in bytes)
984 accessType: read/write/readWrite
985 [condition]: string
986 [hitCondition]: string
989 args_dict = {"breakpoints": dataBreakpoints}
990 command_dict = {
991 "command": "setDataBreakpoints",
992 "type": "request",
993 "arguments": args_dict,
995 return self.send_recv(command_dict)
997 def request_compileUnits(self, moduleId):
998 args_dict = {"moduleId": moduleId}
999 command_dict = {
1000 "command": "compileUnits",
1001 "type": "request",
1002 "arguments": args_dict,
1004 response = self.send_recv(command_dict)
1005 return response
1007 def request_completions(self, text, frameId=None):
1008 args_dict = {"text": text, "column": len(text) + 1}
1009 if frameId:
1010 args_dict["frameId"] = frameId
1011 command_dict = {
1012 "command": "completions",
1013 "type": "request",
1014 "arguments": args_dict,
1016 return self.send_recv(command_dict)
1018 def request_modules(self):
1019 return self.send_recv({"command": "modules", "type": "request"})
1021 def request_stackTrace(
1022 self, threadId=None, startFrame=None, levels=None, dump=False
1024 if threadId is None:
1025 threadId = self.get_thread_id()
1026 args_dict = {"threadId": threadId}
1027 if startFrame is not None:
1028 args_dict["startFrame"] = startFrame
1029 if levels is not None:
1030 args_dict["levels"] = levels
1031 command_dict = {
1032 "command": "stackTrace",
1033 "type": "request",
1034 "arguments": args_dict,
1036 response = self.send_recv(command_dict)
1037 if dump:
1038 for idx, frame in enumerate(response["body"]["stackFrames"]):
1039 name = frame["name"]
1040 if "line" in frame and "source" in frame:
1041 source = frame["source"]
1042 if "sourceReference" not in source:
1043 if "name" in source:
1044 source_name = source["name"]
1045 line = frame["line"]
1046 print("[%3u] %s @ %s:%u" % (idx, name, source_name, line))
1047 continue
1048 print("[%3u] %s" % (idx, name))
1049 return response
1051 def request_threads(self):
1052 """Request a list of all threads and combine any information from any
1053 "stopped" events since those contain more information about why a
1054 thread actually stopped. Returns an array of thread dictionaries
1055 with information about all threads"""
1056 command_dict = {"command": "threads", "type": "request", "arguments": {}}
1057 response = self.send_recv(command_dict)
1058 body = response["body"]
1059 # Fill in "self.threads" correctly so that clients that call
1060 # self.get_threads() or self.get_thread_id(...) can get information
1061 # on threads when the process is stopped.
1062 if "threads" in body:
1063 self.threads = body["threads"]
1064 for thread in self.threads:
1065 # Copy the thread dictionary so we can add key/value pairs to
1066 # it without affecting the original info from the "threads"
1067 # command.
1068 tid = thread["id"]
1069 if tid in self.thread_stop_reasons:
1070 thread_stop_info = self.thread_stop_reasons[tid]
1071 copy_keys = ["reason", "description", "text"]
1072 for key in copy_keys:
1073 if key in thread_stop_info:
1074 thread[key] = thread_stop_info[key]
1075 else:
1076 self.threads = None
1077 return response
1079 def request_variables(
1080 self, variablesReference, start=None, count=None, is_hex=None
1082 args_dict = {"variablesReference": variablesReference}
1083 if start is not None:
1084 args_dict["start"] = start
1085 if count is not None:
1086 args_dict["count"] = count
1087 if is_hex is not None:
1088 args_dict["format"] = {"hex": is_hex}
1089 command_dict = {
1090 "command": "variables",
1091 "type": "request",
1092 "arguments": args_dict,
1094 return self.send_recv(command_dict)
1096 def request_setVariable(self, containingVarRef, name, value, id=None):
1097 args_dict = {
1098 "variablesReference": containingVarRef,
1099 "name": name,
1100 "value": str(value),
1102 if id is not None:
1103 args_dict["id"] = id
1104 command_dict = {
1105 "command": "setVariable",
1106 "type": "request",
1107 "arguments": args_dict,
1109 return self.send_recv(command_dict)
1111 def request_locations(self, locationReference):
1112 args_dict = {
1113 "locationReference": locationReference,
1115 command_dict = {
1116 "command": "locations",
1117 "type": "request",
1118 "arguments": args_dict,
1120 return self.send_recv(command_dict)
1122 def request_testGetTargetBreakpoints(self):
1123 """A request packet used in the LLDB test suite to get all currently
1124 set breakpoint infos for all breakpoints currently set in the
1125 target.
1127 command_dict = {
1128 "command": "_testGetTargetBreakpoints",
1129 "type": "request",
1130 "arguments": {},
1132 return self.send_recv(command_dict)
1134 def terminate(self):
1135 self.send.close()
1136 # self.recv.close()
1138 def request_setInstructionBreakpoints(self, memory_reference=[]):
1139 breakpoints = []
1140 for i in memory_reference:
1141 args_dict = {
1142 "instructionReference": i,
1144 breakpoints.append(args_dict)
1145 args_dict = {"breakpoints": breakpoints}
1146 command_dict = {
1147 "command": "setInstructionBreakpoints",
1148 "type": "request",
1149 "arguments": args_dict,
1151 return self.send_recv(command_dict)
1153 class DebugAdaptorServer(DebugCommunication):
1154 def __init__(
1155 self,
1156 executable=None,
1157 port=None,
1158 init_commands=[],
1159 log_file=None,
1160 env=None,
1162 self.process = None
1163 if executable is not None:
1164 adaptor_env = os.environ.copy()
1165 if env is not None:
1166 adaptor_env.update(env)
1168 if log_file:
1169 adaptor_env["LLDBDAP_LOG"] = log_file
1170 self.process = subprocess.Popen(
1171 [executable],
1172 stdin=subprocess.PIPE,
1173 stdout=subprocess.PIPE,
1174 stderr=subprocess.PIPE,
1175 env=adaptor_env,
1177 DebugCommunication.__init__(
1178 self, self.process.stdout, self.process.stdin, init_commands, log_file
1180 elif port is not None:
1181 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1182 s.connect(("127.0.0.1", port))
1183 DebugCommunication.__init__(
1184 self, s.makefile("r"), s.makefile("w"), init_commands
1187 def get_pid(self):
1188 if self.process:
1189 return self.process.pid
1190 return -1
1192 def terminate(self):
1193 super(DebugAdaptorServer, self).terminate()
1194 if self.process is not None:
1195 self.process.terminate()
1196 self.process.wait()
1197 self.process = None
1200 def attach_options_specified(options):
1201 if options.pid is not None:
1202 return True
1203 if options.waitFor:
1204 return True
1205 if options.attach:
1206 return True
1207 if options.attachCmds:
1208 return True
1209 return False
1212 def run_vscode(dbg, args, options):
1213 dbg.request_initialize(options.sourceInitFile)
1214 if attach_options_specified(options):
1215 response = dbg.request_attach(
1216 program=options.program,
1217 pid=options.pid,
1218 waitFor=options.waitFor,
1219 attachCommands=options.attachCmds,
1220 initCommands=options.initCmds,
1221 preRunCommands=options.preRunCmds,
1222 stopCommands=options.stopCmds,
1223 exitCommands=options.exitCmds,
1224 terminateCommands=options.terminateCmds,
1226 else:
1227 response = dbg.request_launch(
1228 options.program,
1229 args=args,
1230 env=options.envs,
1231 cwd=options.workingDir,
1232 debuggerRoot=options.debuggerRoot,
1233 sourcePath=options.sourcePath,
1234 initCommands=options.initCmds,
1235 preRunCommands=options.preRunCmds,
1236 stopCommands=options.stopCmds,
1237 exitCommands=options.exitCmds,
1238 terminateCommands=options.terminateCmds,
1241 if response["success"]:
1242 if options.sourceBreakpoints:
1243 source_to_lines = {}
1244 for file_line in options.sourceBreakpoints:
1245 (path, line) = file_line.split(":")
1246 if len(path) == 0 or len(line) == 0:
1247 print('error: invalid source with line "%s"' % (file_line))
1249 else:
1250 if path in source_to_lines:
1251 source_to_lines[path].append(int(line))
1252 else:
1253 source_to_lines[path] = [int(line)]
1254 for source in source_to_lines:
1255 dbg.request_setBreakpoints(source, source_to_lines[source])
1256 if options.funcBreakpoints:
1257 dbg.request_setFunctionBreakpoints(options.funcBreakpoints)
1258 dbg.request_configurationDone()
1259 dbg.wait_for_stopped()
1260 else:
1261 if "message" in response:
1262 print(response["message"])
1263 dbg.request_disconnect(terminateDebuggee=True)
1266 def main():
1267 parser = optparse.OptionParser(
1268 description=(
1269 "A testing framework for the Visual Studio Code Debug Adaptor protocol"
1273 parser.add_option(
1274 "--vscode",
1275 type="string",
1276 dest="vscode_path",
1277 help=(
1278 "The path to the command line program that implements the "
1279 "Visual Studio Code Debug Adaptor protocol."
1281 default=None,
1284 parser.add_option(
1285 "--program",
1286 type="string",
1287 dest="program",
1288 help="The path to the program to debug.",
1289 default=None,
1292 parser.add_option(
1293 "--workingDir",
1294 type="string",
1295 dest="workingDir",
1296 default=None,
1297 help="Set the working directory for the process we launch.",
1300 parser.add_option(
1301 "--sourcePath",
1302 type="string",
1303 dest="sourcePath",
1304 default=None,
1305 help=(
1306 "Set the relative source root for any debug info that has "
1307 "relative paths in it."
1311 parser.add_option(
1312 "--debuggerRoot",
1313 type="string",
1314 dest="debuggerRoot",
1315 default=None,
1316 help=(
1317 "Set the working directory for lldb-dap for any object files "
1318 "with relative paths in the Mach-o debug map."
1322 parser.add_option(
1323 "-r",
1324 "--replay",
1325 type="string",
1326 dest="replay",
1327 help=(
1328 "Specify a file containing a packet log to replay with the "
1329 "current Visual Studio Code Debug Adaptor executable."
1331 default=None,
1334 parser.add_option(
1335 "-g",
1336 "--debug",
1337 action="store_true",
1338 dest="debug",
1339 default=False,
1340 help="Pause waiting for a debugger to attach to the debug adaptor",
1343 parser.add_option(
1344 "--sourceInitFile",
1345 action="store_true",
1346 dest="sourceInitFile",
1347 default=False,
1348 help="Whether lldb-dap should source .lldbinit file or not",
1351 parser.add_option(
1352 "--port",
1353 type="int",
1354 dest="port",
1355 help="Attach a socket to a port instead of using STDIN for VSCode",
1356 default=None,
1359 parser.add_option(
1360 "--pid",
1361 type="int",
1362 dest="pid",
1363 help="The process ID to attach to",
1364 default=None,
1367 parser.add_option(
1368 "--attach",
1369 action="store_true",
1370 dest="attach",
1371 default=False,
1372 help=(
1373 "Specify this option to attach to a process by name. The "
1374 "process name is the basename of the executable specified with "
1375 "the --program option."
1379 parser.add_option(
1380 "-f",
1381 "--function-bp",
1382 type="string",
1383 action="append",
1384 dest="funcBreakpoints",
1385 help=(
1386 "Specify the name of a function to break at. "
1387 "Can be specified more than once."
1389 default=[],
1392 parser.add_option(
1393 "-s",
1394 "--source-bp",
1395 type="string",
1396 action="append",
1397 dest="sourceBreakpoints",
1398 default=[],
1399 help=(
1400 "Specify source breakpoints to set in the format of "
1401 "<source>:<line>. "
1402 "Can be specified more than once."
1406 parser.add_option(
1407 "--attachCommand",
1408 type="string",
1409 action="append",
1410 dest="attachCmds",
1411 default=[],
1412 help=(
1413 "Specify a LLDB command that will attach to a process. "
1414 "Can be specified more than once."
1418 parser.add_option(
1419 "--initCommand",
1420 type="string",
1421 action="append",
1422 dest="initCmds",
1423 default=[],
1424 help=(
1425 "Specify a LLDB command that will be executed before the target "
1426 "is created. Can be specified more than once."
1430 parser.add_option(
1431 "--preRunCommand",
1432 type="string",
1433 action="append",
1434 dest="preRunCmds",
1435 default=[],
1436 help=(
1437 "Specify a LLDB command that will be executed after the target "
1438 "has been created. Can be specified more than once."
1442 parser.add_option(
1443 "--stopCommand",
1444 type="string",
1445 action="append",
1446 dest="stopCmds",
1447 default=[],
1448 help=(
1449 "Specify a LLDB command that will be executed each time the"
1450 "process stops. Can be specified more than once."
1454 parser.add_option(
1455 "--exitCommand",
1456 type="string",
1457 action="append",
1458 dest="exitCmds",
1459 default=[],
1460 help=(
1461 "Specify a LLDB command that will be executed when the process "
1462 "exits. Can be specified more than once."
1466 parser.add_option(
1467 "--terminateCommand",
1468 type="string",
1469 action="append",
1470 dest="terminateCmds",
1471 default=[],
1472 help=(
1473 "Specify a LLDB command that will be executed when the debugging "
1474 "session is terminated. Can be specified more than once."
1478 parser.add_option(
1479 "--env",
1480 type="string",
1481 action="append",
1482 dest="envs",
1483 default=[],
1484 help=("Specify environment variables to pass to the launched " "process."),
1487 parser.add_option(
1488 "--waitFor",
1489 action="store_true",
1490 dest="waitFor",
1491 default=False,
1492 help=(
1493 "Wait for the next process to be launched whose name matches "
1494 "the basename of the program specified with the --program "
1495 "option"
1499 (options, args) = parser.parse_args(sys.argv[1:])
1501 if options.vscode_path is None and options.port is None:
1502 print(
1503 "error: must either specify a path to a Visual Studio Code "
1504 "Debug Adaptor vscode executable path using the --vscode "
1505 "option, or a port to attach to for an existing lldb-dap "
1506 "using the --port option"
1508 return
1509 dbg = DebugAdaptorServer(executable=options.vscode_path, port=options.port)
1510 if options.debug:
1511 raw_input('Waiting for debugger to attach pid "%i"' % (dbg.get_pid()))
1512 if options.replay:
1513 dbg.replay_packets(options.replay)
1514 else:
1515 run_vscode(dbg, args, options)
1516 dbg.terminate()
1519 if __name__ == "__main__":
1520 main()