xref: /openbsd-src/gnu/llvm/lldb/packages/Python/lldbsuite/test/tools/lldb-vscode/vscode.py (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
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