1061da546Spatrick#!/usr/bin/env python 2061da546Spatrick 3061da546Spatrickimport binascii 4061da546Spatrickimport json 5061da546Spatrickimport optparse 6061da546Spatrickimport os 7061da546Spatrickimport pprint 8061da546Spatrickimport socket 9061da546Spatrickimport string 10061da546Spatrickimport subprocess 11061da546Spatrickimport sys 12061da546Spatrickimport threading 13dda28197Spatrickimport time 14061da546Spatrick 15061da546Spatrick 16061da546Spatrickdef dump_memory(base_addr, data, num_per_line, outfile): 17061da546Spatrick 18061da546Spatrick data_len = len(data) 19061da546Spatrick hex_string = binascii.hexlify(data) 20061da546Spatrick addr = base_addr 21061da546Spatrick ascii_str = '' 22061da546Spatrick i = 0 23061da546Spatrick while i < data_len: 24061da546Spatrick outfile.write('0x%8.8x: ' % (addr + i)) 25061da546Spatrick bytes_left = data_len - i 26061da546Spatrick if bytes_left >= num_per_line: 27061da546Spatrick curr_data_len = num_per_line 28061da546Spatrick else: 29061da546Spatrick curr_data_len = bytes_left 30061da546Spatrick hex_start_idx = i * 2 31061da546Spatrick hex_end_idx = hex_start_idx + curr_data_len * 2 32061da546Spatrick curr_hex_str = hex_string[hex_start_idx:hex_end_idx] 33061da546Spatrick # 'curr_hex_str' now contains the hex byte string for the 34061da546Spatrick # current line with no spaces between bytes 35061da546Spatrick t = iter(curr_hex_str) 36061da546Spatrick # Print hex bytes separated by space 37061da546Spatrick outfile.write(' '.join(a + b for a, b in zip(t, t))) 38061da546Spatrick # Print two spaces 39061da546Spatrick outfile.write(' ') 40061da546Spatrick # Calculate ASCII string for bytes into 'ascii_str' 41061da546Spatrick ascii_str = '' 42061da546Spatrick for j in range(i, i + curr_data_len): 43061da546Spatrick ch = data[j] 44061da546Spatrick if ch in string.printable and ch not in string.whitespace: 45061da546Spatrick ascii_str += '%c' % (ch) 46061da546Spatrick else: 47061da546Spatrick ascii_str += '.' 48061da546Spatrick # Print ASCII representation and newline 49061da546Spatrick outfile.write(ascii_str) 50061da546Spatrick i = i + curr_data_len 51061da546Spatrick outfile.write('\n') 52061da546Spatrick 53061da546Spatrick 54061da546Spatrickdef read_packet(f, verbose=False, trace_file=None): 55061da546Spatrick '''Decode a JSON packet that starts with the content length and is 56061da546Spatrick followed by the JSON bytes from a file 'f'. Returns None on EOF. 57061da546Spatrick ''' 58061da546Spatrick line = f.readline().decode("utf-8") 59061da546Spatrick if len(line) == 0: 60061da546Spatrick return None # EOF. 61061da546Spatrick 62061da546Spatrick # Watch for line that starts with the prefix 63061da546Spatrick prefix = 'Content-Length: ' 64061da546Spatrick if line.startswith(prefix): 65061da546Spatrick # Decode length of JSON bytes 66061da546Spatrick if verbose: 67061da546Spatrick print('content: "%s"' % (line)) 68061da546Spatrick length = int(line[len(prefix):]) 69061da546Spatrick if verbose: 70061da546Spatrick print('length: "%u"' % (length)) 71061da546Spatrick # Skip empty line 72061da546Spatrick line = f.readline() 73061da546Spatrick if verbose: 74061da546Spatrick print('empty: "%s"' % (line)) 75061da546Spatrick # Read JSON bytes 76061da546Spatrick json_str = f.read(length) 77061da546Spatrick if verbose: 78061da546Spatrick print('json: "%s"' % (json_str)) 79061da546Spatrick if trace_file: 80061da546Spatrick trace_file.write('from adaptor:\n%s\n' % (json_str)) 81061da546Spatrick # Decode the JSON bytes into a python dictionary 82061da546Spatrick return json.loads(json_str) 83061da546Spatrick 84be691f3bSpatrick raise Exception("unexpected malformed message from lldb-vscode: " + line) 85061da546Spatrick 86061da546Spatrick 87061da546Spatrickdef packet_type_is(packet, packet_type): 88061da546Spatrick return 'type' in packet and packet['type'] == packet_type 89061da546Spatrick 90be691f3bSpatrickdef dump_dap_log(log_file): 91be691f3bSpatrick print("========= DEBUG ADAPTER PROTOCOL LOGS =========") 92be691f3bSpatrick if log_file is None: 93be691f3bSpatrick print("no log file available") 94be691f3bSpatrick else: 95be691f3bSpatrick with open(log_file, "r") as file: 96be691f3bSpatrick print(file.read()) 97be691f3bSpatrick print("========= END =========") 98061da546Spatrick 99be691f3bSpatrick 100be691f3bSpatrickdef read_packet_thread(vs_comm, log_file): 101061da546Spatrick done = False 102be691f3bSpatrick try: 103061da546Spatrick while not done: 104061da546Spatrick packet = read_packet(vs_comm.recv, trace_file=vs_comm.trace_file) 105061da546Spatrick # `packet` will be `None` on EOF. We want to pass it down to 106061da546Spatrick # handle_recv_packet anyway so the main thread can handle unexpected 107061da546Spatrick # termination of lldb-vscode and stop waiting for new packets. 108061da546Spatrick done = not vs_comm.handle_recv_packet(packet) 109be691f3bSpatrick finally: 110be691f3bSpatrick dump_dap_log(log_file) 111061da546Spatrick 112061da546Spatrick 113061da546Spatrickclass DebugCommunication(object): 114061da546Spatrick 115be691f3bSpatrick def __init__(self, recv, send, init_commands, log_file=None): 116061da546Spatrick self.trace_file = None 117061da546Spatrick self.send = send 118061da546Spatrick self.recv = recv 119061da546Spatrick self.recv_packets = [] 120061da546Spatrick self.recv_condition = threading.Condition() 121061da546Spatrick self.recv_thread = threading.Thread(target=read_packet_thread, 122be691f3bSpatrick args=(self, log_file)) 123061da546Spatrick self.process_event_body = None 124061da546Spatrick self.exit_status = None 125061da546Spatrick self.initialize_body = None 126061da546Spatrick self.thread_stop_reasons = {} 127dda28197Spatrick self.breakpoint_events = [] 128be691f3bSpatrick self.progress_events = [] 129061da546Spatrick self.sequence = 1 130061da546Spatrick self.threads = None 131061da546Spatrick self.recv_thread.start() 132061da546Spatrick self.output_condition = threading.Condition() 133061da546Spatrick self.output = {} 134061da546Spatrick self.configuration_done_sent = False 135061da546Spatrick self.frame_scopes = {} 136061da546Spatrick self.init_commands = init_commands 137061da546Spatrick 138061da546Spatrick @classmethod 139061da546Spatrick def encode_content(cls, s): 140061da546Spatrick return ("Content-Length: %u\r\n\r\n%s" % (len(s), s)).encode("utf-8") 141061da546Spatrick 142061da546Spatrick @classmethod 143061da546Spatrick def validate_response(cls, command, response): 144061da546Spatrick if command['command'] != response['command']: 145061da546Spatrick raise ValueError('command mismatch in response') 146061da546Spatrick if command['seq'] != response['request_seq']: 147061da546Spatrick raise ValueError('seq mismatch in response') 148061da546Spatrick 149be691f3bSpatrick def get_modules(self): 150be691f3bSpatrick module_list = self.request_modules()['body']['modules'] 151be691f3bSpatrick modules = {} 152be691f3bSpatrick for module in module_list: 153be691f3bSpatrick modules[module['name']] = module 154be691f3bSpatrick return modules 155dda28197Spatrick 156061da546Spatrick def get_output(self, category, timeout=0.0, clear=True): 157061da546Spatrick self.output_condition.acquire() 158061da546Spatrick output = None 159061da546Spatrick if category in self.output: 160061da546Spatrick output = self.output[category] 161061da546Spatrick if clear: 162061da546Spatrick del self.output[category] 163061da546Spatrick elif timeout != 0.0: 164061da546Spatrick self.output_condition.wait(timeout) 165061da546Spatrick if category in self.output: 166061da546Spatrick output = self.output[category] 167061da546Spatrick if clear: 168061da546Spatrick del self.output[category] 169061da546Spatrick self.output_condition.release() 170061da546Spatrick return output 171061da546Spatrick 172dda28197Spatrick def collect_output(self, category, duration, clear=True): 173dda28197Spatrick end_time = time.time() + duration 174dda28197Spatrick collected_output = "" 175dda28197Spatrick while end_time > time.time(): 176dda28197Spatrick output = self.get_output(category, timeout=0.25, clear=clear) 177dda28197Spatrick if output: 178dda28197Spatrick collected_output += output 179dda28197Spatrick return collected_output if collected_output else None 180dda28197Spatrick 181061da546Spatrick def enqueue_recv_packet(self, packet): 182061da546Spatrick self.recv_condition.acquire() 183061da546Spatrick self.recv_packets.append(packet) 184061da546Spatrick self.recv_condition.notify() 185061da546Spatrick self.recv_condition.release() 186061da546Spatrick 187061da546Spatrick def handle_recv_packet(self, packet): 188061da546Spatrick '''Called by the read thread that is waiting for all incoming packets 189061da546Spatrick to store the incoming packet in "self.recv_packets" in a thread safe 190061da546Spatrick way. This function will then signal the "self.recv_condition" to 191061da546Spatrick indicate a new packet is available. Returns True if the caller 192061da546Spatrick should keep calling this function for more packets. 193061da546Spatrick ''' 194dda28197Spatrick # If EOF, notify the read thread by enqueuing a None. 195061da546Spatrick if not packet: 196061da546Spatrick self.enqueue_recv_packet(None) 197061da546Spatrick return False 198061da546Spatrick 199061da546Spatrick # Check the packet to see if is an event packet 200061da546Spatrick keepGoing = True 201061da546Spatrick packet_type = packet['type'] 202061da546Spatrick if packet_type == 'event': 203061da546Spatrick event = packet['event'] 204061da546Spatrick body = None 205061da546Spatrick if 'body' in packet: 206061da546Spatrick body = packet['body'] 207061da546Spatrick # Handle the event packet and cache information from these packets 208061da546Spatrick # as they come in 209061da546Spatrick if event == 'output': 210061da546Spatrick # Store any output we receive so clients can retrieve it later. 211061da546Spatrick category = body['category'] 212061da546Spatrick output = body['output'] 213061da546Spatrick self.output_condition.acquire() 214061da546Spatrick if category in self.output: 215061da546Spatrick self.output[category] += output 216061da546Spatrick else: 217061da546Spatrick self.output[category] = output 218061da546Spatrick self.output_condition.notify() 219061da546Spatrick self.output_condition.release() 220dda28197Spatrick # no need to add 'output' event packets to our packets list 221061da546Spatrick return keepGoing 222061da546Spatrick elif event == 'process': 223061da546Spatrick # When a new process is attached or launched, remember the 224061da546Spatrick # details that are available in the body of the event 225061da546Spatrick self.process_event_body = body 226061da546Spatrick elif event == 'stopped': 227061da546Spatrick # Each thread that stops with a reason will send a 228061da546Spatrick # 'stopped' event. We need to remember the thread stop 229061da546Spatrick # reasons since the 'threads' command doesn't return 230061da546Spatrick # that information. 231061da546Spatrick self._process_stopped() 232061da546Spatrick tid = body['threadId'] 233061da546Spatrick self.thread_stop_reasons[tid] = body 234dda28197Spatrick elif event == 'breakpoint': 235dda28197Spatrick # Breakpoint events come in when a breakpoint has locations 236dda28197Spatrick # added or removed. Keep track of them so we can look for them 237dda28197Spatrick # in tests. 238dda28197Spatrick self.breakpoint_events.append(packet) 239dda28197Spatrick # no need to add 'breakpoint' event packets to our packets list 240dda28197Spatrick return keepGoing 241be691f3bSpatrick elif event.startswith('progress'): 242be691f3bSpatrick # Progress events come in as 'progressStart', 'progressUpdate', 243be691f3bSpatrick # and 'progressEnd' events. Keep these around in case test 244be691f3bSpatrick # cases want to verify them. 245be691f3bSpatrick self.progress_events.append(packet) 246be691f3bSpatrick # No need to add 'progress' event packets to our packets list. 247dda28197Spatrick return keepGoing 248dda28197Spatrick 249061da546Spatrick elif packet_type == 'response': 250061da546Spatrick if packet['command'] == 'disconnect': 251061da546Spatrick keepGoing = False 252061da546Spatrick self.enqueue_recv_packet(packet) 253061da546Spatrick return keepGoing 254061da546Spatrick 255061da546Spatrick def send_packet(self, command_dict, set_sequence=True): 256061da546Spatrick '''Take the "command_dict" python dictionary and encode it as a JSON 257061da546Spatrick string and send the contents as a packet to the VSCode debug 258061da546Spatrick adaptor''' 259061da546Spatrick # Set the sequence ID for this command automatically 260061da546Spatrick if set_sequence: 261061da546Spatrick command_dict['seq'] = self.sequence 262061da546Spatrick self.sequence += 1 263061da546Spatrick # Encode our command dictionary as a JSON string 264061da546Spatrick json_str = json.dumps(command_dict, separators=(',', ':')) 265061da546Spatrick if self.trace_file: 266061da546Spatrick self.trace_file.write('to adaptor:\n%s\n' % (json_str)) 267061da546Spatrick length = len(json_str) 268061da546Spatrick if length > 0: 269061da546Spatrick # Send the encoded JSON packet and flush the 'send' file 270061da546Spatrick self.send.write(self.encode_content(json_str)) 271061da546Spatrick self.send.flush() 272061da546Spatrick 273061da546Spatrick def recv_packet(self, filter_type=None, filter_event=None, timeout=None): 274061da546Spatrick '''Get a JSON packet from the VSCode debug adaptor. This function 275061da546Spatrick assumes a thread that reads packets is running and will deliver 276061da546Spatrick any received packets by calling handle_recv_packet(...). This 277061da546Spatrick function will wait for the packet to arrive and return it when 278061da546Spatrick it does.''' 279061da546Spatrick while True: 280061da546Spatrick try: 281061da546Spatrick self.recv_condition.acquire() 282061da546Spatrick packet = None 283061da546Spatrick while True: 284061da546Spatrick for (i, curr_packet) in enumerate(self.recv_packets): 285061da546Spatrick if not curr_packet: 286061da546Spatrick raise EOFError 287061da546Spatrick packet_type = curr_packet['type'] 288061da546Spatrick if filter_type is None or packet_type in filter_type: 289061da546Spatrick if (filter_event is None or 290061da546Spatrick (packet_type == 'event' and 291061da546Spatrick curr_packet['event'] in filter_event)): 292061da546Spatrick packet = self.recv_packets.pop(i) 293061da546Spatrick break 294061da546Spatrick if packet: 295061da546Spatrick break 296061da546Spatrick # Sleep until packet is received 297061da546Spatrick len_before = len(self.recv_packets) 298061da546Spatrick self.recv_condition.wait(timeout) 299061da546Spatrick len_after = len(self.recv_packets) 300061da546Spatrick if len_before == len_after: 301061da546Spatrick return None # Timed out 302061da546Spatrick return packet 303061da546Spatrick except EOFError: 304061da546Spatrick return None 305061da546Spatrick finally: 306061da546Spatrick self.recv_condition.release() 307061da546Spatrick 308061da546Spatrick return None 309061da546Spatrick 310061da546Spatrick def send_recv(self, command): 311061da546Spatrick '''Send a command python dictionary as JSON and receive the JSON 312061da546Spatrick response. Validates that the response is the correct sequence and 313061da546Spatrick command in the reply. Any events that are received are added to the 314061da546Spatrick events list in this object''' 315061da546Spatrick self.send_packet(command) 316061da546Spatrick done = False 317061da546Spatrick while not done: 318be691f3bSpatrick response_or_request = self.recv_packet(filter_type=['response', 'request']) 319be691f3bSpatrick if response_or_request is None: 320061da546Spatrick desc = 'no response for "%s"' % (command['command']) 321061da546Spatrick raise ValueError(desc) 322be691f3bSpatrick if response_or_request['type'] == 'response': 323be691f3bSpatrick self.validate_response(command, response_or_request) 324be691f3bSpatrick return response_or_request 325be691f3bSpatrick else: 326be691f3bSpatrick if response_or_request['command'] == 'runInTerminal': 327be691f3bSpatrick subprocess.Popen(response_or_request['arguments']['args'], 328be691f3bSpatrick env=response_or_request['arguments']['env']) 329be691f3bSpatrick self.send_packet({ 330be691f3bSpatrick "type": "response", 331be691f3bSpatrick "seq": -1, 332be691f3bSpatrick "request_seq": response_or_request['seq'], 333be691f3bSpatrick "success": True, 334be691f3bSpatrick "command": "runInTerminal", 335be691f3bSpatrick "body": {} 336be691f3bSpatrick }, set_sequence=False) 337be691f3bSpatrick else: 338be691f3bSpatrick desc = 'unkonwn reverse request "%s"' % (response_or_request['command']) 339be691f3bSpatrick raise ValueError(desc) 340be691f3bSpatrick 341061da546Spatrick return None 342061da546Spatrick 343061da546Spatrick def wait_for_event(self, filter=None, timeout=None): 344061da546Spatrick while True: 345061da546Spatrick return self.recv_packet(filter_type='event', filter_event=filter, 346061da546Spatrick timeout=timeout) 347061da546Spatrick return None 348061da546Spatrick 349061da546Spatrick def wait_for_stopped(self, timeout=None): 350061da546Spatrick stopped_events = [] 351061da546Spatrick stopped_event = self.wait_for_event(filter=['stopped', 'exited'], 352061da546Spatrick timeout=timeout) 353061da546Spatrick exited = False 354061da546Spatrick while stopped_event: 355061da546Spatrick stopped_events.append(stopped_event) 356061da546Spatrick # If we exited, then we are done 357061da546Spatrick if stopped_event['event'] == 'exited': 358061da546Spatrick self.exit_status = stopped_event['body']['exitCode'] 359061da546Spatrick exited = True 360061da546Spatrick break 361061da546Spatrick # Otherwise we stopped and there might be one or more 'stopped' 362061da546Spatrick # events for each thread that stopped with a reason, so keep 363061da546Spatrick # checking for more 'stopped' events and return all of them 364061da546Spatrick stopped_event = self.wait_for_event(filter='stopped', timeout=0.25) 365061da546Spatrick if exited: 366061da546Spatrick self.threads = [] 367061da546Spatrick return stopped_events 368061da546Spatrick 369061da546Spatrick def wait_for_exited(self): 370061da546Spatrick event_dict = self.wait_for_event('exited') 371061da546Spatrick if event_dict is None: 372*f6aab3d8Srobert raise ValueError("didn't get exited event") 373*f6aab3d8Srobert return event_dict 374*f6aab3d8Srobert 375*f6aab3d8Srobert def wait_for_terminated(self): 376*f6aab3d8Srobert event_dict = self.wait_for_event('terminated') 377*f6aab3d8Srobert if event_dict is None: 378*f6aab3d8Srobert raise ValueError("didn't get terminated event") 379061da546Spatrick return event_dict 380061da546Spatrick 381061da546Spatrick def get_initialize_value(self, key): 382061da546Spatrick '''Get a value for the given key if it there is a key/value pair in 383061da546Spatrick the "initialize" request response body. 384061da546Spatrick ''' 385061da546Spatrick if self.initialize_body and key in self.initialize_body: 386061da546Spatrick return self.initialize_body[key] 387061da546Spatrick return None 388061da546Spatrick 389061da546Spatrick def get_threads(self): 390061da546Spatrick if self.threads is None: 391061da546Spatrick self.request_threads() 392061da546Spatrick return self.threads 393061da546Spatrick 394061da546Spatrick def get_thread_id(self, threadIndex=0): 395061da546Spatrick '''Utility function to get the first thread ID in the thread list. 396061da546Spatrick If the thread list is empty, then fetch the threads. 397061da546Spatrick ''' 398061da546Spatrick if self.threads is None: 399061da546Spatrick self.request_threads() 400061da546Spatrick if self.threads and threadIndex < len(self.threads): 401061da546Spatrick return self.threads[threadIndex]['id'] 402061da546Spatrick return None 403061da546Spatrick 404061da546Spatrick def get_stackFrame(self, frameIndex=0, threadId=None): 405061da546Spatrick '''Get a single "StackFrame" object from a "stackTrace" request and 406061da546Spatrick return the "StackFrame as a python dictionary, or None on failure 407061da546Spatrick ''' 408061da546Spatrick if threadId is None: 409061da546Spatrick threadId = self.get_thread_id() 410061da546Spatrick if threadId is None: 411061da546Spatrick print('invalid threadId') 412061da546Spatrick return None 413061da546Spatrick response = self.request_stackTrace(threadId, startFrame=frameIndex, 414061da546Spatrick levels=1) 415061da546Spatrick if response: 416061da546Spatrick return response['body']['stackFrames'][0] 417061da546Spatrick print('invalid response') 418061da546Spatrick return None 419061da546Spatrick 420061da546Spatrick def get_completions(self, text): 421061da546Spatrick response = self.request_completions(text) 422061da546Spatrick return response['body']['targets'] 423061da546Spatrick 424061da546Spatrick def get_scope_variables(self, scope_name, frameIndex=0, threadId=None): 425061da546Spatrick stackFrame = self.get_stackFrame(frameIndex=frameIndex, 426061da546Spatrick threadId=threadId) 427061da546Spatrick if stackFrame is None: 428061da546Spatrick return [] 429061da546Spatrick frameId = stackFrame['id'] 430061da546Spatrick if frameId in self.frame_scopes: 431061da546Spatrick frame_scopes = self.frame_scopes[frameId] 432061da546Spatrick else: 433061da546Spatrick scopes_response = self.request_scopes(frameId) 434061da546Spatrick frame_scopes = scopes_response['body']['scopes'] 435061da546Spatrick self.frame_scopes[frameId] = frame_scopes 436061da546Spatrick for scope in frame_scopes: 437061da546Spatrick if scope['name'] == scope_name: 438061da546Spatrick varRef = scope['variablesReference'] 439061da546Spatrick variables_response = self.request_variables(varRef) 440061da546Spatrick if variables_response: 441061da546Spatrick if 'body' in variables_response: 442061da546Spatrick body = variables_response['body'] 443061da546Spatrick if 'variables' in body: 444061da546Spatrick vars = body['variables'] 445061da546Spatrick return vars 446061da546Spatrick return [] 447061da546Spatrick 448061da546Spatrick def get_global_variables(self, frameIndex=0, threadId=None): 449061da546Spatrick return self.get_scope_variables('Globals', frameIndex=frameIndex, 450061da546Spatrick threadId=threadId) 451061da546Spatrick 452061da546Spatrick def get_local_variables(self, frameIndex=0, threadId=None): 453061da546Spatrick return self.get_scope_variables('Locals', frameIndex=frameIndex, 454061da546Spatrick threadId=threadId) 455061da546Spatrick 456*f6aab3d8Srobert def get_registers(self, frameIndex=0, threadId=None): 457*f6aab3d8Srobert return self.get_scope_variables('Registers', frameIndex=frameIndex, 458*f6aab3d8Srobert threadId=threadId) 459*f6aab3d8Srobert 460061da546Spatrick def get_local_variable(self, name, frameIndex=0, threadId=None): 461061da546Spatrick locals = self.get_local_variables(frameIndex=frameIndex, 462061da546Spatrick threadId=threadId) 463061da546Spatrick for local in locals: 464061da546Spatrick if 'name' in local and local['name'] == name: 465061da546Spatrick return local 466061da546Spatrick return None 467061da546Spatrick 468061da546Spatrick def get_local_variable_value(self, name, frameIndex=0, threadId=None): 469061da546Spatrick variable = self.get_local_variable(name, frameIndex=frameIndex, 470061da546Spatrick threadId=threadId) 471061da546Spatrick if variable and 'value' in variable: 472061da546Spatrick return variable['value'] 473061da546Spatrick return None 474061da546Spatrick 475061da546Spatrick def replay_packets(self, replay_file_path): 476061da546Spatrick f = open(replay_file_path, 'r') 477061da546Spatrick mode = 'invalid' 478061da546Spatrick set_sequence = False 479061da546Spatrick command_dict = None 480061da546Spatrick while mode != 'eof': 481061da546Spatrick if mode == 'invalid': 482061da546Spatrick line = f.readline() 483061da546Spatrick if line.startswith('to adapter:'): 484061da546Spatrick mode = 'send' 485061da546Spatrick elif line.startswith('from adapter:'): 486061da546Spatrick mode = 'recv' 487061da546Spatrick elif mode == 'send': 488061da546Spatrick command_dict = read_packet(f) 489061da546Spatrick # Skip the end of line that follows the JSON 490061da546Spatrick f.readline() 491061da546Spatrick if command_dict is None: 492061da546Spatrick raise ValueError('decode packet failed from replay file') 493061da546Spatrick print('Sending:') 494061da546Spatrick pprint.PrettyPrinter(indent=2).pprint(command_dict) 495061da546Spatrick # raw_input('Press ENTER to send:') 496061da546Spatrick self.send_packet(command_dict, set_sequence) 497061da546Spatrick mode = 'invalid' 498061da546Spatrick elif mode == 'recv': 499061da546Spatrick print('Replay response:') 500061da546Spatrick replay_response = read_packet(f) 501061da546Spatrick # Skip the end of line that follows the JSON 502061da546Spatrick f.readline() 503061da546Spatrick pprint.PrettyPrinter(indent=2).pprint(replay_response) 504061da546Spatrick actual_response = self.recv_packet() 505061da546Spatrick if actual_response: 506061da546Spatrick type = actual_response['type'] 507061da546Spatrick print('Actual response:') 508061da546Spatrick if type == 'response': 509061da546Spatrick self.validate_response(command_dict, actual_response) 510061da546Spatrick pprint.PrettyPrinter(indent=2).pprint(actual_response) 511061da546Spatrick else: 512061da546Spatrick print("error: didn't get a valid response") 513061da546Spatrick mode = 'invalid' 514061da546Spatrick 515061da546Spatrick def request_attach(self, program=None, pid=None, waitFor=None, trace=None, 516061da546Spatrick initCommands=None, preRunCommands=None, 517061da546Spatrick stopCommands=None, exitCommands=None, 518dda28197Spatrick attachCommands=None, terminateCommands=None, 519*f6aab3d8Srobert coreFile=None, postRunCommands=None, 520*f6aab3d8Srobert sourceMap=None): 521061da546Spatrick args_dict = {} 522061da546Spatrick if pid is not None: 523061da546Spatrick args_dict['pid'] = pid 524061da546Spatrick if program is not None: 525061da546Spatrick args_dict['program'] = program 526061da546Spatrick if waitFor is not None: 527061da546Spatrick args_dict['waitFor'] = waitFor 528061da546Spatrick if trace: 529061da546Spatrick args_dict['trace'] = trace 530061da546Spatrick args_dict['initCommands'] = self.init_commands 531061da546Spatrick if initCommands: 532061da546Spatrick args_dict['initCommands'].extend(initCommands) 533061da546Spatrick if preRunCommands: 534061da546Spatrick args_dict['preRunCommands'] = preRunCommands 535061da546Spatrick if stopCommands: 536061da546Spatrick args_dict['stopCommands'] = stopCommands 537061da546Spatrick if exitCommands: 538061da546Spatrick args_dict['exitCommands'] = exitCommands 539dda28197Spatrick if terminateCommands: 540dda28197Spatrick args_dict['terminateCommands'] = terminateCommands 541061da546Spatrick if attachCommands: 542061da546Spatrick args_dict['attachCommands'] = attachCommands 543dda28197Spatrick if coreFile: 544dda28197Spatrick args_dict['coreFile'] = coreFile 545be691f3bSpatrick if postRunCommands: 546be691f3bSpatrick args_dict['postRunCommands'] = postRunCommands 547*f6aab3d8Srobert if sourceMap: 548*f6aab3d8Srobert args_dict['sourceMap'] = sourceMap 549061da546Spatrick command_dict = { 550061da546Spatrick 'command': 'attach', 551061da546Spatrick 'type': 'request', 552061da546Spatrick 'arguments': args_dict 553061da546Spatrick } 554061da546Spatrick return self.send_recv(command_dict) 555061da546Spatrick 556061da546Spatrick def request_configurationDone(self): 557061da546Spatrick command_dict = { 558061da546Spatrick 'command': 'configurationDone', 559061da546Spatrick 'type': 'request', 560061da546Spatrick 'arguments': {} 561061da546Spatrick } 562061da546Spatrick response = self.send_recv(command_dict) 563061da546Spatrick if response: 564061da546Spatrick self.configuration_done_sent = True 565061da546Spatrick return response 566061da546Spatrick 567061da546Spatrick def _process_stopped(self): 568061da546Spatrick self.threads = None 569061da546Spatrick self.frame_scopes = {} 570061da546Spatrick 571061da546Spatrick def request_continue(self, threadId=None): 572061da546Spatrick if self.exit_status is not None: 573061da546Spatrick raise ValueError('request_continue called after process exited') 574061da546Spatrick # If we have launched or attached, then the first continue is done by 575061da546Spatrick # sending the 'configurationDone' request 576061da546Spatrick if not self.configuration_done_sent: 577061da546Spatrick return self.request_configurationDone() 578061da546Spatrick args_dict = {} 579061da546Spatrick if threadId is None: 580061da546Spatrick threadId = self.get_thread_id() 581061da546Spatrick args_dict['threadId'] = threadId 582061da546Spatrick command_dict = { 583061da546Spatrick 'command': 'continue', 584061da546Spatrick 'type': 'request', 585061da546Spatrick 'arguments': args_dict 586061da546Spatrick } 587061da546Spatrick response = self.send_recv(command_dict) 588061da546Spatrick # Caller must still call wait_for_stopped. 589061da546Spatrick return response 590061da546Spatrick 591061da546Spatrick def request_disconnect(self, terminateDebuggee=None): 592061da546Spatrick args_dict = {} 593061da546Spatrick if terminateDebuggee is not None: 594061da546Spatrick if terminateDebuggee: 595061da546Spatrick args_dict['terminateDebuggee'] = True 596061da546Spatrick else: 597061da546Spatrick args_dict['terminateDebuggee'] = False 598061da546Spatrick command_dict = { 599061da546Spatrick 'command': 'disconnect', 600061da546Spatrick 'type': 'request', 601061da546Spatrick 'arguments': args_dict 602061da546Spatrick } 603061da546Spatrick return self.send_recv(command_dict) 604061da546Spatrick 605be691f3bSpatrick def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None): 606061da546Spatrick stackFrame = self.get_stackFrame(frameIndex=frameIndex, 607061da546Spatrick threadId=threadId) 608061da546Spatrick if stackFrame is None: 609061da546Spatrick return [] 610061da546Spatrick args_dict = { 611061da546Spatrick 'expression': expression, 612be691f3bSpatrick 'context': context, 613061da546Spatrick 'frameId': stackFrame['id'], 614061da546Spatrick } 615061da546Spatrick command_dict = { 616061da546Spatrick 'command': 'evaluate', 617061da546Spatrick 'type': 'request', 618061da546Spatrick 'arguments': args_dict 619061da546Spatrick } 620061da546Spatrick return self.send_recv(command_dict) 621061da546Spatrick 622*f6aab3d8Srobert def request_initialize(self, sourceInitFile): 623061da546Spatrick command_dict = { 624061da546Spatrick 'command': 'initialize', 625061da546Spatrick 'type': 'request', 626061da546Spatrick 'arguments': { 627061da546Spatrick 'adapterID': 'lldb-native', 628061da546Spatrick 'clientID': 'vscode', 629061da546Spatrick 'columnsStartAt1': True, 630061da546Spatrick 'linesStartAt1': True, 631061da546Spatrick 'locale': 'en-us', 632061da546Spatrick 'pathFormat': 'path', 633061da546Spatrick 'supportsRunInTerminalRequest': True, 634061da546Spatrick 'supportsVariablePaging': True, 635*f6aab3d8Srobert 'supportsVariableType': True, 636*f6aab3d8Srobert 'sourceInitFile': sourceInitFile 637061da546Spatrick } 638061da546Spatrick } 639061da546Spatrick response = self.send_recv(command_dict) 640061da546Spatrick if response: 641061da546Spatrick if 'body' in response: 642061da546Spatrick self.initialize_body = response['body'] 643061da546Spatrick return response 644061da546Spatrick 645061da546Spatrick def request_launch(self, program, args=None, cwd=None, env=None, 646061da546Spatrick stopOnEntry=False, disableASLR=True, 647061da546Spatrick disableSTDIO=False, shellExpandArguments=False, 648061da546Spatrick trace=False, initCommands=None, preRunCommands=None, 649dda28197Spatrick stopCommands=None, exitCommands=None, 650dda28197Spatrick terminateCommands=None ,sourcePath=None, 651be691f3bSpatrick debuggerRoot=None, launchCommands=None, sourceMap=None, 652be691f3bSpatrick runInTerminal=False, expectFailure=False, 653be691f3bSpatrick postRunCommands=None): 654061da546Spatrick args_dict = { 655061da546Spatrick 'program': program 656061da546Spatrick } 657061da546Spatrick if args: 658061da546Spatrick args_dict['args'] = args 659061da546Spatrick if cwd: 660061da546Spatrick args_dict['cwd'] = cwd 661061da546Spatrick if env: 662061da546Spatrick args_dict['env'] = env 663061da546Spatrick if stopOnEntry: 664061da546Spatrick args_dict['stopOnEntry'] = stopOnEntry 665061da546Spatrick if disableASLR: 666061da546Spatrick args_dict['disableASLR'] = disableASLR 667061da546Spatrick if disableSTDIO: 668061da546Spatrick args_dict['disableSTDIO'] = disableSTDIO 669061da546Spatrick if shellExpandArguments: 670061da546Spatrick args_dict['shellExpandArguments'] = shellExpandArguments 671061da546Spatrick if trace: 672061da546Spatrick args_dict['trace'] = trace 673061da546Spatrick args_dict['initCommands'] = self.init_commands 674061da546Spatrick if initCommands: 675061da546Spatrick args_dict['initCommands'].extend(initCommands) 676061da546Spatrick if preRunCommands: 677061da546Spatrick args_dict['preRunCommands'] = preRunCommands 678061da546Spatrick if stopCommands: 679061da546Spatrick args_dict['stopCommands'] = stopCommands 680061da546Spatrick if exitCommands: 681061da546Spatrick args_dict['exitCommands'] = exitCommands 682dda28197Spatrick if terminateCommands: 683dda28197Spatrick args_dict['terminateCommands'] = terminateCommands 684061da546Spatrick if sourcePath: 685061da546Spatrick args_dict['sourcePath'] = sourcePath 686061da546Spatrick if debuggerRoot: 687061da546Spatrick args_dict['debuggerRoot'] = debuggerRoot 688061da546Spatrick if launchCommands: 689061da546Spatrick args_dict['launchCommands'] = launchCommands 690dda28197Spatrick if sourceMap: 691dda28197Spatrick args_dict['sourceMap'] = sourceMap 692be691f3bSpatrick if runInTerminal: 693be691f3bSpatrick args_dict['runInTerminal'] = runInTerminal 694be691f3bSpatrick if postRunCommands: 695be691f3bSpatrick args_dict['postRunCommands'] = postRunCommands 696061da546Spatrick command_dict = { 697061da546Spatrick 'command': 'launch', 698061da546Spatrick 'type': 'request', 699061da546Spatrick 'arguments': args_dict 700061da546Spatrick } 701061da546Spatrick response = self.send_recv(command_dict) 702061da546Spatrick 703be691f3bSpatrick if not expectFailure: 704061da546Spatrick # Wait for a 'process' and 'initialized' event in any order 705061da546Spatrick self.wait_for_event(filter=['process', 'initialized']) 706061da546Spatrick self.wait_for_event(filter=['process', 'initialized']) 707061da546Spatrick return response 708061da546Spatrick 709061da546Spatrick def request_next(self, threadId): 710061da546Spatrick if self.exit_status is not None: 711061da546Spatrick raise ValueError('request_continue called after process exited') 712061da546Spatrick args_dict = {'threadId': threadId} 713061da546Spatrick command_dict = { 714061da546Spatrick 'command': 'next', 715061da546Spatrick 'type': 'request', 716061da546Spatrick 'arguments': args_dict 717061da546Spatrick } 718061da546Spatrick return self.send_recv(command_dict) 719061da546Spatrick 720061da546Spatrick def request_stepIn(self, threadId): 721061da546Spatrick if self.exit_status is not None: 722061da546Spatrick raise ValueError('request_continue called after process exited') 723061da546Spatrick args_dict = {'threadId': threadId} 724061da546Spatrick command_dict = { 725061da546Spatrick 'command': 'stepIn', 726061da546Spatrick 'type': 'request', 727061da546Spatrick 'arguments': args_dict 728061da546Spatrick } 729061da546Spatrick return self.send_recv(command_dict) 730061da546Spatrick 731061da546Spatrick def request_stepOut(self, threadId): 732061da546Spatrick if self.exit_status is not None: 733061da546Spatrick raise ValueError('request_continue called after process exited') 734061da546Spatrick args_dict = {'threadId': threadId} 735061da546Spatrick command_dict = { 736061da546Spatrick 'command': 'stepOut', 737061da546Spatrick 'type': 'request', 738061da546Spatrick 'arguments': args_dict 739061da546Spatrick } 740061da546Spatrick return self.send_recv(command_dict) 741061da546Spatrick 742061da546Spatrick def request_pause(self, threadId=None): 743061da546Spatrick if self.exit_status is not None: 744061da546Spatrick raise ValueError('request_continue called after process exited') 745061da546Spatrick if threadId is None: 746061da546Spatrick threadId = self.get_thread_id() 747061da546Spatrick args_dict = {'threadId': threadId} 748061da546Spatrick command_dict = { 749061da546Spatrick 'command': 'pause', 750061da546Spatrick 'type': 'request', 751061da546Spatrick 'arguments': args_dict 752061da546Spatrick } 753061da546Spatrick return self.send_recv(command_dict) 754061da546Spatrick 755061da546Spatrick def request_scopes(self, frameId): 756061da546Spatrick args_dict = {'frameId': frameId} 757061da546Spatrick command_dict = { 758061da546Spatrick 'command': 'scopes', 759061da546Spatrick 'type': 'request', 760061da546Spatrick 'arguments': args_dict 761061da546Spatrick } 762061da546Spatrick return self.send_recv(command_dict) 763061da546Spatrick 764*f6aab3d8Srobert def request_setBreakpoints(self, file_path, line_array, data=None): 765*f6aab3d8Srobert ''' data is array of parameters for breakpoints in line_array. 766*f6aab3d8Srobert Each parameter object is 1:1 mapping with entries in line_entry. 767*f6aab3d8Srobert It contains optional location/hitCondition/logMessage parameters. 768*f6aab3d8Srobert ''' 769061da546Spatrick (dir, base) = os.path.split(file_path) 770be691f3bSpatrick source_dict = { 771be691f3bSpatrick 'name': base, 772be691f3bSpatrick 'path': file_path 773be691f3bSpatrick } 774be691f3bSpatrick args_dict = { 775be691f3bSpatrick 'source': source_dict, 776be691f3bSpatrick 'sourceModified': False, 777be691f3bSpatrick } 778be691f3bSpatrick if line_array is not None: 779be691f3bSpatrick args_dict['lines'] = '%s' % line_array 780061da546Spatrick breakpoints = [] 781*f6aab3d8Srobert for i, line in enumerate(line_array): 782*f6aab3d8Srobert breakpoint_data = None 783*f6aab3d8Srobert if data is not None and i < len(data): 784*f6aab3d8Srobert breakpoint_data = data[i] 785061da546Spatrick bp = {'line': line} 786*f6aab3d8Srobert if breakpoint_data is not None: 787*f6aab3d8Srobert if 'condition' in breakpoint_data and breakpoint_data['condition']: 788*f6aab3d8Srobert bp['condition'] = breakpoint_data['condition'] 789*f6aab3d8Srobert if 'hitCondition' in breakpoint_data and breakpoint_data['hitCondition']: 790*f6aab3d8Srobert bp['hitCondition'] = breakpoint_data['hitCondition'] 791*f6aab3d8Srobert if 'logMessage' in breakpoint_data and breakpoint_data['logMessage']: 792*f6aab3d8Srobert bp['logMessage'] = breakpoint_data['logMessage'] 793061da546Spatrick breakpoints.append(bp) 794be691f3bSpatrick args_dict['breakpoints'] = breakpoints 795be691f3bSpatrick 796061da546Spatrick command_dict = { 797061da546Spatrick 'command': 'setBreakpoints', 798061da546Spatrick 'type': 'request', 799061da546Spatrick 'arguments': args_dict 800061da546Spatrick } 801061da546Spatrick return self.send_recv(command_dict) 802061da546Spatrick 803061da546Spatrick def request_setExceptionBreakpoints(self, filters): 804061da546Spatrick args_dict = {'filters': filters} 805061da546Spatrick command_dict = { 806061da546Spatrick 'command': 'setExceptionBreakpoints', 807061da546Spatrick 'type': 'request', 808061da546Spatrick 'arguments': args_dict 809061da546Spatrick } 810061da546Spatrick return self.send_recv(command_dict) 811061da546Spatrick 812061da546Spatrick def request_setFunctionBreakpoints(self, names, condition=None, 813061da546Spatrick hitCondition=None): 814061da546Spatrick breakpoints = [] 815061da546Spatrick for name in names: 816061da546Spatrick bp = {'name': name} 817061da546Spatrick if condition is not None: 818061da546Spatrick bp['condition'] = condition 819061da546Spatrick if hitCondition is not None: 820061da546Spatrick bp['hitCondition'] = hitCondition 821061da546Spatrick breakpoints.append(bp) 822061da546Spatrick args_dict = {'breakpoints': breakpoints} 823061da546Spatrick command_dict = { 824061da546Spatrick 'command': 'setFunctionBreakpoints', 825061da546Spatrick 'type': 'request', 826061da546Spatrick 'arguments': args_dict 827061da546Spatrick } 828061da546Spatrick return self.send_recv(command_dict) 829061da546Spatrick 830be691f3bSpatrick def request_compileUnits(self, moduleId): 831dda28197Spatrick args_dict = {'moduleId': moduleId} 832dda28197Spatrick command_dict = { 833be691f3bSpatrick 'command': 'compileUnits', 834dda28197Spatrick 'type': 'request', 835dda28197Spatrick 'arguments': args_dict 836dda28197Spatrick } 837dda28197Spatrick response = self.send_recv(command_dict) 838dda28197Spatrick return response 839dda28197Spatrick 840061da546Spatrick def request_completions(self, text): 841061da546Spatrick args_dict = { 842061da546Spatrick 'text': text, 843061da546Spatrick 'column': len(text) 844061da546Spatrick } 845061da546Spatrick command_dict = { 846061da546Spatrick 'command': 'completions', 847061da546Spatrick 'type': 'request', 848061da546Spatrick 'arguments': args_dict 849061da546Spatrick } 850061da546Spatrick return self.send_recv(command_dict) 851061da546Spatrick 852be691f3bSpatrick def request_modules(self): 853be691f3bSpatrick return self.send_recv({ 854be691f3bSpatrick 'command': 'modules', 855be691f3bSpatrick 'type': 'request' 856be691f3bSpatrick }) 857be691f3bSpatrick 858061da546Spatrick def request_stackTrace(self, threadId=None, startFrame=None, levels=None, 859061da546Spatrick dump=False): 860061da546Spatrick if threadId is None: 861061da546Spatrick threadId = self.get_thread_id() 862061da546Spatrick args_dict = {'threadId': threadId} 863061da546Spatrick if startFrame is not None: 864061da546Spatrick args_dict['startFrame'] = startFrame 865061da546Spatrick if levels is not None: 866061da546Spatrick args_dict['levels'] = levels 867061da546Spatrick command_dict = { 868061da546Spatrick 'command': 'stackTrace', 869061da546Spatrick 'type': 'request', 870061da546Spatrick 'arguments': args_dict 871061da546Spatrick } 872061da546Spatrick response = self.send_recv(command_dict) 873061da546Spatrick if dump: 874061da546Spatrick for (idx, frame) in enumerate(response['body']['stackFrames']): 875061da546Spatrick name = frame['name'] 876061da546Spatrick if 'line' in frame and 'source' in frame: 877061da546Spatrick source = frame['source'] 878061da546Spatrick if 'sourceReference' not in source: 879061da546Spatrick if 'name' in source: 880061da546Spatrick source_name = source['name'] 881061da546Spatrick line = frame['line'] 882061da546Spatrick print("[%3u] %s @ %s:%u" % (idx, name, source_name, 883061da546Spatrick line)) 884061da546Spatrick continue 885061da546Spatrick print("[%3u] %s" % (idx, name)) 886061da546Spatrick return response 887061da546Spatrick 888061da546Spatrick def request_threads(self): 889061da546Spatrick '''Request a list of all threads and combine any information from any 890061da546Spatrick "stopped" events since those contain more information about why a 891061da546Spatrick thread actually stopped. Returns an array of thread dictionaries 892061da546Spatrick with information about all threads''' 893061da546Spatrick command_dict = { 894061da546Spatrick 'command': 'threads', 895061da546Spatrick 'type': 'request', 896061da546Spatrick 'arguments': {} 897061da546Spatrick } 898061da546Spatrick response = self.send_recv(command_dict) 899061da546Spatrick body = response['body'] 900061da546Spatrick # Fill in "self.threads" correctly so that clients that call 901061da546Spatrick # self.get_threads() or self.get_thread_id(...) can get information 902061da546Spatrick # on threads when the process is stopped. 903061da546Spatrick if 'threads' in body: 904061da546Spatrick self.threads = body['threads'] 905061da546Spatrick for thread in self.threads: 906061da546Spatrick # Copy the thread dictionary so we can add key/value pairs to 907dda28197Spatrick # it without affecting the original info from the "threads" 908061da546Spatrick # command. 909061da546Spatrick tid = thread['id'] 910061da546Spatrick if tid in self.thread_stop_reasons: 911061da546Spatrick thread_stop_info = self.thread_stop_reasons[tid] 912061da546Spatrick copy_keys = ['reason', 'description', 'text'] 913061da546Spatrick for key in copy_keys: 914061da546Spatrick if key in thread_stop_info: 915061da546Spatrick thread[key] = thread_stop_info[key] 916061da546Spatrick else: 917061da546Spatrick self.threads = None 918061da546Spatrick return response 919061da546Spatrick 920061da546Spatrick def request_variables(self, variablesReference, start=None, count=None): 921061da546Spatrick args_dict = {'variablesReference': variablesReference} 922061da546Spatrick if start is not None: 923061da546Spatrick args_dict['start'] = start 924061da546Spatrick if count is not None: 925061da546Spatrick args_dict['count'] = count 926061da546Spatrick command_dict = { 927061da546Spatrick 'command': 'variables', 928061da546Spatrick 'type': 'request', 929061da546Spatrick 'arguments': args_dict 930061da546Spatrick } 931061da546Spatrick return self.send_recv(command_dict) 932061da546Spatrick 933061da546Spatrick def request_setVariable(self, containingVarRef, name, value, id=None): 934061da546Spatrick args_dict = { 935061da546Spatrick 'variablesReference': containingVarRef, 936061da546Spatrick 'name': name, 937061da546Spatrick 'value': str(value) 938061da546Spatrick } 939061da546Spatrick if id is not None: 940061da546Spatrick args_dict['id'] = id 941061da546Spatrick command_dict = { 942061da546Spatrick 'command': 'setVariable', 943061da546Spatrick 'type': 'request', 944061da546Spatrick 'arguments': args_dict 945061da546Spatrick } 946061da546Spatrick return self.send_recv(command_dict) 947061da546Spatrick 948061da546Spatrick def request_testGetTargetBreakpoints(self): 949061da546Spatrick '''A request packet used in the LLDB test suite to get all currently 950061da546Spatrick set breakpoint infos for all breakpoints currently set in the 951061da546Spatrick target. 952061da546Spatrick ''' 953061da546Spatrick command_dict = { 954061da546Spatrick 'command': '_testGetTargetBreakpoints', 955061da546Spatrick 'type': 'request', 956061da546Spatrick 'arguments': {} 957061da546Spatrick } 958061da546Spatrick return self.send_recv(command_dict) 959061da546Spatrick 960061da546Spatrick def terminate(self): 961061da546Spatrick self.send.close() 962061da546Spatrick # self.recv.close() 963061da546Spatrick 964061da546Spatrick 965061da546Spatrickclass DebugAdaptor(DebugCommunication): 966be691f3bSpatrick def __init__(self, executable=None, port=None, init_commands=[], log_file=None, env=None): 967061da546Spatrick self.process = None 968061da546Spatrick if executable is not None: 969dda28197Spatrick adaptor_env = os.environ.copy() 970be691f3bSpatrick if env is not None: 971be691f3bSpatrick adaptor_env.update(env) 972be691f3bSpatrick 973dda28197Spatrick if log_file: 974dda28197Spatrick adaptor_env['LLDBVSCODE_LOG'] = log_file 975061da546Spatrick self.process = subprocess.Popen([executable], 976061da546Spatrick stdin=subprocess.PIPE, 977061da546Spatrick stdout=subprocess.PIPE, 978dda28197Spatrick stderr=subprocess.PIPE, 979dda28197Spatrick env=adaptor_env) 980061da546Spatrick DebugCommunication.__init__(self, self.process.stdout, 981be691f3bSpatrick self.process.stdin, init_commands, log_file) 982061da546Spatrick elif port is not None: 983061da546Spatrick s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 984061da546Spatrick s.connect(('127.0.0.1', port)) 985061da546Spatrick DebugCommunication.__init__(self, s.makefile('r'), s.makefile('w'), 986061da546Spatrick init_commands) 987061da546Spatrick 988061da546Spatrick def get_pid(self): 989061da546Spatrick if self.process: 990061da546Spatrick return self.process.pid 991061da546Spatrick return -1 992061da546Spatrick 993061da546Spatrick def terminate(self): 994061da546Spatrick super(DebugAdaptor, self).terminate() 995061da546Spatrick if self.process is not None: 996061da546Spatrick self.process.terminate() 997061da546Spatrick self.process.wait() 998061da546Spatrick self.process = None 999061da546Spatrick 1000061da546Spatrick 1001061da546Spatrickdef attach_options_specified(options): 1002061da546Spatrick if options.pid is not None: 1003061da546Spatrick return True 1004061da546Spatrick if options.waitFor: 1005061da546Spatrick return True 1006061da546Spatrick if options.attach: 1007061da546Spatrick return True 1008061da546Spatrick if options.attachCmds: 1009061da546Spatrick return True 1010061da546Spatrick return False 1011061da546Spatrick 1012061da546Spatrick 1013061da546Spatrickdef run_vscode(dbg, args, options): 1014*f6aab3d8Srobert dbg.request_initialize(options.sourceInitFile) 1015061da546Spatrick if attach_options_specified(options): 1016061da546Spatrick response = dbg.request_attach(program=options.program, 1017061da546Spatrick pid=options.pid, 1018061da546Spatrick waitFor=options.waitFor, 1019061da546Spatrick attachCommands=options.attachCmds, 1020061da546Spatrick initCommands=options.initCmds, 1021061da546Spatrick preRunCommands=options.preRunCmds, 1022061da546Spatrick stopCommands=options.stopCmds, 1023dda28197Spatrick exitCommands=options.exitCmds, 1024dda28197Spatrick terminateCommands=options.terminateCmds) 1025061da546Spatrick else: 1026061da546Spatrick response = dbg.request_launch(options.program, 1027061da546Spatrick args=args, 1028061da546Spatrick env=options.envs, 1029061da546Spatrick cwd=options.workingDir, 1030061da546Spatrick debuggerRoot=options.debuggerRoot, 1031061da546Spatrick sourcePath=options.sourcePath, 1032061da546Spatrick initCommands=options.initCmds, 1033061da546Spatrick preRunCommands=options.preRunCmds, 1034061da546Spatrick stopCommands=options.stopCmds, 1035dda28197Spatrick exitCommands=options.exitCmds, 1036dda28197Spatrick terminateCommands=options.terminateCmds) 1037061da546Spatrick 1038061da546Spatrick if response['success']: 1039061da546Spatrick if options.sourceBreakpoints: 1040061da546Spatrick source_to_lines = {} 1041061da546Spatrick for file_line in options.sourceBreakpoints: 1042061da546Spatrick (path, line) = file_line.split(':') 1043061da546Spatrick if len(path) == 0 or len(line) == 0: 1044061da546Spatrick print('error: invalid source with line "%s"' % 1045061da546Spatrick (file_line)) 1046061da546Spatrick 1047061da546Spatrick else: 1048061da546Spatrick if path in source_to_lines: 1049061da546Spatrick source_to_lines[path].append(int(line)) 1050061da546Spatrick else: 1051061da546Spatrick source_to_lines[path] = [int(line)] 1052061da546Spatrick for source in source_to_lines: 1053061da546Spatrick dbg.request_setBreakpoints(source, source_to_lines[source]) 1054061da546Spatrick if options.funcBreakpoints: 1055061da546Spatrick dbg.request_setFunctionBreakpoints(options.funcBreakpoints) 1056061da546Spatrick dbg.request_configurationDone() 1057061da546Spatrick dbg.wait_for_stopped() 1058061da546Spatrick else: 1059061da546Spatrick if 'message' in response: 1060061da546Spatrick print(response['message']) 1061061da546Spatrick dbg.request_disconnect(terminateDebuggee=True) 1062061da546Spatrick 1063061da546Spatrick 1064061da546Spatrickdef main(): 1065061da546Spatrick parser = optparse.OptionParser( 1066061da546Spatrick description=('A testing framework for the Visual Studio Code Debug ' 1067061da546Spatrick 'Adaptor protocol')) 1068061da546Spatrick 1069061da546Spatrick parser.add_option( 1070061da546Spatrick '--vscode', 1071061da546Spatrick type='string', 1072061da546Spatrick dest='vscode_path', 1073061da546Spatrick help=('The path to the command line program that implements the ' 1074061da546Spatrick 'Visual Studio Code Debug Adaptor protocol.'), 1075061da546Spatrick default=None) 1076061da546Spatrick 1077061da546Spatrick parser.add_option( 1078061da546Spatrick '--program', 1079061da546Spatrick type='string', 1080061da546Spatrick dest='program', 1081061da546Spatrick help='The path to the program to debug.', 1082061da546Spatrick default=None) 1083061da546Spatrick 1084061da546Spatrick parser.add_option( 1085061da546Spatrick '--workingDir', 1086061da546Spatrick type='string', 1087061da546Spatrick dest='workingDir', 1088061da546Spatrick default=None, 1089061da546Spatrick help='Set the working directory for the process we launch.') 1090061da546Spatrick 1091061da546Spatrick parser.add_option( 1092061da546Spatrick '--sourcePath', 1093061da546Spatrick type='string', 1094061da546Spatrick dest='sourcePath', 1095061da546Spatrick default=None, 1096061da546Spatrick help=('Set the relative source root for any debug info that has ' 1097061da546Spatrick 'relative paths in it.')) 1098061da546Spatrick 1099061da546Spatrick parser.add_option( 1100061da546Spatrick '--debuggerRoot', 1101061da546Spatrick type='string', 1102061da546Spatrick dest='debuggerRoot', 1103061da546Spatrick default=None, 1104061da546Spatrick help=('Set the working directory for lldb-vscode for any object files ' 1105061da546Spatrick 'with relative paths in the Mach-o debug map.')) 1106061da546Spatrick 1107061da546Spatrick parser.add_option( 1108061da546Spatrick '-r', '--replay', 1109061da546Spatrick type='string', 1110061da546Spatrick dest='replay', 1111061da546Spatrick help=('Specify a file containing a packet log to replay with the ' 1112061da546Spatrick 'current Visual Studio Code Debug Adaptor executable.'), 1113061da546Spatrick default=None) 1114061da546Spatrick 1115061da546Spatrick parser.add_option( 1116061da546Spatrick '-g', '--debug', 1117061da546Spatrick action='store_true', 1118061da546Spatrick dest='debug', 1119061da546Spatrick default=False, 1120061da546Spatrick help='Pause waiting for a debugger to attach to the debug adaptor') 1121061da546Spatrick 1122061da546Spatrick parser.add_option( 1123*f6aab3d8Srobert '--sourceInitFile', 1124*f6aab3d8Srobert action='store_true', 1125*f6aab3d8Srobert dest='sourceInitFile', 1126*f6aab3d8Srobert default=False, 1127*f6aab3d8Srobert help='Whether lldb-vscode should source .lldbinit file or not') 1128*f6aab3d8Srobert 1129*f6aab3d8Srobert parser.add_option( 1130061da546Spatrick '--port', 1131061da546Spatrick type='int', 1132061da546Spatrick dest='port', 1133061da546Spatrick help="Attach a socket to a port instead of using STDIN for VSCode", 1134061da546Spatrick default=None) 1135061da546Spatrick 1136061da546Spatrick parser.add_option( 1137061da546Spatrick '--pid', 1138061da546Spatrick type='int', 1139061da546Spatrick dest='pid', 1140061da546Spatrick help="The process ID to attach to", 1141061da546Spatrick default=None) 1142061da546Spatrick 1143061da546Spatrick parser.add_option( 1144061da546Spatrick '--attach', 1145061da546Spatrick action='store_true', 1146061da546Spatrick dest='attach', 1147061da546Spatrick default=False, 1148061da546Spatrick help=('Specify this option to attach to a process by name. The ' 1149dda28197Spatrick 'process name is the basename of the executable specified with ' 1150061da546Spatrick 'the --program option.')) 1151061da546Spatrick 1152061da546Spatrick parser.add_option( 1153061da546Spatrick '-f', '--function-bp', 1154061da546Spatrick type='string', 1155061da546Spatrick action='append', 1156061da546Spatrick dest='funcBreakpoints', 1157061da546Spatrick help=('Specify the name of a function to break at. ' 1158061da546Spatrick 'Can be specified more than once.'), 1159061da546Spatrick default=[]) 1160061da546Spatrick 1161061da546Spatrick parser.add_option( 1162061da546Spatrick '-s', '--source-bp', 1163061da546Spatrick type='string', 1164061da546Spatrick action='append', 1165061da546Spatrick dest='sourceBreakpoints', 1166061da546Spatrick default=[], 1167061da546Spatrick help=('Specify source breakpoints to set in the format of ' 1168061da546Spatrick '<source>:<line>. ' 1169061da546Spatrick 'Can be specified more than once.')) 1170061da546Spatrick 1171061da546Spatrick parser.add_option( 1172061da546Spatrick '--attachCommand', 1173061da546Spatrick type='string', 1174061da546Spatrick action='append', 1175061da546Spatrick dest='attachCmds', 1176061da546Spatrick default=[], 1177061da546Spatrick help=('Specify a LLDB command that will attach to a process. ' 1178061da546Spatrick 'Can be specified more than once.')) 1179061da546Spatrick 1180061da546Spatrick parser.add_option( 1181061da546Spatrick '--initCommand', 1182061da546Spatrick type='string', 1183061da546Spatrick action='append', 1184061da546Spatrick dest='initCmds', 1185061da546Spatrick default=[], 1186061da546Spatrick help=('Specify a LLDB command that will be executed before the target ' 1187061da546Spatrick 'is created. Can be specified more than once.')) 1188061da546Spatrick 1189061da546Spatrick parser.add_option( 1190061da546Spatrick '--preRunCommand', 1191061da546Spatrick type='string', 1192061da546Spatrick action='append', 1193061da546Spatrick dest='preRunCmds', 1194061da546Spatrick default=[], 1195061da546Spatrick help=('Specify a LLDB command that will be executed after the target ' 1196061da546Spatrick 'has been created. Can be specified more than once.')) 1197061da546Spatrick 1198061da546Spatrick parser.add_option( 1199061da546Spatrick '--stopCommand', 1200061da546Spatrick type='string', 1201061da546Spatrick action='append', 1202061da546Spatrick dest='stopCmds', 1203061da546Spatrick default=[], 1204061da546Spatrick help=('Specify a LLDB command that will be executed each time the' 1205061da546Spatrick 'process stops. Can be specified more than once.')) 1206061da546Spatrick 1207061da546Spatrick parser.add_option( 1208061da546Spatrick '--exitCommand', 1209061da546Spatrick type='string', 1210061da546Spatrick action='append', 1211061da546Spatrick dest='exitCmds', 1212061da546Spatrick default=[], 1213061da546Spatrick help=('Specify a LLDB command that will be executed when the process ' 1214061da546Spatrick 'exits. Can be specified more than once.')) 1215061da546Spatrick 1216061da546Spatrick parser.add_option( 1217dda28197Spatrick '--terminateCommand', 1218dda28197Spatrick type='string', 1219dda28197Spatrick action='append', 1220dda28197Spatrick dest='terminateCmds', 1221dda28197Spatrick default=[], 1222dda28197Spatrick help=('Specify a LLDB command that will be executed when the debugging ' 1223dda28197Spatrick 'session is terminated. Can be specified more than once.')) 1224dda28197Spatrick 1225dda28197Spatrick parser.add_option( 1226061da546Spatrick '--env', 1227061da546Spatrick type='string', 1228061da546Spatrick action='append', 1229061da546Spatrick dest='envs', 1230061da546Spatrick default=[], 1231061da546Spatrick help=('Specify environment variables to pass to the launched ' 1232061da546Spatrick 'process.')) 1233061da546Spatrick 1234061da546Spatrick parser.add_option( 1235061da546Spatrick '--waitFor', 1236061da546Spatrick action='store_true', 1237061da546Spatrick dest='waitFor', 1238061da546Spatrick default=False, 1239061da546Spatrick help=('Wait for the next process to be launched whose name matches ' 1240061da546Spatrick 'the basename of the program specified with the --program ' 1241061da546Spatrick 'option')) 1242061da546Spatrick 1243061da546Spatrick (options, args) = parser.parse_args(sys.argv[1:]) 1244061da546Spatrick 1245061da546Spatrick if options.vscode_path is None and options.port is None: 1246061da546Spatrick print('error: must either specify a path to a Visual Studio Code ' 1247061da546Spatrick 'Debug Adaptor vscode executable path using the --vscode ' 1248061da546Spatrick 'option, or a port to attach to for an existing lldb-vscode ' 1249061da546Spatrick 'using the --port option') 1250061da546Spatrick return 1251061da546Spatrick dbg = DebugAdaptor(executable=options.vscode_path, port=options.port) 1252061da546Spatrick if options.debug: 1253061da546Spatrick raw_input('Waiting for debugger to attach pid "%i"' % ( 1254061da546Spatrick dbg.get_pid())) 1255061da546Spatrick if options.replay: 1256061da546Spatrick dbg.replay_packets(options.replay) 1257061da546Spatrick else: 1258061da546Spatrick run_vscode(dbg, args, options) 1259061da546Spatrick dbg.terminate() 1260061da546Spatrick 1261061da546Spatrick 1262061da546Spatrickif __name__ == '__main__': 1263061da546Spatrick main() 1264