lldbutil.py revision 8a3b54eb714602c777d57ccc3626440c41dee46a
1"""
2This LLDB module contains miscellaneous utilities.
3"""
4
5import lldb
6import os, sys
7import StringIO
8
9# ===================================================
10# Utilities for locating/checking executable programs
11# ===================================================
12
13def is_exe(fpath):
14    """Return true if fpath is an executable."""
15    return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
16
17def which(program):
18    """Find the full path to a program; return None otherwise."""
19    fpath, fname = os.path.split(program)
20    if fpath:
21        if is_exe(program):
22            return program
23    else:
24        for path in os.environ["PATH"].split(os.pathsep):
25            exe_file = os.path.join(path, program)
26            if is_exe(exe_file):
27                return exe_file
28    return None
29
30# ===========================================
31# Iterator for lldb aggregate data structures
32# ===========================================
33
34def lldb_iter(obj, getsize, getelem):
35    """A generator adaptor for lldb aggregate data structures.
36
37    API clients pass in an aggregate object or a container of it, the name of
38    the method to get the size of the aggregate, and the name of the method to
39    get the element by index.
40
41    Example usages:
42
43    1. Pass an aggregate as the first argument:
44
45    def disassemble_instructions (insts):
46        from lldbutil import lldb_iter
47        for i in lldb_iter(insts, 'GetSize', 'GetInstructionAtIndex'):
48            print i
49
50    2. Pass a container of aggregate which provides APIs to get to the size and
51       the element of the aggregate:
52
53    # Module is a container of symbol table
54    module = target.FindModule(filespec)
55    for symbol in lldb_iter(module, 'GetNumSymbols', 'GetSymbolAtIndex'):
56        name = symbol.GetName()
57        ...
58    """
59    size = getattr(obj, getsize)
60    elem = getattr(obj, getelem)
61    for i in range(size()):
62        yield elem(i)
63
64
65# ===================================================
66# Disassembly for an SBFunction or an SBSymbol object
67# ===================================================
68
69def disassemble(target, function_or_symbol):
70    """Disassemble the function or symbol given a target.
71
72    It returns the disassembly content in a string object.
73    """
74    buf = StringIO.StringIO()
75    insts = function_or_symbol.GetInstructions(target)
76    for i in lldb_iter(insts, 'GetSize', 'GetInstructionAtIndex'):
77        print >> buf, i
78    return buf.getvalue()
79
80
81# ==========================================================
82# Integer (byte size 1, 2, 4, and 8) to bytearray conversion
83# ==========================================================
84
85def int_to_bytearray(val, bytesize):
86    """Utility function to convert an integer into a bytearray.
87
88    It returns the bytearray in the little endian format.  It is easy to get the
89    big endian format, just do ba.reverse() on the returned object.
90    """
91    import struct
92
93    if bytesize == 1:
94        return bytearray([val])
95
96    # Little endian followed by a format character.
97    template = "<%c"
98    if bytesize == 2:
99        fmt = template % 'h'
100    elif bytesize == 4:
101        fmt = template % 'i'
102    elif bytesize == 4:
103        fmt = template % 'q'
104    else:
105        return None
106
107    packed = struct.pack(fmt, val)
108    return bytearray(map(ord, packed))
109
110def bytearray_to_int(bytes, bytesize):
111    """Utility function to convert a bytearray into an integer.
112
113    It interprets the bytearray in the little endian format. For a big endian
114    bytearray, just do ba.reverse() on the object before passing it in.
115    """
116    import struct
117
118    if bytesize == 1:
119        return ba[0]
120
121    # Little endian followed by a format character.
122    template = "<%c"
123    if bytesize == 2:
124        fmt = template % 'h'
125    elif bytesize == 4:
126        fmt = template % 'i'
127    elif bytesize == 4:
128        fmt = template % 'q'
129    else:
130        return None
131
132    unpacked = struct.unpack(fmt, str(bytes))
133    return unpacked[0]
134
135
136# ==============================================================
137# Get the description of an lldb object or None if not available
138# ==============================================================
139def get_description(obj, option=None):
140    """Calls lldb_obj.GetDescription() and returns a string, or None.
141
142    For SBTarget and SBBreakpointLocation lldb objects, an extra option can be
143    passed in to describe the detailed level of description desired:
144        o lldb.eDescriptionLevelBrief
145        o lldb.eDescriptionLevelFull
146        o lldb.eDescriptionLevelVerbose
147    """
148    method = getattr(obj, 'GetDescription')
149    if not method:
150        return None
151    if isinstance(obj, lldb.SBTarget) or isinstance(obj, lldb.SBBreakpointLocation):
152        if option is None:
153            option = lldb.eDescriptionLevelBrief
154
155    stream = lldb.SBStream()
156    if option is None:
157        success = method(stream)
158    else:
159        success = method(stream, option)
160    if not success:
161        return None
162    return stream.GetData()
163
164
165# =================================================
166# Convert some enum value to its string counterpart
167# =================================================
168
169def StateTypeString(enum):
170    """Returns the stateType string given an enum."""
171    if enum == lldb.eStateInvalid:
172        return "invalid"
173    elif enum == lldb.eStateUnloaded:
174        return "unloaded"
175    elif enum == lldb.eStateConnected:
176        return "connected"
177    elif enum == lldb.eStateAttaching:
178        return "attaching"
179    elif enum == lldb.eStateLaunching:
180        return "launching"
181    elif enum == lldb.eStateStopped:
182        return "stopped"
183    elif enum == lldb.eStateRunning:
184        return "running"
185    elif enum == lldb.eStateStepping:
186        return "stepping"
187    elif enum == lldb.eStateCrashed:
188        return "crashed"
189    elif enum == lldb.eStateDetached:
190        return "detached"
191    elif enum == lldb.eStateExited:
192        return "exited"
193    elif enum == lldb.eStateSuspended:
194        return "suspended"
195    else:
196        raise Exception("Unknown StateType enum")
197
198def StopReasonString(enum):
199    """Returns the stopReason string given an enum."""
200    if enum == lldb.eStopReasonInvalid:
201        return "invalid"
202    elif enum == lldb.eStopReasonNone:
203        return "none"
204    elif enum == lldb.eStopReasonTrace:
205        return "trace"
206    elif enum == lldb.eStopReasonBreakpoint:
207        return "breakpoint"
208    elif enum == lldb.eStopReasonWatchpoint:
209        return "watchpoint"
210    elif enum == lldb.eStopReasonSignal:
211        return "signal"
212    elif enum == lldb.eStopReasonException:
213        return "exception"
214    elif enum == lldb.eStopReasonPlanComplete:
215        return "plancomplete"
216    else:
217        raise Exception("Unknown StopReason enum")
218
219def ValueTypeString(enum):
220    """Returns the valueType string given an enum."""
221    if enum == lldb.eValueTypeInvalid:
222        return "invalid"
223    elif enum == lldb.eValueTypeVariableGlobal:
224        return "global_variable"
225    elif enum == lldb.eValueTypeVariableStatic:
226        return "static_variable"
227    elif enum == lldb.eValueTypeVariableArgument:
228        return "argument_variable"
229    elif enum == lldb.eValueTypeVariableLocal:
230        return "local_variable"
231    elif enum == lldb.eValueTypeRegister:
232        return "register"
233    elif enum == lldb.eValueTypeRegisterSet:
234        return "register_set"
235    elif enum == lldb.eValueTypeConstResult:
236        return "constant_result"
237    else:
238        raise Exception("Unknown ValueType enum")
239
240
241# ==================================================
242# Utility functions related to Threads and Processes
243# ==================================================
244
245def get_stopped_threads(process, reason):
246    """Returns the thread(s) with the specified stop reason in a list."""
247    threads = []
248    for t in lldb_iter(process, 'GetNumThreads', 'GetThreadAtIndex'):
249        if t.GetStopReason() == reason:
250            threads.append(t)
251    return threads
252
253def get_stopped_thread(process, reason):
254    """A convenience function which returns the first thread with the given stop
255    reason or None.
256
257    Example usages:
258
259    1. Get the stopped thread due to a breakpoint condition
260
261    ...
262        from lldbutil import get_stopped_thread
263        thread = get_stopped_thread(self.process, lldb.eStopReasonPlanComplete)
264        self.assertTrue(thread != None, "There should be a thread stopped due to breakpoint condition")
265    ...
266
267    2. Get the thread stopped due to a breakpoint
268
269    ...
270        from lldbutil import get_stopped_thread
271        thread = get_stopped_thread(self.process, lldb.eStopReasonBreakpoint)
272        self.assertTrue(thread != None, "There should be a thread stopped due to breakpoint")
273    ...
274
275    """
276    threads = get_stopped_threads(process, reason)
277    if len(threads) == 0:
278        return None
279    return threads[0]
280
281def get_threads_stopped_at_breakpoint (process, bkpt):
282    """ For a stopped process returns the thread stopped at the breakpoint passed in bkpt"""
283    stopped_threads = []
284    threads = []
285
286    stopped_threads = get_stopped_threads (process, lldb.eStopReasonBreakpoint)
287
288    if len(stopped_threads) == 0:
289        return threads
290
291    for thread in stopped_threads:
292    # Make sure we've hit our breakpoint...
293        break_id = thread.GetStopReasonDataAtIndex (0)
294        if break_id == bkpt.GetID():
295            threads.append(thread)
296
297    return threads
298
299def continue_to_breakpoint (process, bkpt):
300    """ Continues the process, if it stops, returns the threads stopped at bkpt; otherwise, returns None"""
301    process.Continue()
302    if process.GetState() != lldb.eStateStopped:
303        return None
304    else:
305        return get_threads_stopped_at_breakpoint (process, bkpt)
306
307def get_caller_symbol(thread):
308    """
309    Returns the symbol name for the call site of the leaf function.
310    """
311    depth = thread.GetNumFrames()
312    if depth <= 1:
313        return None
314    caller = thread.GetFrameAtIndex(1).GetSymbol()
315    if caller:
316        return caller.GetName()
317    else:
318        return None
319
320
321def get_function_names(thread):
322    """
323    Returns a sequence of function names from the stack frames of this thread.
324    """
325    def GetFuncName(i):
326        return thread.GetFrameAtIndex(i).GetFunction().GetName()
327
328    return map(GetFuncName, range(thread.GetNumFrames()))
329
330
331def get_symbol_names(thread):
332    """
333    Returns a sequence of symbols for this thread.
334    """
335    def GetSymbol(i):
336        return thread.GetFrameAtIndex(i).GetSymbol().GetName()
337
338    return map(GetSymbol, range(thread.GetNumFrames()))
339
340
341def get_pc_addresses(thread):
342    """
343    Returns a sequence of pc addresses for this thread.
344    """
345    def GetPCAddress(i):
346        return thread.GetFrameAtIndex(i).GetPCAddress()
347
348    return map(GetPCAddress, range(thread.GetNumFrames()))
349
350
351def get_filenames(thread):
352    """
353    Returns a sequence of file names from the stack frames of this thread.
354    """
355    def GetFilename(i):
356        return thread.GetFrameAtIndex(i).GetLineEntry().GetFileSpec().GetFilename()
357
358    return map(GetFilename, range(thread.GetNumFrames()))
359
360
361def get_line_numbers(thread):
362    """
363    Returns a sequence of line numbers from the stack frames of this thread.
364    """
365    def GetLineNumber(i):
366        return thread.GetFrameAtIndex(i).GetLineEntry().GetLine()
367
368    return map(GetLineNumber, range(thread.GetNumFrames()))
369
370
371def get_module_names(thread):
372    """
373    Returns a sequence of module names from the stack frames of this thread.
374    """
375    def GetModuleName(i):
376        return thread.GetFrameAtIndex(i).GetModule().GetFileSpec().GetFilename()
377
378    return map(GetModuleName, range(thread.GetNumFrames()))
379
380
381def get_stack_frames(thread):
382    """
383    Returns a sequence of stack frames for this thread.
384    """
385    def GetStackFrame(i):
386        return thread.GetFrameAtIndex(i)
387
388    return map(GetStackFrame, range(thread.GetNumFrames()))
389
390
391def print_stacktrace(thread, string_buffer = False):
392    """Prints a simple stack trace of this thread."""
393
394    output = StringIO.StringIO() if string_buffer else sys.stdout
395    target = thread.GetProcess().GetTarget()
396
397    depth = thread.GetNumFrames()
398
399    mods = get_module_names(thread)
400    funcs = get_function_names(thread)
401    symbols = get_symbol_names(thread)
402    files = get_filenames(thread)
403    lines = get_line_numbers(thread)
404    addrs = get_pc_addresses(thread)
405
406    if thread.GetStopReason() != lldb.eStopReasonInvalid:
407        desc =  "stop reason=" + StopReasonString(thread.GetStopReason())
408    else:
409        desc = ""
410    print >> output, "Stack trace for thread id={0:#x} name={1} queue={2} ".format(
411        thread.GetThreadID(), thread.GetName(), thread.GetQueueName()) + desc
412
413    for i in range(depth):
414        frame = thread.GetFrameAtIndex(i)
415        function = frame.GetFunction()
416
417        load_addr = addrs[i].GetLoadAddress(target)
418        if not function.IsValid():
419            file_addr = addrs[i].GetFileAddress()
420            print >> output, "  frame #{num}: {addr:#016x} {mod}`{symbol} + ????".format(
421                num=i, addr=load_addr, mod=mods[i], symbol=symbols[i])
422        else:
423            print >> output, "  frame #{num}: {addr:#016x} {mod}`{func} at {file}:{line}".format(
424                num=i, addr=load_addr, mod=mods[i], func=funcs[i], file=files[i], line=lines[i])
425
426    if string_buffer:
427        return output.getvalue()
428
429
430def print_stacktraces(process, string_buffer = False):
431    """Prints the stack traces of all the threads."""
432
433    output = StringIO.StringIO() if string_buffer else sys.stdout
434
435    print >> output, "Stack traces for " + repr(process)
436
437    for i in range(process.GetNumThreads()):
438        print >> output, print_stacktrace(process.GetThreadAtIndex(i), string_buffer=True)
439
440    if string_buffer:
441        return output.getvalue()
442