diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 |
commit | 9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch) | |
tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /examples/python | |
parent | 3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff) |
Notes
Diffstat (limited to 'examples/python')
30 files changed, 9672 insertions, 0 deletions
diff --git a/examples/python/cmdtemplate.py b/examples/python/cmdtemplate.py new file mode 100644 index 0000000000000..ca575362f9b7e --- /dev/null +++ b/examples/python/cmdtemplate.py @@ -0,0 +1,76 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# # To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command +# (lldb) command script import /path/to/cmdtemplate.py +#---------------------------------------------------------------------- + +import lldb +import commands +import optparse +import shlex + +def create_framestats_options(): + usage = "usage: %prog [options]" + description='''This command is meant to be an example of how to make an LLDB command that +does something useful, follows best practices, and exploits the SB API. +Specifically, this command computes the aggregate and average size of the variables in the current frame +and allows you to tweak exactly which variables are to be accounted in the computation. +''' + parser = optparse.OptionParser(description=description, prog='framestats',usage=usage) + parser.add_option('-i', '--in-scope', action='store_true', dest='inscope', help='in_scope_only = True', default=False) + parser.add_option('-a', '--arguments', action='store_true', dest='arguments', help='arguments = True', default=False) + parser.add_option('-l', '--locals', action='store_true', dest='locals', help='locals = True', default=False) + parser.add_option('-s', '--statics', action='store_true', dest='statics', help='statics = True', default=False) + return parser + +def the_framestats_command(debugger, command, result, dict): + # Use the Shell Lexer to properly parse up command options just like a + # shell would + command_args = shlex.split(command) + parser = create_framestats_options() + try: + (options, args) = parser.parse_args(command_args) + except: + # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + result.SetError ("option parsing failed") + return + + # in a command - the lldb.* convenience variables are not to be used + # and their values (if any) are undefined + # this is the best practice to access those objects from within a command + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + if not frame.IsValid(): + return "no frame here" + # from now on, replace lldb.<thing>.whatever with <thing>.whatever + variables_list = frame.GetVariables(options.arguments, options.locals, options.statics, options.inscope) + variables_count = variables_list.GetSize() + if variables_count == 0: + print >> result, "no variables here" + return + total_size = 0 + for i in range(0,variables_count): + variable = variables_list.GetValueAtIndex(i) + variable_type = variable.GetType() + total_size = total_size + variable_type.GetByteSize() + average_size = float(total_size) / variables_count + print >>result, "Your frame has %d variables. Their total size is %d bytes. The average size is %f bytes" % (variables_count,total_size,average_size) + # not returning anything is akin to returning success + +def __lldb_init_module (debugger, dict): + # This initializer is being run from LLDB in the embedded command interpreter + # Make the options so we can generate the help text for the new LLDB + # command line command prior to registering it with LLDB below + parser = create_framestats_options() + the_framestats_command.__doc__ = parser.format_help() + # Add any commands contained in this module to LLDB + debugger.HandleCommand('command script add -f cmdtemplate.the_framestats_command framestats') + print 'The "framestats" command has been installed, type "help framestats" or "framestats --help" for detailed help.' diff --git a/examples/python/crashlog.py b/examples/python/crashlog.py new file mode 100755 index 0000000000000..60a6a1f50f005 --- /dev/null +++ b/examples/python/crashlog.py @@ -0,0 +1,829 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb": +# +# cd /path/containing/crashlog.py +# lldb +# (lldb) script import crashlog +# "crashlog" command installed, type "crashlog --help" for detailed help +# (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash +# +# The benefit of running the crashlog command inside lldb in the +# embedded python interpreter is when the command completes, there +# will be a target with all of the files loaded at the locations +# described in the crash log. Only the files that have stack frames +# in the backtrace will be loaded unless the "--load-all" option +# has been specified. This allows users to explore the program in the +# state it was in right at crash time. +# +# On MacOSX csh, tcsh: +# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash ) +# +# On MacOSX sh, bash: +# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash +#---------------------------------------------------------------------- + +import commands +import cmd +import datetime +import glob +import optparse +import os +import platform +import plistlib +import pprint # pp = pprint.PrettyPrinter(indent=4); pp.pprint(command_args) +import re +import shlex +import string +import sys +import time +import uuid + +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + +from lldb.utils import symbolication + +PARSE_MODE_NORMAL = 0 +PARSE_MODE_THREAD = 1 +PARSE_MODE_IMAGES = 2 +PARSE_MODE_THREGS = 3 +PARSE_MODE_SYSTEM = 4 + +class CrashLog(symbolication.Symbolicator): + """Class that does parses darwin crash logs""" + parent_process_regex = re.compile('^Parent Process:\s*(.*)\[(\d+)\]'); + thread_state_regex = re.compile('^Thread ([0-9]+) crashed with') + thread_regex = re.compile('^Thread ([0-9]+)([^:]*):(.*)') + app_backtrace_regex = re.compile('^Application Specific Backtrace ([0-9]+)([^:]*):(.*)') + frame_regex = re.compile('^([0-9]+)\s+([^ ]+)\s+(0x[0-9a-fA-F]+) +(.*)') + image_regex_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^<]+)<([-0-9a-fA-F]+)> (.*)'); + image_regex_no_uuid = re.compile('(0x[0-9a-fA-F]+)[- ]+(0x[0-9a-fA-F]+) +[+]?([^ ]+) +([^/]+)/(.*)'); + empty_line_regex = re.compile('^$') + + class Thread: + """Class that represents a thread in a darwin crash log""" + def __init__(self, index, app_specific_backtrace): + self.index = index + self.frames = list() + self.idents = list() + self.registers = dict() + self.reason = None + self.queue = None + self.app_specific_backtrace = app_specific_backtrace + + def dump(self, prefix): + if self.app_specific_backtrace: + print "%Application Specific Backtrace[%u] %s" % (prefix, self.index, self.reason) + else: + print "%sThread[%u] %s" % (prefix, self.index, self.reason) + if self.frames: + print "%s Frames:" % (prefix) + for frame in self.frames: + frame.dump(prefix + ' ') + if self.registers: + print "%s Registers:" % (prefix) + for reg in self.registers.keys(): + print "%s %-5s = %#16.16x" % (prefix, reg, self.registers[reg]) + + def dump_symbolicated (self, crash_log, options): + this_thread_crashed = self.app_specific_backtrace + if not this_thread_crashed: + this_thread_crashed = self.did_crash() + if options.crashed_only and this_thread_crashed == False: + return + + print "%s" % self + #prev_frame_index = -1 + display_frame_idx = -1 + for frame_idx, frame in enumerate(self.frames): + disassemble = (this_thread_crashed or options.disassemble_all_threads) and frame_idx < options.disassemble_depth; + if frame_idx == 0: + symbolicated_frame_addresses = crash_log.symbolicate (frame.pc & crash_log.addr_mask, options.verbose) + else: + # Any frame above frame zero and we have to subtract one to get the previous line entry + symbolicated_frame_addresses = crash_log.symbolicate ((frame.pc & crash_log.addr_mask) - 1, options.verbose) + + if symbolicated_frame_addresses: + symbolicated_frame_address_idx = 0 + for symbolicated_frame_address in symbolicated_frame_addresses: + display_frame_idx += 1 + print '[%3u] %s' % (frame_idx, symbolicated_frame_address) + if (options.source_all or self.did_crash()) and display_frame_idx < options.source_frames and options.source_context: + source_context = options.source_context + line_entry = symbolicated_frame_address.get_symbol_context().line_entry + if line_entry.IsValid(): + strm = lldb.SBStream() + if line_entry: + lldb.debugger.GetSourceManager().DisplaySourceLinesWithLineNumbers(line_entry.file, line_entry.line, source_context, source_context, "->", strm) + source_text = strm.GetData() + if source_text: + # Indent the source a bit + indent_str = ' ' + join_str = '\n' + indent_str + print '%s%s' % (indent_str, join_str.join(source_text.split('\n'))) + if symbolicated_frame_address_idx == 0: + if disassemble: + instructions = symbolicated_frame_address.get_instructions() + if instructions: + print + symbolication.disassemble_instructions (crash_log.get_target(), + instructions, + frame.pc, + options.disassemble_before, + options.disassemble_after, frame.index > 0) + print + symbolicated_frame_address_idx += 1 + else: + print frame + + def add_ident(self, ident): + if not ident in self.idents: + self.idents.append(ident) + + def did_crash(self): + return self.reason != None + + def __str__(self): + if self.app_specific_backtrace: + s = "Application Specific Backtrace[%u]" % self.index + else: + s = "Thread[%u]" % self.index + if self.reason: + s += ' %s' % self.reason + return s + + + class Frame: + """Class that represents a stack frame in a thread in a darwin crash log""" + def __init__(self, index, pc, description): + self.pc = pc + self.description = description + self.index = index + + def __str__(self): + if self.description: + return "[%3u] 0x%16.16x %s" % (self.index, self.pc, self.description) + else: + return "[%3u] 0x%16.16x" % (self.index, self.pc) + + def dump(self, prefix): + print "%s%s" % (prefix, str(self)) + + class DarwinImage(symbolication.Image): + """Class that represents a binary images in a darwin crash log""" + dsymForUUIDBinary = os.path.expanduser('~rc/bin/dsymForUUID') + if not os.path.exists(dsymForUUIDBinary): + dsymForUUIDBinary = commands.getoutput('which dsymForUUID') + + dwarfdump_uuid_regex = re.compile('UUID: ([-0-9a-fA-F]+) \(([^\(]+)\) .*') + + def __init__(self, text_addr_lo, text_addr_hi, identifier, version, uuid, path): + symbolication.Image.__init__(self, path, uuid); + self.add_section (symbolication.Section(text_addr_lo, text_addr_hi, "__TEXT")) + self.identifier = identifier + self.version = version + + def locate_module_and_debug_symbols(self): + # Don't load a module twice... + if self.resolved: + return True + # Mark this as resolved so we don't keep trying + self.resolved = True + uuid_str = self.get_normalized_uuid_string() + print 'Getting symbols for %s %s...' % (uuid_str, self.path), + if os.path.exists(self.dsymForUUIDBinary): + dsym_for_uuid_command = '%s %s' % (self.dsymForUUIDBinary, uuid_str) + s = commands.getoutput(dsym_for_uuid_command) + if s: + plist_root = plistlib.readPlistFromString (s) + if plist_root: + plist = plist_root[uuid_str] + if plist: + if 'DBGArchitecture' in plist: + self.arch = plist['DBGArchitecture'] + if 'DBGDSYMPath' in plist: + self.symfile = os.path.realpath(plist['DBGDSYMPath']) + if 'DBGSymbolRichExecutable' in plist: + self.path = os.path.expanduser (plist['DBGSymbolRichExecutable']) + self.resolved_path = self.path + if not self.resolved_path and os.path.exists(self.path): + dwarfdump_cmd_output = commands.getoutput('dwarfdump --uuid "%s"' % self.path) + self_uuid = self.get_uuid() + for line in dwarfdump_cmd_output.splitlines(): + match = self.dwarfdump_uuid_regex.search (line) + if match: + dwarf_uuid_str = match.group(1) + dwarf_uuid = uuid.UUID(dwarf_uuid_str) + if self_uuid == dwarf_uuid: + self.resolved_path = self.path + self.arch = match.group(2) + break; + if not self.resolved_path: + self.unavailable = True + print "error\n error: unable to locate '%s' with UUID %s" % (self.path, uuid_str) + return False + if (self.resolved_path and os.path.exists(self.resolved_path)) or (self.path and os.path.exists(self.path)): + print 'ok' + # if self.resolved_path: + # print ' exe = "%s"' % self.resolved_path + # if self.symfile: + # print ' dsym = "%s"' % self.symfile + return True + else: + self.unavailable = True + return False + + + + def __init__(self, path): + """CrashLog constructor that take a path to a darwin crash log file""" + symbolication.Symbolicator.__init__(self); + self.path = os.path.expanduser(path); + self.info_lines = list() + self.system_profile = list() + self.threads = list() + self.backtraces = list() # For application specific backtraces + self.idents = list() # A list of the required identifiers for doing all stack backtraces + self.crashed_thread_idx = -1 + self.version = -1 + self.error = None + self.target = None + # With possible initial component of ~ or ~user replaced by that user's home directory. + try: + f = open(self.path) + except IOError: + self.error = 'error: cannot open "%s"' % self.path + return + + self.file_lines = f.read().splitlines() + parse_mode = PARSE_MODE_NORMAL + thread = None + app_specific_backtrace = False + for line in self.file_lines: + # print line + line_len = len(line) + if line_len == 0: + if thread: + if parse_mode == PARSE_MODE_THREAD: + if thread.index == self.crashed_thread_idx: + thread.reason = '' + if self.thread_exception: + thread.reason += self.thread_exception + if self.thread_exception_data: + thread.reason += " (%s)" % self.thread_exception_data + if app_specific_backtrace: + self.backtraces.append(thread) + else: + self.threads.append(thread) + thread = None + else: + # only append an extra empty line if the previous line + # in the info_lines wasn't empty + if len(self.info_lines) > 0 and len(self.info_lines[-1]): + self.info_lines.append(line) + parse_mode = PARSE_MODE_NORMAL + # print 'PARSE_MODE_NORMAL' + elif parse_mode == PARSE_MODE_NORMAL: + if line.startswith ('Process:'): + (self.process_name, pid_with_brackets) = line[8:].strip().split(' [') + self.process_id = pid_with_brackets.strip('[]') + elif line.startswith ('Path:'): + self.process_path = line[5:].strip() + elif line.startswith ('Identifier:'): + self.process_identifier = line[11:].strip() + elif line.startswith ('Version:'): + version_string = line[8:].strip() + matched_pair = re.search("(.+)\((.+)\)", version_string) + if matched_pair: + self.process_version = matched_pair.group(1) + self.process_compatability_version = matched_pair.group(2) + else: + self.process = version_string + self.process_compatability_version = version_string + elif self.parent_process_regex.search(line): + parent_process_match = self.parent_process_regex.search(line) + self.parent_process_name = parent_process_match.group(1) + self.parent_process_id = parent_process_match.group(2) + elif line.startswith ('Exception Type:'): + self.thread_exception = line[15:].strip() + continue + elif line.startswith ('Exception Codes:'): + self.thread_exception_data = line[16:].strip() + continue + elif line.startswith ('Crashed Thread:'): + self.crashed_thread_idx = int(line[15:].strip().split()[0]) + continue + elif line.startswith ('Report Version:'): + self.version = int(line[15:].strip()) + continue + elif line.startswith ('System Profile:'): + parse_mode = PARSE_MODE_SYSTEM + continue + elif (line.startswith ('Interval Since Last Report:') or + line.startswith ('Crashes Since Last Report:') or + line.startswith ('Per-App Interval Since Last Report:') or + line.startswith ('Per-App Crashes Since Last Report:') or + line.startswith ('Sleep/Wake UUID:') or + line.startswith ('Anonymous UUID:')): + # ignore these + continue + elif line.startswith ('Thread'): + thread_state_match = self.thread_state_regex.search (line) + if thread_state_match: + app_specific_backtrace = False + thread_state_match = self.thread_regex.search (line) + thread_idx = int(thread_state_match.group(1)) + parse_mode = PARSE_MODE_THREGS + thread = self.threads[thread_idx] + else: + thread_match = self.thread_regex.search (line) + if thread_match: + app_specific_backtrace = False + parse_mode = PARSE_MODE_THREAD + thread_idx = int(thread_match.group(1)) + thread = CrashLog.Thread(thread_idx, False) + continue + elif line.startswith ('Binary Images:'): + parse_mode = PARSE_MODE_IMAGES + continue + elif line.startswith ('Application Specific Backtrace'): + app_backtrace_match = self.app_backtrace_regex.search (line) + if app_backtrace_match: + parse_mode = PARSE_MODE_THREAD + app_specific_backtrace = True + idx = int(app_backtrace_match.group(1)) + thread = CrashLog.Thread(idx, True) + self.info_lines.append(line.strip()) + elif parse_mode == PARSE_MODE_THREAD: + if line.startswith ('Thread'): + continue + frame_match = self.frame_regex.search(line) + if frame_match: + ident = frame_match.group(2) + thread.add_ident(ident) + if not ident in self.idents: + self.idents.append(ident) + thread.frames.append (CrashLog.Frame(int(frame_match.group(1)), int(frame_match.group(3), 0), frame_match.group(4))) + else: + print 'error: frame regex failed for line: "%s"' % line + elif parse_mode == PARSE_MODE_IMAGES: + image_match = self.image_regex_uuid.search (line) + if image_match: + image = CrashLog.DarwinImage (int(image_match.group(1),0), + int(image_match.group(2),0), + image_match.group(3).strip(), + image_match.group(4).strip(), + uuid.UUID(image_match.group(5)), + image_match.group(6)) + self.images.append (image) + else: + image_match = self.image_regex_no_uuid.search (line) + if image_match: + image = CrashLog.DarwinImage (int(image_match.group(1),0), + int(image_match.group(2),0), + image_match.group(3).strip(), + image_match.group(4).strip(), + None, + image_match.group(5)) + self.images.append (image) + else: + print "error: image regex failed for: %s" % line + + elif parse_mode == PARSE_MODE_THREGS: + stripped_line = line.strip() + # "r12: 0x00007fff6b5939c8 r13: 0x0000000007000006 r14: 0x0000000000002a03 r15: 0x0000000000000c00" + reg_values = re.findall ('([a-zA-Z0-9]+: 0[Xx][0-9a-fA-F]+) *', stripped_line); + for reg_value in reg_values: + #print 'reg_value = "%s"' % reg_value + (reg, value) = reg_value.split(': ') + #print 'reg = "%s"' % reg + #print 'value = "%s"' % value + thread.registers[reg.strip()] = int(value, 0) + elif parse_mode == PARSE_MODE_SYSTEM: + self.system_profile.append(line) + f.close() + + def dump(self): + print "Crash Log File: %s" % (self.path) + if self.backtraces: + print "\nApplication Specific Backtraces:" + for thread in self.backtraces: + thread.dump(' ') + print "\nThreads:" + for thread in self.threads: + thread.dump(' ') + print "\nImages:" + for image in self.images: + image.dump(' ') + + def find_image_with_identifier(self, identifier): + for image in self.images: + if image.identifier == identifier: + return image + regex_text = '^.*\.%s$' % (identifier) + regex = re.compile(regex_text) + for image in self.images: + if regex.match(image.identifier): + return image + return None + + def create_target(self): + #print 'crashlog.create_target()...' + if self.target is None: + self.target = symbolication.Symbolicator.create_target(self) + if self.target: + return self.target + # We weren't able to open the main executable as, but we can still symbolicate + print 'crashlog.create_target()...2' + if self.idents: + for ident in self.idents: + image = self.find_image_with_identifier (ident) + if image: + self.target = image.create_target () + if self.target: + return self.target # success + print 'crashlog.create_target()...3' + for image in self.images: + self.target = image.create_target () + if self.target: + return self.target # success + print 'crashlog.create_target()...4' + print 'error: unable to locate any executables from the crash log' + return self.target + + def get_target(self): + return self.target + +def usage(): + print "Usage: lldb-symbolicate.py [-n name] executable-image" + sys.exit(0) + +class Interactive(cmd.Cmd): + '''Interactive prompt for analyzing one or more Darwin crash logs, type "help" to see a list of supported commands.''' + image_option_parser = None + + def __init__(self, crash_logs): + cmd.Cmd.__init__(self) + self.use_rawinput = False + self.intro = 'Interactive crashlogs prompt, type "help" to see a list of supported commands.' + self.crash_logs = crash_logs + self.prompt = '% ' + + def default(self, line): + '''Catch all for unknown command, which will exit the interpreter.''' + print "uknown command: %s" % line + return True + + def do_q(self, line): + '''Quit command''' + return True + + def do_quit(self, line): + '''Quit command''' + return True + + def do_symbolicate(self, line): + description='''Symbolicate one or more darwin crash log files by index to provide source file and line information, + inlined stack frames back to the concrete functions, and disassemble the location of the crash + for the first frame of the crashed thread.''' + option_parser = CreateSymbolicateCrashLogOptions ('symbolicate', description, False) + command_args = shlex.split(line) + try: + (options, args) = option_parser.parse_args(command_args) + except: + return + + if args: + # We have arguments, they must valid be crash log file indexes + for idx_str in args: + idx = int(idx_str) + if idx < len(self.crash_logs): + SymbolicateCrashLog (self.crash_logs[idx], options) + else: + print 'error: crash log index %u is out of range' % (idx) + else: + # No arguments, symbolicate all crash logs using the options provided + for idx in range(len(self.crash_logs)): + SymbolicateCrashLog (self.crash_logs[idx], options) + + def do_list(self, line=None): + '''Dump a list of all crash logs that are currently loaded. + + USAGE: list''' + print '%u crash logs are loaded:' % len(self.crash_logs) + for (crash_log_idx, crash_log) in enumerate(self.crash_logs): + print '[%u] = %s' % (crash_log_idx, crash_log.path) + + def do_image(self, line): + '''Dump information about one or more binary images in the crash log given an image basename, or all images if no arguments are provided.''' + usage = "usage: %prog [options] <PATH> [PATH ...]" + description='''Dump information about one or more images in all crash logs. The <PATH> can be a full path, image basename, or partial path. Searches are done in this order.''' + command_args = shlex.split(line) + if not self.image_option_parser: + self.image_option_parser = optparse.OptionParser(description=description, prog='image',usage=usage) + self.image_option_parser.add_option('-a', '--all', action='store_true', help='show all images', default=False) + try: + (options, args) = self.image_option_parser.parse_args(command_args) + except: + return + + if args: + for image_path in args: + fullpath_search = image_path[0] == '/' + for (crash_log_idx, crash_log) in enumerate(self.crash_logs): + matches_found = 0 + for (image_idx, image) in enumerate(crash_log.images): + if fullpath_search: + if image.get_resolved_path() == image_path: + matches_found += 1 + print '[%u] ' % (crash_log_idx), image + else: + image_basename = image.get_resolved_path_basename() + if image_basename == image_path: + matches_found += 1 + print '[%u] ' % (crash_log_idx), image + if matches_found == 0: + for (image_idx, image) in enumerate(crash_log.images): + resolved_image_path = image.get_resolved_path() + if resolved_image_path and string.find(image.get_resolved_path(), image_path) >= 0: + print '[%u] ' % (crash_log_idx), image + else: + for crash_log in self.crash_logs: + for (image_idx, image) in enumerate(crash_log.images): + print '[%u] %s' % (image_idx, image) + return False + + +def interactive_crashlogs(options, args): + crash_log_files = list() + for arg in args: + for resolved_path in glob.glob(arg): + crash_log_files.append(resolved_path) + + crash_logs = list(); + for crash_log_file in crash_log_files: + #print 'crash_log_file = "%s"' % crash_log_file + crash_log = CrashLog(crash_log_file) + if crash_log.error: + print crash_log.error + continue + if options.debug: + crash_log.dump() + if not crash_log.images: + print 'error: no images in crash log "%s"' % (crash_log) + continue + else: + crash_logs.append(crash_log) + + interpreter = Interactive(crash_logs) + # List all crash logs that were imported + interpreter.do_list() + interpreter.cmdloop() + + +def save_crashlog(debugger, command, result, dict): + usage = "usage: %prog [options] <output-path>" + description='''Export the state of current target into a crashlog file''' + parser = optparse.OptionParser(description=description, prog='save_crashlog',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + try: + (options, args) = parser.parse_args(shlex.split(command)) + except: + result.PutCString ("error: invalid options"); + return + if len(args) != 1: + result.PutCString ("error: invalid arguments, a single output file is the only valid argument") + return + out_file = open(args[0], 'w') + if not out_file: + result.PutCString ("error: failed to open file '%s' for writing...", args[0]); + return + target = debugger.GetSelectedTarget() + if target: + identifier = target.executable.basename + if lldb.process: + pid = lldb.process.id + if pid != lldb.LLDB_INVALID_PROCESS_ID: + out_file.write('Process: %s [%u]\n' % (identifier, pid)) + out_file.write('Path: %s\n' % (target.executable.fullpath)) + out_file.write('Identifier: %s\n' % (identifier)) + out_file.write('\nDate/Time: %s\n' % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + out_file.write('OS Version: Mac OS X %s (%s)\n' % (platform.mac_ver()[0], commands.getoutput('sysctl -n kern.osversion'))); + out_file.write('Report Version: 9\n') + for thread_idx in range(lldb.process.num_threads): + thread = lldb.process.thread[thread_idx] + out_file.write('\nThread %u:\n' % (thread_idx)) + for (frame_idx, frame) in enumerate(thread.frames): + frame_pc = frame.pc + frame_offset = 0 + if frame.function: + block = frame.GetFrameBlock() + block_range = block.range[frame.addr] + if block_range: + block_start_addr = block_range[0] + frame_offset = frame_pc - block_start_addr.load_addr + else: + frame_offset = frame_pc - frame.function.addr.load_addr + elif frame.symbol: + frame_offset = frame_pc - frame.symbol.addr.load_addr + out_file.write('%-3u %-32s 0x%16.16x %s' % (frame_idx, frame.module.file.basename, frame_pc, frame.name)) + if frame_offset > 0: + out_file.write(' + %u' % (frame_offset)) + line_entry = frame.line_entry + if line_entry: + if options.verbose: + # This will output the fullpath + line + column + out_file.write(' %s' % (line_entry)) + else: + out_file.write(' %s:%u' % (line_entry.file.basename, line_entry.line)) + column = line_entry.column + if column: + out_file.write(':%u' % (column)) + out_file.write('\n') + + out_file.write('\nBinary Images:\n') + for module in target.modules: + text_segment = module.section['__TEXT'] + if text_segment: + text_segment_load_addr = text_segment.GetLoadAddress(target) + if text_segment_load_addr != lldb.LLDB_INVALID_ADDRESS: + text_segment_end_load_addr = text_segment_load_addr + text_segment.size + identifier = module.file.basename + module_version = '???' + module_version_array = module.GetVersion() + if module_version_array: + module_version = '.'.join(map(str,module_version_array)) + out_file.write (' 0x%16.16x - 0x%16.16x %s (%s - ???) <%s> %s\n' % (text_segment_load_addr, text_segment_end_load_addr, identifier, module_version, module.GetUUIDString(), module.file.fullpath)) + out_file.close() + else: + result.PutCString ("error: invalid target"); + + +def Symbolicate(debugger, command, result, dict): + try: + SymbolicateCrashLogs (shlex.split(command)) + except: + result.PutCString ("error: python exception %s" % sys.exc_info()[0]) + +def SymbolicateCrashLog(crash_log, options): + if crash_log.error: + print crash_log.error + return + if options.debug: + crash_log.dump() + if not crash_log.images: + print 'error: no images in crash log' + return + + if options.dump_image_list: + print "Binary Images:" + for image in crash_log.images: + if options.verbose: + print image.debug_dump() + else: + print image + + target = crash_log.create_target () + if not target: + return + exe_module = target.GetModuleAtIndex(0) + images_to_load = list() + loaded_images = list() + if options.load_all_images: + # --load-all option was specified, load everything up + for image in crash_log.images: + images_to_load.append(image) + else: + # Only load the images found in stack frames for the crashed threads + if options.crashed_only: + for thread in crash_log.threads: + if thread.did_crash(): + for ident in thread.idents: + images = crash_log.find_images_with_identifier (ident) + if images: + for image in images: + images_to_load.append(image) + else: + print 'error: can\'t find image for identifier "%s"' % ident + else: + for ident in crash_log.idents: + images = crash_log.find_images_with_identifier (ident) + if images: + for image in images: + images_to_load.append(image) + else: + print 'error: can\'t find image for identifier "%s"' % ident + + for image in images_to_load: + if not image in loaded_images: + err = image.add_module (target) + if err: + print err + else: + #print 'loaded %s' % image + loaded_images.append(image) + + if crash_log.backtraces: + for thread in crash_log.backtraces: + thread.dump_symbolicated (crash_log, options) + print + + for thread in crash_log.threads: + thread.dump_symbolicated (crash_log, options) + print + + +def CreateSymbolicateCrashLogOptions(command_name, description, add_interactive_options): + usage = "usage: %prog [options] <FILE> [FILE ...]" + option_parser = optparse.OptionParser(description=description, prog='crashlog',usage=usage) + option_parser.add_option('--verbose' , '-v', action='store_true', dest='verbose', help='display verbose debug info', default=False) + option_parser.add_option('--debug' , '-g', action='store_true', dest='debug', help='display verbose debug logging', default=False) + option_parser.add_option('--load-all' , '-a', action='store_true', dest='load_all_images', help='load all executable images, not just the images found in the crashed stack frames', default=False) + option_parser.add_option('--images' , action='store_true', dest='dump_image_list', help='show image list', default=False) + option_parser.add_option('--debug-delay' , type='int', dest='debug_delay', metavar='NSEC', help='pause for NSEC seconds for debugger', default=0) + option_parser.add_option('--crashed-only' , '-c', action='store_true', dest='crashed_only', help='only symbolicate the crashed thread', default=False) + option_parser.add_option('--disasm-depth' , '-d', type='int', dest='disassemble_depth', help='set the depth in stack frames that should be disassembled (default is 1)', default=1) + option_parser.add_option('--disasm-all' , '-D', action='store_true', dest='disassemble_all_threads', help='enabled disassembly of frames on all threads (not just the crashed thread)', default=False) + option_parser.add_option('--disasm-before' , '-B', type='int', dest='disassemble_before', help='the number of instructions to disassemble before the frame PC', default=4) + option_parser.add_option('--disasm-after' , '-A', type='int', dest='disassemble_after', help='the number of instructions to disassemble after the frame PC', default=4) + option_parser.add_option('--source-context', '-C', type='int', metavar='NLINES', dest='source_context', help='show NLINES source lines of source context (default = 4)', default=4) + option_parser.add_option('--source-frames' , type='int', metavar='NFRAMES', dest='source_frames', help='show source for NFRAMES (default = 4)', default=4) + option_parser.add_option('--source-all' , action='store_true', dest='source_all', help='show source for all threads, not just the crashed thread', default=False) + if add_interactive_options: + option_parser.add_option('-i', '--interactive', action='store_true', help='parse all crash logs and enter interactive mode', default=False) + return option_parser + +def SymbolicateCrashLogs(command_args): + description='''Symbolicate one or more darwin crash log files to provide source file and line information, +inlined stack frames back to the concrete functions, and disassemble the location of the crash +for the first frame of the crashed thread. +If this script is imported into the LLDB command interpreter, a "crashlog" command will be added to the interpreter +for use at the LLDB command line. After a crash log has been parsed and symbolicated, a target will have been +created that has all of the shared libraries loaded at the load addresses found in the crash log file. This allows +you to explore the program as if it were stopped at the locations described in the crash log and functions can +be disassembled and lookups can be performed using the addresses found in the crash log.''' + option_parser = CreateSymbolicateCrashLogOptions ('crashlog', description, True) + try: + (options, args) = option_parser.parse_args(command_args) + except: + return + + if options.debug: + print 'command_args = %s' % command_args + print 'options', options + print 'args', args + + if options.debug_delay > 0: + print "Waiting %u seconds for debugger to attach..." % options.debug_delay + time.sleep(options.debug_delay) + error = lldb.SBError() + + if args: + if options.interactive: + interactive_crashlogs(options, args) + else: + for crash_log_file in args: + crash_log = CrashLog(crash_log_file) + SymbolicateCrashLog (crash_log, options) +if __name__ == '__main__': + # Create a new debugger instance + lldb.debugger = lldb.SBDebugger.Create() + SymbolicateCrashLogs (sys.argv[1:]) + lldb.SBDebugger.Destroy (lldb.debugger) +elif getattr(lldb, 'debugger', None): + lldb.debugger.HandleCommand('command script add -f lldb.macosx.crashlog.Symbolicate crashlog') + lldb.debugger.HandleCommand('command script add -f lldb.macosx.crashlog.save_crashlog save_crashlog') + print '"crashlog" and "save_crashlog" command installed, use the "--help" option for detailed help' + diff --git a/examples/python/delta.py b/examples/python/delta.py new file mode 100755 index 0000000000000..e470de536d857 --- /dev/null +++ b/examples/python/delta.py @@ -0,0 +1,115 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# This module will enable GDB remote packet logging when the +# 'start_gdb_log' command is called with a filename to log to. When the +# 'stop_gdb_log' command is called, it will disable the logging and +# print out statistics about how long commands took to execute and also +# will primnt ou +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command. This can be done from the LLDB command line: +# (lldb) command script import /path/to/gdbremote.py +# Or it can be added to your ~/.lldbinit file so this module is always +# available. +#---------------------------------------------------------------------- + +import commands +import optparse +import os +import shlex +import re +import tempfile + +def start_gdb_log(debugger, command, result, dict): + '''Start logging GDB remote packets by enabling logging with timestamps and + thread safe logging. Follow a call to this function with a call to "stop_gdb_log" + in order to dump out the commands.''' + global log_file + if log_file: + result.PutCString ('error: logging is already in progress with file "%s"', log_file) + else: + args_len = len(args) + if args_len == 0: + log_file = tempfile.mktemp() + elif len(args) == 1: + log_file = args[0] + + if log_file: + debugger.HandleCommand('log enable --threadsafe --timestamp --file "%s" gdb-remote packets' % log_file); + result.PutCString ("GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." % log_file) + return + + result.PutCString ('error: invalid log file path') + result.PutCString (usage) + +def parse_time_log(debugger, command, result, dict): + # Any commands whose names might be followed by more valid C identifier + # characters must be listed here + command_args = shlex.split(command) + parse_time_log_args (command_args) + +def parse_time_log_args(command_args): + usage = "usage: parse_time_log [options] [<LOGFILEPATH>]" + description='''Parse a log file that contains timestamps and convert the timestamps to delta times between log lines.''' + parser = optparse.OptionParser(description=description, prog='parse_time_log',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + for log_file in args: + parse_log_file (log_file, options) + +def parse_log_file(file, options): + '''Parse a log file that was contains timestamps. These logs are typically + generated using: + (lldb) log enable --threadsafe --timestamp --file <FILE> .... + + This log file will contain timestamps and this function will then normalize + those packets to be relative to the first value timestamp that is found and + show delta times between log lines and also keep track of how long it takes + for GDB remote commands to make a send/receive round trip. This can be + handy when trying to figure out why some operation in the debugger is taking + a long time during a preset set of debugger commands.''' + + print '#----------------------------------------------------------------------' + print "# Log file: '%s'" % file + print '#----------------------------------------------------------------------' + + timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$') + + base_time = 0.0 + last_time = 0.0 + file = open(file) + lines = file.read().splitlines() + for line in lines: + match = timestamp_regex.match (line) + if match: + curr_time = float (match.group(2)) + delta = 0.0 + if base_time: + delta = curr_time - last_time + else: + base_time = curr_time + + print '%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3)) + last_time = curr_time + else: + print line + + + +if __name__ == '__main__': + import sys + parse_time_log_args (sys.argv[1:]) + +else: + import lldb + if lldb.debugger: + # This initializer is being run from LLDB in the embedded command interpreter + # Add any commands contained in this module to LLDB + lldb.debugger.HandleCommand('command script add -f delta.parse_time_log parse_time_log') + print 'The "parse_time_log" command is now installed and ready for use, type "parse_time_log --help" for more information' diff --git a/examples/python/diagnose_nsstring.py b/examples/python/diagnose_nsstring.py new file mode 100644 index 0000000000000..aca5c7f220fc9 --- /dev/null +++ b/examples/python/diagnose_nsstring.py @@ -0,0 +1,171 @@ +# This implements the "diagnose-nsstring" command, usually installed in the debug session like +# command script import lldb.diagnose +# it is used when NSString summary formatter fails to replicate the logic that went into LLDB making the +# decisions it did and providing some useful context information that can be used for improving the formatter + +import lldb + +def read_memory(process,location,size): + data = "" + error = lldb.SBError() + for x in range(0,size-1): + byte = process.ReadUnsignedFromMemory(x+location,1,error) + if error.fail: + data = data + "err%s" % "" if x == size-2 else ":" + else: + try: + data = data + "0x%x" % byte + if byte == 0: + data = data + "(\\0)" + elif byte == 0xa: + data = data + "(\\a)" + elif byte == 0xb: + data = data + "(\\b)" + elif byte == 0xc: + data = data + "(\\c)" + elif byte == '\n': + data = data + "(\\n)" + else: + data = data + "(%s)" % chr(byte) + if x < size-2: + data = data + ":" + except Exception as e: + print e + return data + +def diagnose_nsstring_Command_Impl(debugger,command,result,internal_dict): + """ + A command to diagnose the LLDB NSString data formatter + invoke as + (lldb) diagnose-nsstring <expr returning NSString> + e.g. + (lldb) diagnose-nsstring @"Hello world" + """ + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + if not target.IsValid() or not process.IsValid(): + return "unable to get target/process - cannot proceed" + options = lldb.SBExpressionOptions() + options.SetFetchDynamicValue() + error = lldb.SBError() + if frame.IsValid(): + nsstring = frame.EvaluateExpression(command,options) + else: + nsstring = target.EvaluateExpression(command,options) + print >>result,str(nsstring) + nsstring_address = nsstring.GetValueAsUnsigned(0) + if nsstring_address == 0: + return "unable to obtain the string - cannot proceed" + expression = "\ +struct $__lldb__notInlineMutable {\ + char* buffer;\ + signed long length;\ + signed long capacity;\ + unsigned int hasGap:1;\ + unsigned int isFixedCapacity:1;\ + unsigned int isExternalMutable:1;\ + unsigned int capacityProvidedExternally:1;\n\ +#if __LP64__\n\ + unsigned long desiredCapacity:60;\n\ +#else\n\ + unsigned long desiredCapacity:28;\n\ +#endif\n\ + void* contentsAllocator;\ +};\ +\ +struct $__lldb__CFString {\ + void* _cfisa;\ + uint8_t _cfinfo[4];\ + uint32_t _rc;\ + union {\ + struct __inline1 {\ + signed long length;\ + } inline1;\ + struct __notInlineImmutable1 {\ + char* buffer;\ + signed long length;\ + void* contentsDeallocator;\ + } notInlineImmutable1;\ + struct __notInlineImmutable2 {\ + char* buffer;\ + void* contentsDeallocator;\ + } notInlineImmutable2;\ + struct $__lldb__notInlineMutable notInlineMutable;\ + } variants;\ +};\ +" + + expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address + # print expression + dumped = target.EvaluateExpression(expression,options) + print >>result, str(dumped) + + little_endian = (target.byte_order == lldb.eByteOrderLittle) + ptr_size = target.addr_size + + info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex(0 if little_endian else 3).GetValueAsUnsigned(0) + is_mutable = (info_bits & 1) == 1 + is_inline = (info_bits & 0x60) == 0 + has_explicit_length = (info_bits & (1 | 4)) != 4 + is_unicode = (info_bits & 0x10) == 0x10 + is_special = (nsstring.GetDynamicValue(lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2") + has_null = (info_bits & 8) == 8 + + print >>result,"\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \ + (info_bits, "yes" if is_mutable else "no","yes" if is_inline else "no","yes" if has_explicit_length else "no","yes" if is_unicode else "no","yes" if is_special else "no","yes" if has_null else "no") + + + explicit_length_offset = 0 + if not has_null and has_explicit_length and not is_special: + explicit_length_offset = 2*ptr_size + if is_mutable and not is_inline: + explicit_length_offset = explicit_length_offset + ptr_size + elif is_inline: + pass + elif not is_inline and not is_mutable: + explicit_length_offset = explicit_length_offset + ptr_size + else: + explicit_length_offset = 0 + + if explicit_length_offset == 0: + print >>result,"There is no explicit length marker - skipping this step\n" + else: + explicit_length_offset = nsstring_address + explicit_length_offset + explicit_length = process.ReadUnsignedFromMemory(explicit_length_offset, 4, error) + print >>result,"Explicit length location is at 0x%x - read value is %d\n" % (explicit_length_offset,explicit_length) + + if is_mutable: + location = 2 * ptr_size + nsstring_address + location = process.ReadPointerFromMemory(location,error) + elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable: + location = 3 * ptr_size + nsstring_address + elif is_unicode: + location = 2 * ptr_size + nsstring_address + if is_inline: + if not has_explicit_length: + print >>result,"Unicode & Inline & !Explicit is a new combo - no formula for it" + else: + location += ptr_size + else: + location = process.ReadPointerFromMemory(location,error) + elif is_special: + location = nsstring_address + ptr_size + 4 + elif is_inline: + location = 2 * ptr_size + nsstring_address + if not has_explicit_length: + location += 1 + else: + location = 2 * ptr_size + nsstring_address + location = process.ReadPointerFromMemory(location,error) + print >>result,"Expected data location: 0x%x\n" % (location) + print >>result,"1K of data around location: %s\n" % read_memory(process,location,1024) + print >>result,"5K of data around string pointer: %s\n" % read_memory(process,nsstring_address,1024*5) + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand("command script add -f %s.diagnose_nsstring_Command_Impl diagnose-nsstring" % __name__) + print 'The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.' + +__lldb_init_module(lldb.debugger,None) +__lldb_init_module = None
\ No newline at end of file diff --git a/examples/python/diagnose_unwind.py b/examples/python/diagnose_unwind.py new file mode 100644 index 0000000000000..e977c4ed1b0ff --- /dev/null +++ b/examples/python/diagnose_unwind.py @@ -0,0 +1,270 @@ +# This implements the "diagnose-unwind" command, usually installed +# in the debug session like +# command script import lldb.diagnose +# it is used when lldb's backtrace fails -- it collects and prints +# information about the stack frames, and tries an alternate unwind +# algorithm, that will help to understand why lldb's unwind algorithm +# did not succeed. + +import optparse +import lldb +import re +import shlex + +# Print the frame number, pc, frame pointer, module UUID and function name +# Returns the SBModule that contains the PC, if it could be found +def backtrace_print_frame (target, frame_num, addr, fp): + process = target.GetProcess() + addr_for_printing = addr + addr_width = process.GetAddressByteSize() * 2 + if frame_num > 0: + addr = addr - 1 + + sbaddr = lldb.SBAddress() + try: + sbaddr.SetLoadAddress(addr, target) + module_description = "" + if sbaddr.GetModule(): + module_filename = "" + module_uuid_str = sbaddr.GetModule().GetUUIDString() + if module_uuid_str == None: + module_uuid_str = "" + if sbaddr.GetModule().GetFileSpec(): + module_filename = sbaddr.GetModule().GetFileSpec().GetFilename() + if module_filename == None: + module_filename = "" + if module_uuid_str != "" or module_filename != "": + module_description = '%s %s' % (module_filename, module_uuid_str) + except Exception: + print '%2d: pc==0x%-*x fp==0x%-*x' % (frame_num, addr_width, addr_for_printing, addr_width, fp) + return + + sym_ctx = target.ResolveSymbolContextForAddress(sbaddr, lldb.eSymbolContextEverything) + if sym_ctx.IsValid() and sym_ctx.GetSymbol().IsValid(): + function_start = sym_ctx.GetSymbol().GetStartAddress().GetLoadAddress(target) + offset = addr - function_start + print '%2d: pc==0x%-*x fp==0x%-*x %s %s + %d' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description, sym_ctx.GetSymbol().GetName(), offset) + else: + print '%2d: pc==0x%-*x fp==0x%-*x %s' % (frame_num, addr_width, addr_for_printing, addr_width, fp, module_description) + return sbaddr.GetModule() + +# A simple stack walk algorithm that follows the frame chain. +# Returns a two-element list; the first element is a list of modules +# seen and the second element is a list of addresses seen during the backtrace. +def simple_backtrace(debugger): + target = debugger.GetSelectedTarget() + process = target.GetProcess() + cur_thread = process.GetSelectedThread() + + initial_fp = cur_thread.GetFrameAtIndex(0).GetFP() + + # If the pseudoreg "fp" isn't recognized, on arm hardcode to r7 which is correct for Darwin programs. + if initial_fp == lldb.LLDB_INVALID_ADDRESS and target.triple[0:3] == "arm": + for reggroup in cur_thread.GetFrameAtIndex(1).registers: + if reggroup.GetName() == "General Purpose Registers": + for reg in reggroup: + if reg.GetName() == "r7": + initial_fp = int (reg.GetValue(), 16) + + module_list = [] + address_list = [cur_thread.GetFrameAtIndex(0).GetPC()] + this_module = backtrace_print_frame (target, 0, cur_thread.GetFrameAtIndex(0).GetPC(), initial_fp) + print_stack_frame (process, initial_fp) + print "" + if this_module != None: + module_list.append (this_module) + if cur_thread.GetNumFrames() < 2: + return [module_list, address_list] + + cur_fp = process.ReadPointerFromMemory (initial_fp, lldb.SBError()) + cur_pc = process.ReadPointerFromMemory (initial_fp + process.GetAddressByteSize(), lldb.SBError()) + + frame_num = 1 + + while cur_pc != 0 and cur_fp != 0 and cur_pc != lldb.LLDB_INVALID_ADDRESS and cur_fp != lldb.LLDB_INVALID_ADDRESS: + address_list.append (cur_pc) + this_module = backtrace_print_frame (target, frame_num, cur_pc, cur_fp) + print_stack_frame (process, cur_fp) + print "" + if this_module != None: + module_list.append (this_module) + frame_num = frame_num + 1 + next_pc = 0 + next_fp = 0 + if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386" or target.triple[0:3] == "arm": + error = lldb.SBError() + next_pc = process.ReadPointerFromMemory(cur_fp + process.GetAddressByteSize(), error) + if not error.Success(): + next_pc = 0 + next_fp = process.ReadPointerFromMemory(cur_fp, error) + if not error.Success(): + next_fp = 0 + # Clear the 0th bit for arm frames - this indicates it is a thumb frame + if target.triple[0:3] == "arm" and (next_pc & 1) == 1: + next_pc = next_pc & ~1 + cur_pc = next_pc + cur_fp = next_fp + this_module = backtrace_print_frame (target, frame_num, cur_pc, cur_fp) + print_stack_frame (process, cur_fp) + print "" + if this_module != None: + module_list.append (this_module) + return [module_list, address_list] + +def print_stack_frame(process, fp): + if fp == 0 or fp == lldb.LLDB_INVALID_ADDRESS or fp == 1: + return + addr_size = process.GetAddressByteSize() + addr = fp - (2 * addr_size) + i = 0 + outline = "Stack frame from $fp-%d: " % (2 * addr_size) + error = lldb.SBError() + try: + while i < 5 and error.Success(): + address = process.ReadPointerFromMemory(addr + (i * addr_size), error) + outline += " 0x%x" % address + i += 1 + print outline + except Exception: + return + +def diagnose_unwind(debugger, command, result, dict): + """ +Gather diagnostic information to help debug incorrect unwind (backtrace) +behavior in lldb. When there is a backtrace that doesn't look +correct, run this command with the correct thread selected and a +large amount of diagnostic information will be printed, it is likely +to be helpful when reporting the problem. + """ + + command_args = shlex.split(command) + parser = create_diagnose_unwind_options() + try: + (options, args) = parser.parse_args(command_args) + except: + return + target = debugger.GetSelectedTarget() + if target: + process = target.GetProcess() + if process: + thread = process.GetSelectedThread() + if thread: + lldb_versions_match = re.search(r'[lL][lL][dD][bB]-(\d+)([.](\d+))?([.](\d+))?', debugger.GetVersionString()) + lldb_version = 0 + lldb_minor = 0 + if len(lldb_versions_match.groups()) >= 1 and lldb_versions_match.groups()[0]: + lldb_major = int(lldb_versions_match.groups()[0]) + if len(lldb_versions_match.groups()) >= 5 and lldb_versions_match.groups()[4]: + lldb_minor = int(lldb_versions_match.groups()[4]) + + modules_seen = [] + addresses_seen = [] + + print 'LLDB version %s' % debugger.GetVersionString() + print 'Unwind diagnostics for thread %d' % thread.GetIndexID() + print "" + print "=============================================================================================" + print "" + print "OS plugin setting:" + debugger.HandleCommand("settings show target.process.python-os-plugin-path") + print "" + print "Live register context:" + thread.SetSelectedFrame(0) + debugger.HandleCommand("register read") + print "" + print "=============================================================================================" + print "" + print "lldb's unwind algorithm:" + print "" + frame_num = 0 + for frame in thread.frames: + if not frame.IsInlined(): + this_module = backtrace_print_frame (target, frame_num, frame.GetPC(), frame.GetFP()) + print_stack_frame (process, frame.GetFP()) + print "" + if this_module != None: + modules_seen.append (this_module) + addresses_seen.append (frame.GetPC()) + frame_num = frame_num + 1 + print "" + print "=============================================================================================" + print "" + print "Simple stack walk algorithm:" + print "" + (module_list, address_list) = simple_backtrace(debugger) + if module_list and module_list != None: + modules_seen += module_list + if address_list and address_list != None: + addresses_seen = set(addresses_seen) + addresses_seen.update(set(address_list)) + + print "" + print "=============================================================================================" + print "" + print "Modules seen in stack walks:" + print "" + modules_already_seen = set() + for module in modules_seen: + if module != None and module.GetFileSpec().GetFilename() != None: + if not module.GetFileSpec().GetFilename() in modules_already_seen: + debugger.HandleCommand('image list %s' % module.GetFileSpec().GetFilename()) + modules_already_seen.add(module.GetFileSpec().GetFilename()) + + print "" + print "=============================================================================================" + print "" + print "Disassembly ofaddresses seen in stack walks:" + print "" + additional_addresses_to_disassemble = addresses_seen + for frame in thread.frames: + if not frame.IsInlined(): + print "--------------------------------------------------------------------------------------" + print "" + print "Disassembly of %s, frame %d, address 0x%x" % (frame.GetFunctionName(), frame.GetFrameID(), frame.GetPC()) + print "" + if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386": + debugger.HandleCommand('disassemble -F att -a 0x%x' % frame.GetPC()) + else: + debugger.HandleCommand('disassemble -a 0x%x' % frame.GetPC()) + if frame.GetPC() in additional_addresses_to_disassemble: + additional_addresses_to_disassemble.remove (frame.GetPC()) + + for address in list(additional_addresses_to_disassemble): + print "--------------------------------------------------------------------------------------" + print "" + print "Disassembly of 0x%x" % address + print "" + if target.triple[0:6] == "x86_64" or target.triple[0:4] == "i386": + debugger.HandleCommand('disassemble -F att -a 0x%x' % address) + else: + debugger.HandleCommand('disassemble -a 0x%x' % address) + + print "" + print "=============================================================================================" + print "" + additional_addresses_to_show_unwind = addresses_seen + for frame in thread.frames: + if not frame.IsInlined(): + print "--------------------------------------------------------------------------------------" + print "" + print "Unwind instructions for %s, frame %d" % (frame.GetFunctionName(), frame.GetFrameID()) + print "" + debugger.HandleCommand('image show-unwind -a "0x%x"' % frame.GetPC()) + if frame.GetPC() in additional_addresses_to_show_unwind: + additional_addresses_to_show_unwind.remove (frame.GetPC()) + + for address in list(additional_addresses_to_show_unwind): + print "--------------------------------------------------------------------------------------" + print "" + print "Unwind instructions for 0x%x" % address + print "" + debugger.HandleCommand('image show-unwind -a "0x%x"' % address) + +def create_diagnose_unwind_options(): + usage = "usage: %prog" + description='''Print diagnostic information about a thread backtrace which will help to debug unwind problems''' + parser = optparse.OptionParser(description=description, prog='diagnose_unwind',usage=usage) + return parser + +lldb.debugger.HandleCommand('command script add -f %s.diagnose_unwind diagnose-unwind' % __name__) +print 'The "diagnose-unwind" command has been installed, type "help diagnose-unwind" for detailed help.' diff --git a/examples/python/dict_utils.py b/examples/python/dict_utils.py new file mode 100755 index 0000000000000..7dc5e7a8b56e3 --- /dev/null +++ b/examples/python/dict_utils.py @@ -0,0 +1,61 @@ + +class LookupDictionary(dict): + """ + a dictionary which can lookup value by key, or keys by value + """ + def __init__(self, items=[]): + """items can be a list of pair_lists or a dictionary""" + dict.__init__(self, items) + + def get_keys_for_value(self, value, fail_value = None): + """find the key(s) as a list given a value""" + list_result = [item[0] for item in self.items() if item[1] == value] + if len(list_result) > 0: + return list_result + return fail_value + + def get_first_key_for_value(self, value, fail_value = None): + """return the first key of this dictionary given the value""" + list_result = [item[0] for item in self.items() if item[1] == value] + if len(list_result) > 0: + return list_result[0] + return fail_value + + def get_value(self, key, fail_value = None): + """find the value given a key""" + if key in self: + return self[key] + return fail_value + + +class Enum(LookupDictionary): + + def __init__(self, initial_value=0, items=[]): + """items can be a list of pair_lists or a dictionary""" + LookupDictionary.__init__(self, items) + self.value = initial_value + + def set_value(self, v): + v_typename = typeof(v).__name__ + if v_typename == 'str': + if str in self: + v = self[v] + else: + v = 0 + else: + self.value = v + + def get_enum_value(self): + return self.value + + def get_enum_name(self): + return self.__str__() + + def __str__(self): + s = self.get_first_key_for_value (self.value, None) + if s == None: + s = "%#8.8x" % self.value + return s + + def __repr__(self): + return self.__str__()
\ No newline at end of file diff --git a/examples/python/disasm-stress-test.py b/examples/python/disasm-stress-test.py new file mode 100755 index 0000000000000..5aa354dc24cb1 --- /dev/null +++ b/examples/python/disasm-stress-test.py @@ -0,0 +1,168 @@ +#!/usr/bin/python + +import argparse, datetime, re, subprocess, sys, time + +parser = argparse.ArgumentParser(description="Run an exhaustive test of the LLDB disassembler for a specific architecture.") + +parser.add_argument('--arch', required=True, action='store', help='The architecture whose disassembler is to be tested') +parser.add_argument('--bytes', required=True, action='store', type=int, help='The byte width of instructions for that architecture') +parser.add_argument('--random', required=False, action='store_true', help='Enables non-sequential testing') +parser.add_argument('--start', required=False, action='store', type=int, help='The first instruction value to test') +parser.add_argument('--skip', required=False, action='store', type=int, help='The interval between instructions to test') +parser.add_argument('--log', required=False, action='store', help='A log file to write the most recent instruction being tested') +parser.add_argument('--time', required=False, action='store_true', help='Every 100,000 instructions, print an ETA to standard out') +parser.add_argument('--lldb', required=False, action='store', help='The path to LLDB.framework, if LLDB should be overridden') + +arguments = sys.argv[1:] + +arg_ns = parser.parse_args(arguments) + +def AddLLDBToSysPathOnMacOSX(): + def GetLLDBFrameworkPath(): + lldb_path = subprocess.check_output(["xcrun", "-find", "lldb"]) + re_result = re.match("(.*)/Developer/usr/bin/lldb", lldb_path) + if re_result == None: + return None + xcode_contents_path = re_result.group(1) + return xcode_contents_path + "/SharedFrameworks/LLDB.framework" + + lldb_framework_path = GetLLDBFrameworkPath() + + if lldb_framework_path == None: + print "Couldn't find LLDB.framework" + sys.exit(-1) + + sys.path.append(lldb_framework_path + "/Resources/Python") + +if arg_ns.lldb == None: + AddLLDBToSysPathOnMacOSX() +else: + sys.path.append(arg_ns.lldb + "/Resources/Python") + +import lldb + +debugger = lldb.SBDebugger.Create() + +if debugger.IsValid() == False: + print "Couldn't create an SBDebugger" + sys.exit(-1) + +target = debugger.CreateTargetWithFileAndArch(None, arg_ns.arch) + +if target.IsValid() == False: + print "Couldn't create an SBTarget for architecture " + arg_ns.arch + sys.exit(-1) + +def ResetLogFile(log_file): + if log_file != sys.stdout: + log_file.seek(0) + +def PrintByteArray(log_file, byte_array): + for byte in byte_array: + print >>log_file, hex(byte) + " ", + print >>log_file + +class SequentialInstructionProvider: + def __init__(self, byte_width, log_file, start=0, skip=1): + self.m_byte_width = byte_width + self.m_log_file = log_file + self.m_start = start + self.m_skip = skip + self.m_value = start + self.m_last = (1 << (byte_width * 8)) - 1 + def PrintCurrentState(self, ret): + ResetLogFile(self.m_log_file) + print >>self.m_log_file, self.m_value + PrintByteArray(self.m_log_file, ret) + def GetNextInstruction(self): + if self.m_value > self.m_last: + return None + ret = bytearray(self.m_byte_width) + for i in range(self.m_byte_width): + ret[self.m_byte_width - (i + 1)] = (self.m_value >> (i * 8)) & 255 + self.PrintCurrentState(ret) + self.m_value += self.m_skip + return ret + def GetNumInstructions(self): + return (self.m_last - self.m_start) / self.m_skip + def __iter__(self): + return self + def next(self): + ret = self.GetNextInstruction() + if ret == None: + raise StopIteration + return ret + +class RandomInstructionProvider: + def __init__(self, byte_width, log_file): + self.m_byte_width = byte_width + self.m_log_file = log_file + self.m_random_file = open("/dev/random", 'r') + def PrintCurrentState(self, ret): + ResetLogFile(self.m_log_file) + PrintByteArray(self.m_log_file, ret) + def GetNextInstruction(self): + ret = bytearray(self.m_byte_width) + for i in range(self.m_byte_width): + ret[i] = self.m_random_file.read(1) + self.PrintCurrentState(ret) + return ret + def __iter__(self): + return self + def next(self): + ret = self.GetNextInstruction() + if ret == None: + raise StopIteration + return ret + +log_file = None + +def GetProviderWithArguments(args): + global log_file + if args.log != None: + log_file = open(args.log, 'w') + else: + log_file = sys.stdout + instruction_provider = None + if args.random == True: + instruction_provider = RandomInstructionProvider(args.bytes, log_file) + else: + start = 0 + skip = 1 + if args.start != None: + start = args.start + if args.skip != None: + skip = args.skip + instruction_provider = SequentialInstructionProvider(args.bytes, log_file, start, skip) + return instruction_provider + +instruction_provider = GetProviderWithArguments(arg_ns) + +fake_address = lldb.SBAddress() + +actually_time = arg_ns.time and not arg_ns.random + +if actually_time: + num_instructions_logged = 0 + total_num_instructions = instruction_provider.GetNumInstructions() + start_time = time.time() + +for inst_bytes in instruction_provider: + if actually_time: + if (num_instructions_logged != 0) and (num_instructions_logged % 100000 == 0): + curr_time = time.time() + elapsed_time = curr_time - start_time + remaining_time = float(total_num_instructions - num_instructions_logged) * (float(elapsed_time) / float(num_instructions_logged)) + print str(datetime.timedelta(seconds=remaining_time)) + num_instructions_logged = num_instructions_logged + 1 + inst_list = target.GetInstructions(fake_address, inst_bytes) + if not inst_list.IsValid(): + print >>log_file, "Invalid instruction list" + continue + inst = inst_list.GetInstructionAtIndex(0) + if not inst.IsValid(): + print >>log_file, "Invalid instruction" + continue + instr_output_stream = lldb.SBStream() + inst.GetDescription(instr_output_stream) + print >>log_file, instr_output_stream.GetData() diff --git a/examples/python/disasm.py b/examples/python/disasm.py new file mode 100755 index 0000000000000..732cf106b11d9 --- /dev/null +++ b/examples/python/disasm.py @@ -0,0 +1,119 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# On MacOSX csh, tcsh: +# setenv PYTHONPATH /Developer/Library/PrivateFrameworks/LLDB.framework/Resources/Python +# On MacOSX sh, bash: +# export PYTHONPATH=/Developer/Library/PrivateFrameworks/LLDB.framework/Resources/Python +#---------------------------------------------------------------------- + +import lldb +import os +import sys + +def disassemble_instructions (insts): + for i in insts: + print i + +def usage(): + print "Usage: disasm.py [-n name] executable-image" + print " By default, it breaks at and disassembles the 'main' function." + sys.exit(0) + +if len(sys.argv) == 2: + fname = 'main' + exe = sys.argv[1] +elif len(sys.argv) == 4: + if sys.argv[1] != '-n': + usage() + else: + fname = sys.argv[2] + exe = sys.argv[3] +else: + usage() + +# Create a new debugger instance +debugger = lldb.SBDebugger.Create() + +# When we step or continue, don't return from the function until the process +# stops. We do this by setting the async mode to false. +debugger.SetAsync (False) + +# Create a target from a file and arch +print "Creating a target for '%s'" % exe + +target = debugger.CreateTargetWithFileAndArch (exe, lldb.LLDB_ARCH_DEFAULT) + +if target: + # If the target is valid set a breakpoint at main + main_bp = target.BreakpointCreateByName (fname, target.GetExecutable().GetFilename()); + + print main_bp + + # Launch the process. Since we specified synchronous mode, we won't return + # from this function until we hit the breakpoint at main + process = target.LaunchSimple (None, None, os.getcwd()) + + # Make sure the launch went ok + if process: + # Print some simple process info + state = process.GetState () + print process + if state == lldb.eStateStopped: + # Get the first thread + thread = process.GetThreadAtIndex (0) + if thread: + # Print some simple thread info + print thread + # Get the first frame + frame = thread.GetFrameAtIndex (0) + if frame: + # Print some simple frame info + print frame + function = frame.GetFunction() + # See if we have debug info (a function) + if function: + # We do have a function, print some info for the function + print function + # Now get all instructions for this function and print them + insts = function.GetInstructions(target) + disassemble_instructions (insts) + else: + # See if we have a symbol in the symbol table for where we stopped + symbol = frame.GetSymbol(); + if symbol: + # We do have a symbol, print some info for the symbol + print symbol + # Now get all instructions for this symbol and print them + insts = symbol.GetInstructions(target) + disassemble_instructions (insts) + + registerList = frame.GetRegisters() + print "Frame registers (size of register set = %d):" % registerList.GetSize() + for value in registerList: + #print value + print "%s (number of children = %d):" % (value.GetName(), value.GetNumChildren()) + for child in value: + print "Name: ", child.GetName(), " Value: ", child.GetValue() + + print "Hit the breakpoint at main, enter to continue and wait for program to exit or 'Ctrl-D'/'quit' to terminate the program" + next = sys.stdin.readline() + if not next or next.rstrip('\n') == 'quit': + print "Terminating the inferior process..." + process.Kill() + else: + # Now continue to the program exit + process.Continue() + # When we return from the above function we will hopefully be at the + # program exit. Print out some process info + print process + elif state == lldb.eStateExited: + print "Didn't hit the breakpoint at main, program has exited..." + else: + print "Unexpected process state: %s, killing process..." % debugger.StateAsCString (state) + process.Kill() + + + +lldb.SBDebugger.Terminate() diff --git a/examples/python/file_extract.py b/examples/python/file_extract.py new file mode 100755 index 0000000000000..3afc0c3c1a0b2 --- /dev/null +++ b/examples/python/file_extract.py @@ -0,0 +1,221 @@ +#! /usr/bin/env python + +import string +import struct +import sys + +class FileExtract: + '''Decode binary data from a file''' + + def __init__(self, f, b = '='): + '''Initialize with an open binary file and optional byte order''' + + self.file = f + self.byte_order = b + self.offsets = list() + + def set_byte_order(self, b): + '''Set the byte order, valid values are "big", "little", "swap", "native", "<", ">", "@", "="''' + if b == 'big': + self.byte_order = '>' + elif b == 'little': + self.byte_order = '<' + elif b == 'swap': + # swap what ever the current byte order is + self.byte_order = swap_unpack_char() + elif b == 'native': + self.byte_order = '=' + elif b == '<' or b == '>' or b == '@' or b == '=': + self.byte_order = b + else: + print "error: invalid byte order specified: '%s'" % b + + def is_in_memory(self): + return False + + def seek(self, offset, whence = 0): + if self.file: + return self.file.seek(offset, whence) + raise ValueError + + def tell(self): + if self.file: + return self.file.tell() + raise ValueError + + def read_size (self, byte_size): + s = self.file.read(byte_size) + if len(s) != byte_size: + return None + return s + + def push_offset_and_seek(self, offset): + '''Push the current file offset and seek to "offset"''' + self.offsets.append(self.file.tell()) + self.file.seek(offset, 0) + + def pop_offset_and_seek(self): + '''Pop a previously pushed file offset, or do nothing if there were no previously pushed offsets''' + if len(self.offsets) > 0: + self.file.seek(self.offsets.pop()) + + def get_sint8(self, fail_value=0): + '''Extract a single int8_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(1) + if s: + v, = struct.unpack(self.byte_order + 'b', s) + return v + else: + return fail_value + + def get_uint8(self, fail_value=0): + '''Extract a single uint8_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(1) + if s: + v, = struct.unpack(self.byte_order + 'B', s) + return v + else: + return fail_value + + def get_sint16(self, fail_value=0): + '''Extract a single int16_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(2) + if s: + v, = struct.unpack(self.byte_order + 'h', s) + return v + else: + return fail_value + + def get_uint16(self, fail_value=0): + '''Extract a single uint16_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(2) + if s: + v, = struct.unpack(self.byte_order + 'H', s) + return v + else: + return fail_value + + def get_sint32(self, fail_value=0): + '''Extract a single int32_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(4) + if s: + v, = struct.unpack(self.byte_order + 'i', s) + return v + else: + return fail_value + + def get_uint32(self, fail_value=0): + '''Extract a single uint32_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(4) + if s: + v, = struct.unpack(self.byte_order + 'I', s) + return v + else: + return fail_value + + def get_sint64(self, fail_value=0): + '''Extract a single int64_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(8) + if s: + v, = struct.unpack(self.byte_order + 'q', s) + return v + else: + return fail_value + + def get_uint64(self, fail_value=0): + '''Extract a single uint64_t from the binary file at the current file position, returns a single integer''' + s = self.read_size(8) + if s: + v, = struct.unpack(self.byte_order + 'Q', s) + return v + else: + return fail_value + + def get_fixed_length_c_string(self, n, fail_value='', isprint_only_with_space_padding=False): + '''Extract a single fixed length C string from the binary file at the current file position, returns a single C string''' + s = self.read_size(n) + if s: + cstr, = struct.unpack(self.byte_order + ("%i" % n) + 's', s) + # Strip trialing NULLs + cstr = string.strip(cstr, "\0") + if isprint_only_with_space_padding: + for c in cstr: + if c in string.printable or ord(c) == 0: + continue + return fail_value + return cstr + else: + return fail_value + + def get_c_string(self): + '''Extract a single NULL terminated C string from the binary file at the current file position, returns a single C string''' + cstr = '' + byte = self.get_uint8() + while byte != 0: + cstr += "%c" % byte + byte = self.get_uint8() + return cstr + + def get_n_sint8(self, n, fail_value=0): + '''Extract "n" int8_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'b', s) + else: + return (fail_value,) * n + + def get_n_uint8(self, n, fail_value=0): + '''Extract "n" uint8_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'B', s) + else: + return (fail_value,) * n + + def get_n_sint16(self, n, fail_value=0): + '''Extract "n" int16_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(2*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'h', s) + else: + return (fail_value,) * n + + def get_n_uint16(self, n, fail_value=0): + '''Extract "n" uint16_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(2*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'H', s) + else: + return (fail_value,) * n + + def get_n_sint32(self, n, fail_value=0): + '''Extract "n" int32_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(4*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'i', s) + else: + return (fail_value,) * n + + def get_n_uint32(self, n, fail_value=0): + '''Extract "n" uint32_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(4*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'I', s) + else: + return (fail_value,) * n + + def get_n_sint64(self, n, fail_value=0): + '''Extract "n" int64_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(8*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'q', s) + else: + return (fail_value,) * n + + def get_n_uint64(self, n, fail_value=0): + '''Extract "n" uint64_t integers from the binary file at the current file position, returns a list of integers''' + s = self.read_size(8*n) + if s: + return struct.unpack(self.byte_order + ("%u" % n) + 'Q', s) + else: + return (fail_value,) * n diff --git a/examples/python/gdb_disassemble.py b/examples/python/gdb_disassemble.py new file mode 100755 index 0000000000000..d9a2f212fc9aa --- /dev/null +++ b/examples/python/gdb_disassemble.py @@ -0,0 +1,24 @@ +import lldb + +def disassemble(debugger, command, result, dict): + if lldb.frame.function: + instructions = lldb.frame.function.instructions + start_addr = lldb.frame.function.addr.load_addr + name = lldb.frame.function.name + elif lldb.frame.symbol: + instructions = lldb.frame.symbol.instructions + start_addr = lldb.frame.symbol.addr.load_addr + name = lldb.frame.symbol.name + + for inst in instructions: + inst_addr = inst.addr.load_addr + inst_offset = inst_addr - start_addr + comment = inst.comment + if comment: + print "<%s + %-4u> 0x%x %8s %s ; %s" % (name, inst_offset, inst_addr, inst.mnemonic, inst.operands, comment) + else: + print "<%s + %-4u> 0x%x %8s %s" % (name, inst_offset, inst_addr, inst.mnemonic, inst.operands) + +# Install the command when the module gets imported +lldb.debugger.HandleCommand('command script add -f gdb_disassemble.disassemble gdb-disassemble') +print 'Installed "gdb-disassemble" command for disassembly'
\ No newline at end of file diff --git a/examples/python/gdbremote.py b/examples/python/gdbremote.py new file mode 100755 index 0000000000000..4cbfdb2ba333a --- /dev/null +++ b/examples/python/gdbremote.py @@ -0,0 +1,1362 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# This module will enable GDB remote packet logging when the +# 'start_gdb_log' command is called with a filename to log to. When the +# 'stop_gdb_log' command is called, it will disable the logging and +# print out statistics about how long commands took to execute and also +# will primnt ou +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command. This can be done from the LLDB command line: +# (lldb) command script import /path/to/gdbremote.py +# Or it can be added to your ~/.lldbinit file so this module is always +# available. +#---------------------------------------------------------------------- + +import binascii +import commands +import json +import math +import optparse +import os +import re +import shlex +import string +import sys +import tempfile +import xml.etree.ElementTree as ET + +#---------------------------------------------------------------------- +# Global variables +#---------------------------------------------------------------------- +g_log_file = '' +g_byte_order = 'little' +g_number_regex = re.compile('^(0x[0-9a-fA-F]+|[0-9]+)') +g_thread_id_regex = re.compile('^(-1|[0-9a-fA-F]+|0)') + +class TerminalColors: + '''Simple terminal colors class''' + def __init__(self, enabled = True): + # TODO: discover terminal type from "file" and disable if + # it can't handle the color codes + self.enabled = enabled + + def reset(self): + '''Reset all terminal colors and formatting.''' + if self.enabled: + return "\x1b[0m"; + return '' + + def bold(self, on = True): + '''Enable or disable bold depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[1m"; + else: + return "\x1b[22m"; + return '' + + def italics(self, on = True): + '''Enable or disable italics depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[3m"; + else: + return "\x1b[23m"; + return '' + + def underline(self, on = True): + '''Enable or disable underline depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[4m"; + else: + return "\x1b[24m"; + return '' + + def inverse(self, on = True): + '''Enable or disable inverse depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[7m"; + else: + return "\x1b[27m"; + return '' + + def strike(self, on = True): + '''Enable or disable strike through depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[9m"; + else: + return "\x1b[29m"; + return '' + + def black(self, fg = True): + '''Set the foreground or background color to black. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[30m"; + else: + return "\x1b[40m"; + return '' + + def red(self, fg = True): + '''Set the foreground or background color to red. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[31m"; + else: + return "\x1b[41m"; + return '' + + def green(self, fg = True): + '''Set the foreground or background color to green. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[32m"; + else: + return "\x1b[42m"; + return '' + + def yellow(self, fg = True): + '''Set the foreground or background color to yellow. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[33m"; + else: + return "\x1b[43m"; + return '' + + def blue(self, fg = True): + '''Set the foreground or background color to blue. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[34m"; + else: + return "\x1b[44m"; + return '' + + def magenta(self, fg = True): + '''Set the foreground or background color to magenta. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[35m"; + else: + return "\x1b[45m"; + return '' + + def cyan(self, fg = True): + '''Set the foreground or background color to cyan. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[36m"; + else: + return "\x1b[46m"; + return '' + + def white(self, fg = True): + '''Set the foreground or background color to white. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[37m"; + else: + return "\x1b[47m"; + return '' + + def default(self, fg = True): + '''Set the foreground or background color to the default. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[39m"; + else: + return "\x1b[49m"; + return '' + + +def start_gdb_log(debugger, command, result, dict): + '''Start logging GDB remote packets by enabling logging with timestamps and + thread safe logging. Follow a call to this function with a call to "stop_gdb_log" + in order to dump out the commands.''' + global g_log_file + command_args = shlex.split(command) + usage = "usage: start_gdb_log [options] [<LOGFILEPATH>]" + description='''The command enables GDB remote packet logging with timestamps. The packets will be logged to <LOGFILEPATH> if supplied, or a temporary file will be used. Logging stops when stop_gdb_log is called and the packet times will + be aggregated and displayed.''' + parser = optparse.OptionParser(description=description, prog='start_gdb_log',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + if g_log_file: + result.PutCString ('error: logging is already in progress with file "%s"' % g_log_file) + else: + args_len = len(args) + if args_len == 0: + g_log_file = tempfile.mktemp() + elif len(args) == 1: + g_log_file = args[0] + + if g_log_file: + debugger.HandleCommand('log enable --threadsafe --timestamp --file "%s" gdb-remote packets' % g_log_file); + result.PutCString ("GDB packet logging enable with log file '%s'\nUse the 'stop_gdb_log' command to stop logging and show packet statistics." % g_log_file) + return + + result.PutCString ('error: invalid log file path') + result.PutCString (usage) + +def stop_gdb_log(debugger, command, result, dict): + '''Stop logging GDB remote packets to the file that was specified in a call + to "start_gdb_log" and normalize the timestamps to be relative to the first + timestamp in the log file. Also print out statistics for how long each + command took to allow performance bottlenecks to be determined.''' + global g_log_file + # Any commands whose names might be followed by more valid C identifier + # characters must be listed here + command_args = shlex.split(command) + usage = "usage: stop_gdb_log [options]" + description='''The command stops a previously enabled GDB remote packet logging command. Packet logging must have been previously enabled with a call to start_gdb_log.''' + parser = optparse.OptionParser(description=description, prog='stop_gdb_log',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='display verbose debug info', default=False) + parser.add_option('-C', '--color', action='store_true', dest='color', help='add terminal colors', default=False) + parser.add_option('-c', '--sort-by-count', action='store_true', dest='sort_count', help='display verbose debug info', default=False) + parser.add_option('-s', '--symbolicate', action='store_true', dest='symbolicate', help='symbolicate addresses in log using current "lldb.target"', default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + options.colors = TerminalColors(options.color) + options.symbolicator = None + if options.symbolicate: + if lldb.target: + import lldb.utils.symbolication + options.symbolicator = lldb.utils.symbolication.Symbolicator() + options.symbolicator.target = lldb.target + else: + print "error: can't symbolicate without a target" + + if not g_log_file: + result.PutCString ('error: logging must have been previously enabled with a call to "stop_gdb_log"') + elif os.path.exists (g_log_file): + if len(args) == 0: + debugger.HandleCommand('log disable gdb-remote packets'); + result.PutCString ("GDB packet logging disabled. Logged packets are in '%s'" % g_log_file) + parse_gdb_log_file (g_log_file, options) + else: + result.PutCString (usage) + else: + print 'error: the GDB packet log file "%s" does not exist' % g_log_file + +def is_hex_byte(str): + if len(str) == 2: + return str[0] in string.hexdigits and str[1] in string.hexdigits; + return False + +# global register info list +g_register_infos = list() +g_max_register_info_name_len = 0 + +class RegisterInfo: + """Class that represents register information""" + def __init__(self, kvp): + self.info = dict() + for kv in kvp: + key = kv[0] + value = kv[1] + self.info[key] = value + def name(self): + '''Get the name of the register.''' + if self.info and 'name' in self.info: + return self.info['name'] + return None + + def bit_size(self): + '''Get the size in bits of the register.''' + if self.info and 'bitsize' in self.info: + return int(self.info['bitsize']) + return 0 + + def byte_size(self): + '''Get the size in bytes of the register.''' + return self.bit_size() / 8 + + def get_value_from_hex_string(self, hex_str): + '''Dump the register value given a native byte order encoded hex ASCII byte string.''' + encoding = self.info['encoding'] + bit_size = self.bit_size() + packet = Packet(hex_str) + if encoding == 'uint': + uval = packet.get_hex_uint(g_byte_order) + if bit_size == 8: + return '0x%2.2x' % (uval) + elif bit_size == 16: + return '0x%4.4x' % (uval) + elif bit_size == 32: + return '0x%8.8x' % (uval) + elif bit_size == 64: + return '0x%16.16x' % (uval) + bytes = list(); + uval = packet.get_hex_uint8() + while uval != None: + bytes.append(uval) + uval = packet.get_hex_uint8() + value_str = '0x' + if g_byte_order == 'little': + bytes.reverse() + for byte in bytes: + value_str += '%2.2x' % byte + return '%s' % (value_str) + + def __str__(self): + '''Dump the register info key/value pairs''' + s = '' + for key in self.info.keys(): + if s: + s += ', ' + s += "%s=%s " % (key, self.info[key]) + return s + +class Packet: + """Class that represents a packet that contains string data""" + def __init__(self, packet_str): + self.str = packet_str + + def peek_char(self): + ch = 0 + if self.str: + ch = self.str[0] + return ch + + def get_char(self): + ch = 0 + if self.str: + ch = self.str[0] + self.str = self.str[1:] + return ch + + def skip_exact_string(self, s): + if self.str and self.str.startswith(s): + self.str = self.str[len(s):] + return True + else: + return False + + def get_thread_id(self, fail_value = -1): + match = g_number_regex.match (self.str) + if match: + number_str = match.group(1) + self.str = self.str[len(number_str):] + return int(number_str, 0) + else: + return fail_value + + def get_hex_uint8(self): + if self.str and len(self.str) >= 2 and self.str[0] in string.hexdigits and self.str[1] in string.hexdigits: + uval = int(self.str[0:2], 16) + self.str = self.str[2:] + return uval + return None + + def get_hex_uint16(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + return uval + + def get_hex_uint32(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 24 + return uval + + def get_hex_uint64(self, byte_order): + uval = 0 + if byte_order == 'big': + uval |= self.get_hex_uint8() << 56 + uval |= self.get_hex_uint8() << 48 + uval |= self.get_hex_uint8() << 40 + uval |= self.get_hex_uint8() << 32 + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() + else: + uval |= self.get_hex_uint8() + uval |= self.get_hex_uint8() << 8 + uval |= self.get_hex_uint8() << 16 + uval |= self.get_hex_uint8() << 24 + uval |= self.get_hex_uint8() << 32 + uval |= self.get_hex_uint8() << 40 + uval |= self.get_hex_uint8() << 48 + uval |= self.get_hex_uint8() << 56 + return uval + + def get_number(self, fail_value=-1): + '''Get a number from the packet. The number must be in big endian format and should be parsed + according to its prefix (starts with "0x" means hex, starts with "0" means octal, starts with + [1-9] means decimal, etc)''' + match = g_number_regex.match (self.str) + if match: + number_str = match.group(1) + self.str = self.str[len(number_str):] + return int(number_str, 0) + else: + return fail_value + + + def get_hex_ascii_str(self, n=0): + hex_chars = self.get_hex_chars(n) + if hex_chars: + return binascii.unhexlify(hex_chars) + else: + return None + + def get_hex_chars(self, n = 0): + str_len = len(self.str) + if n == 0: + # n was zero, so we need to determine all hex chars and + # stop when we hit the end of the string of a non-hex character + while n < str_len and self.str[n] in string.hexdigits: + n = n + 1 + else: + if n > str_len: + return None # Not enough chars + # Verify all chars are hex if a length was specified + for i in range(n): + if self.str[i] not in string.hexdigits: + return None # Not all hex digits + if n == 0: + return None + hex_str = self.str[0:n] + self.str = self.str[n:] + return hex_str + + def get_hex_uint(self, byte_order, n = 0): + if byte_order == 'big': + hex_str = self.get_hex_chars(n) + if hex_str == None: + return None + return int(hex_str, 16) + else: + uval = self.get_hex_uint8() + if uval == None: + return None + uval_result = 0 + shift = 0 + while uval != None: + uval_result |= (uval << shift) + shift += 8 + uval = self.get_hex_uint8() + return uval_result + + def get_key_value_pairs(self): + kvp = list() + if ';' in self.str: + key_value_pairs = string.split(self.str, ';') + for key_value_pair in key_value_pairs: + if len(key_value_pair): + kvp.append(string.split(key_value_pair, ':')) + return kvp + + def split(self, ch): + return string.split(self.str, ch) + + def split_hex(self, ch, byte_order): + hex_values = list() + strings = string.split(self.str, ch) + for str in strings: + hex_values.append(Packet(str).get_hex_uint(byte_order)) + return hex_values + + def __str__(self): + return self.str + + def __len__(self): + return len(self.str) + +g_thread_suffix_regex = re.compile(';thread:([0-9a-fA-F]+);') +def get_thread_from_thread_suffix(str): + if str: + match = g_thread_suffix_regex.match (str) + if match: + return int(match.group(1), 16) + return None + +def cmd_qThreadStopInfo(options, cmd, args): + packet = Packet(args) + tid = packet.get_hex_uint('big') + print "get_thread_stop_info (tid = 0x%x)" % (tid) + +def cmd_stop_reply(options, cmd, args): + print "get_last_stop_info()" + return False + +def rsp_stop_reply(options, cmd, cmd_args, rsp): + global g_byte_order + packet = Packet(rsp) + stop_type = packet.get_char() + if stop_type == 'T' or stop_type == 'S': + signo = packet.get_hex_uint8() + key_value_pairs = packet.get_key_value_pairs() + for key_value_pair in key_value_pairs: + key = key_value_pair[0] + if is_hex_byte(key): + reg_num = Packet(key).get_hex_uint8() + if reg_num < len(g_register_infos): + reg_info = g_register_infos[reg_num] + key_value_pair[0] = reg_info.name() + key_value_pair[1] = reg_info.get_value_from_hex_string (key_value_pair[1]) + elif key == 'jthreads' or key == 'jstopinfo': + key_value_pair[1] = binascii.unhexlify(key_value_pair[1]) + key_value_pairs.insert(0, ['signal', signo]) + print 'stop_reply():' + dump_key_value_pairs (key_value_pairs) + elif stop_type == 'W': + exit_status = packet.get_hex_uint8() + print 'stop_reply(): exit (status=%i)' % exit_status + elif stop_type == 'O': + print 'stop_reply(): stdout = "%s"' % packet.str + + +def cmd_unknown_packet(options, cmd, args): + if args: + print "cmd: %s, args: %s", cmd, args + else: + print "cmd: %s", cmd + return False + +def cmd_qSymbol(options, cmd, args): + if args == ':': + print 'ready to serve symbols' + else: + packet = Packet(args) + symbol_addr = packet.get_hex_uint('big') + if symbol_addr is None: + if packet.skip_exact_string(':'): + symbol_name = packet.get_hex_ascii_str() + print 'lookup_symbol("%s") -> symbol not available yet' % (symbol_name) + else: + print 'error: bad command format' + else: + if packet.skip_exact_string(':'): + symbol_name = packet.get_hex_ascii_str() + print 'lookup_symbol("%s") -> 0x%x' % (symbol_name, symbol_addr) + else: + print 'error: bad command format' + +def rsp_qSymbol(options, cmd, cmd_args, rsp): + if len(rsp) == 0: + print "Unsupported" + else: + if rsp == "OK": + print "No more symbols to lookup" + else: + packet = Packet(rsp) + if packet.skip_exact_string("qSymbol:"): + symbol_name = packet.get_hex_ascii_str() + print 'lookup_symbol("%s")' % (symbol_name) + else: + print 'error: response string should start with "qSymbol:": respnse is "%s"' % (rsp) + +def cmd_qXfer(options, cmd, args): + # $qXfer:features:read:target.xml:0,1ffff#14 + print "read target special data %s" % (args) + return True + +def rsp_qXfer(options, cmd, cmd_args, rsp): + data = string.split(cmd_args, ':') + if data[0] == 'features': + if data[1] == 'read': + filename, extension = os.path.splitext(data[2]) + if extension == '.xml': + response = Packet(rsp) + xml_string = response.get_hex_ascii_str() + ch = xml_string[0] + if ch == 'l': + xml_string = xml_string[1:] + xml_root = ET.fromstring(xml_string) + for reg_element in xml_root.findall("./feature/reg"): + if not 'value_regnums' in reg_element.attrib: + reg_info = RegisterInfo([]) + if 'name' in reg_element.attrib: + reg_info.info['name'] = reg_element.attrib['name'] + else: + reg_info.info['name'] = 'unspecified' + if 'encoding' in reg_element.attrib: + reg_info.info['encoding'] = reg_element.attrib['encoding'] + else: + reg_info.info['encoding'] = 'uint' + if 'offset' in reg_element.attrib: + reg_info.info['offset'] = reg_element.attrib['offset'] + if 'bitsize' in reg_element.attrib: + reg_info.info['bitsize'] = reg_element.attrib['bitsize'] + g_register_infos.append(reg_info) + print 'XML for "%s":' % (data[2]) + ET.dump(xml_root) + +def cmd_A(options, cmd, args): + print 'launch process:' + packet = Packet(args) + while 1: + arg_len = packet.get_number() + if arg_len == -1: + break + if not packet.skip_exact_string(','): + break + arg_idx = packet.get_number() + if arg_idx == -1: + break + if not packet.skip_exact_string(','): + break; + arg_value = packet.get_hex_ascii_str(arg_len) + print 'argv[%u] = "%s"' % (arg_idx, arg_value) + +def cmd_qC(options, cmd, args): + print "query_current_thread_id()" + +def rsp_qC(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + if packet.skip_exact_string("QC"): + tid = packet.get_thread_id() + print "current_thread_id = %#x" % (tid) + else: + print "current_thread_id = old thread ID" + +def cmd_query_packet(options, cmd, args): + if args: + print "%s%s" % (cmd, args) + else: + print "%s" % (cmd) + return False + +def rsp_ok_error(rsp): + print "rsp: ", rsp + +def rsp_ok_means_supported(options, cmd, cmd_args, rsp): + if rsp == 'OK': + print "%s%s is supported" % (cmd, cmd_args) + elif rsp == '': + print "%s%s is not supported" % (cmd, cmd_args) + else: + print "%s%s -> %s" % (cmd, cmd_args, rsp) + +def rsp_ok_means_success(options, cmd, cmd_args, rsp): + if rsp == 'OK': + print "success" + elif rsp == '': + print "%s%s is not supported" % (cmd, cmd_args) + else: + print "%s%s -> %s" % (cmd, cmd_args, rsp) + +def dump_key_value_pairs(key_value_pairs): + max_key_len = 0 + for key_value_pair in key_value_pairs: + key_len = len(key_value_pair[0]) + if max_key_len < key_len: + max_key_len = key_len + for key_value_pair in key_value_pairs: + key = key_value_pair[0] + value = key_value_pair[1] + print "%*s = %s" % (max_key_len, key, value) + +def rsp_dump_key_value_pairs(options, cmd, cmd_args, rsp): + if rsp: + print '%s response:' % (cmd) + packet = Packet(rsp) + key_value_pairs = packet.get_key_value_pairs() + dump_key_value_pairs(key_value_pairs) + else: + print "not supported" + +def cmd_c(options, cmd, args): + print "continue()" + return False + +def cmd_s(options, cmd, args): + print "step()" + return False + +def cmd_vCont(options, cmd, args): + if args == '?': + print "%s: get supported extended continue modes" % (cmd) + else: + got_other_threads = 0 + s = '' + for thread_action in string.split(args[1:], ';'): + (short_action, thread) = string.split(thread_action, ':') + tid = int(thread, 16) + if short_action == 'c': + action = 'continue' + elif short_action == 's': + action = 'step' + elif short_action[0] == 'C': + action = 'continue with signal 0x%s' % (short_action[1:]) + elif short_action == 'S': + action = 'step with signal 0x%s' % (short_action[1:]) + else: + action = short_action + if s: + s += ', ' + if tid == -1: + got_other_threads = 1 + s += 'other-threads:' + else: + s += 'thread 0x%4.4x: %s' % (tid, action) + if got_other_threads: + print "extended_continue (%s)" % (s) + else: + print "extended_continue (%s, other-threads: suspend)" % (s) + return False + +def rsp_vCont(options, cmd, cmd_args, rsp): + if cmd_args == '?': + # Skip the leading 'vCont;' + rsp = rsp[6:] + modes = string.split(rsp, ';') + s = "%s: supported extended continue modes include: " % (cmd) + + for i, mode in enumerate(modes): + if i: + s += ', ' + if mode == 'c': + s += 'continue' + elif mode == 'C': + s += 'continue with signal' + elif mode == 's': + s += 'step' + elif mode == 'S': + s += 'step with signal' + else: + s += 'unrecognized vCont mode: ', mode + print s + elif rsp: + if rsp[0] == 'T' or rsp[0] == 'S' or rsp[0] == 'W' or rsp[0] == 'X': + rsp_stop_reply (options, cmd, cmd_args, rsp) + return + if rsp[0] == 'O': + print "stdout: %s" % (rsp) + return + else: + print "not supported (cmd = '%s', args = '%s', rsp = '%s')" % (cmd, cmd_args, rsp) + +def cmd_vAttach(options, cmd, args): + (extra_command, args) = string.split(args, ';') + if extra_command: + print "%s%s(%s)" % (cmd, extra_command, args) + else: + print "attach(pid = %u)" % int(args, 16) + return False + + +def cmd_qRegisterInfo(options, cmd, args): + print 'query_register_info(reg_num=%i)' % (int(args, 16)) + return False + +def rsp_qRegisterInfo(options, cmd, cmd_args, rsp): + global g_max_register_info_name_len + print 'query_register_info(reg_num=%i):' % (int(cmd_args, 16)), + if len(rsp) == 3 and rsp[0] == 'E': + g_max_register_info_name_len = 0 + for reg_info in g_register_infos: + name_len = len(reg_info.name()) + if g_max_register_info_name_len < name_len: + g_max_register_info_name_len = name_len + print' DONE' + else: + packet = Packet(rsp) + reg_info = RegisterInfo(packet.get_key_value_pairs()) + g_register_infos.append(reg_info) + print reg_info + return False + +def cmd_qThreadInfo(options, cmd, args): + if cmd == 'qfThreadInfo': + query_type = 'first' + else: + query_type = 'subsequent' + print 'get_current_thread_list(type=%s)' % (query_type) + return False + +def rsp_qThreadInfo(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + response_type = packet.get_char() + if response_type == 'm': + tids = packet.split_hex(';', 'big') + for i, tid in enumerate(tids): + if i: + print ',', + print '0x%x' % (tid), + print + elif response_type == 'l': + print 'END' + +def rsp_hex_big_endian(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + uval = packet.get_hex_uint('big') + print '%s: 0x%x' % (cmd, uval) + +def cmd_read_mem_bin(options, cmd, args): + # x0x7fff5fc39200,0x200 + packet = Packet(args) + addr = packet.get_number() + comma = packet.get_char() + size = packet.get_number() + print 'binary_read_memory (addr = 0x%16.16x, size = %u)' % (addr, size) + return False + +def rsp_mem_bin_bytes(options, cmd, cmd_args, rsp): + packet = Packet(cmd_args) + addr = packet.get_number() + comma = packet.get_char() + size = packet.get_number() + print 'memory:' + if size > 0: + dump_hex_memory_buffer (addr, rsp) + +def cmd_read_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + comma = packet.get_char() + size = packet.get_hex_uint('big') + print 'read_memory (addr = 0x%16.16x, size = %u)' % (addr, size) + return False + +def dump_hex_memory_buffer(addr, hex_byte_str): + packet = Packet(hex_byte_str) + idx = 0 + ascii = '' + uval = packet.get_hex_uint8() + while uval != None: + if ((idx % 16) == 0): + if ascii: + print ' ', ascii + ascii = '' + print '0x%x:' % (addr + idx), + print '%2.2x' % (uval), + if 0x20 <= uval and uval < 0x7f: + ascii += '%c' % uval + else: + ascii += '.' + uval = packet.get_hex_uint8() + idx = idx + 1 + if ascii: + print ' ', ascii + ascii = '' + +def cmd_write_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + if packet.get_char() != ',': + print 'error: invalid write memory command (missing comma after address)' + return + size = packet.get_hex_uint('big') + if packet.get_char() != ':': + print 'error: invalid write memory command (missing colon after size)' + return + print 'write_memory (addr = 0x%16.16x, size = %u, data:' % (addr, size) + dump_hex_memory_buffer (addr, packet.str) + return False + +def cmd_alloc_memory(options, cmd, args): + packet = Packet(args) + byte_size = packet.get_hex_uint('big') + if packet.get_char() != ',': + print 'error: invalid allocate memory command (missing comma after address)' + return + print 'allocate_memory (byte-size = %u (0x%x), permissions = %s)' % (byte_size, byte_size, packet.str) + return False + +def rsp_alloc_memory(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + addr = packet.get_hex_uint('big') + print 'addr = 0x%x' % addr + +def cmd_dealloc_memory(options, cmd, args): + packet = Packet(args) + addr = packet.get_hex_uint('big') + if packet.get_char() != ',': + print 'error: invalid allocate memory command (missing comma after address)' + else: + print 'deallocate_memory (addr = 0x%x, permissions = %s)' % (addr, packet.str) + return False +def rsp_memory_bytes(options, cmd, cmd_args, rsp): + addr = Packet(cmd_args).get_hex_uint('big') + dump_hex_memory_buffer (addr, rsp) + +def get_register_name_equal_value(options, reg_num, hex_value_str): + if reg_num < len(g_register_infos): + reg_info = g_register_infos[reg_num] + value_str = reg_info.get_value_from_hex_string (hex_value_str) + s = reg_info.name() + ' = ' + if options.symbolicator: + symbolicated_addresses = options.symbolicator.symbolicate (int(value_str, 0)) + if symbolicated_addresses: + s += options.colors.magenta() + s += '%s' % symbolicated_addresses[0] + s += options.colors.reset() + return s + s += value_str + return s + else: + reg_value = Packet(hex_value_str).get_hex_uint(g_byte_order) + return 'reg(%u) = 0x%x' % (reg_num, reg_value) + +def cmd_read_one_reg(options, cmd, args): + packet = Packet(args) + reg_num = packet.get_hex_uint('big') + tid = get_thread_from_thread_suffix (packet.str) + name = None + if reg_num < len(g_register_infos): + name = g_register_infos[reg_num].name () + if packet.str: + packet.get_char() # skip ; + thread_info = packet.get_key_value_pairs() + tid = int(thread_info[0][1], 16) + s = 'read_register (reg_num=%u' % reg_num + if name: + s += ' (%s)' % (name) + if tid != None: + s += ', tid = 0x%4.4x' % (tid) + s += ')' + print s + return False + +def rsp_read_one_reg(options, cmd, cmd_args, rsp): + packet = Packet(cmd_args) + reg_num = packet.get_hex_uint('big') + print get_register_name_equal_value (options, reg_num, rsp) + +def cmd_write_one_reg(options, cmd, args): + packet = Packet(args) + reg_num = packet.get_hex_uint('big') + if packet.get_char() != '=': + print 'error: invalid register write packet' + else: + name = None + hex_value_str = packet.get_hex_chars() + tid = get_thread_from_thread_suffix (packet.str) + s = 'write_register (reg_num=%u' % reg_num + if name: + s += ' (%s)' % (name) + s += ', value = ' + s += get_register_name_equal_value(options, reg_num, hex_value_str) + if tid != None: + s += ', tid = 0x%4.4x' % (tid) + s += ')' + print s + return False + +def dump_all_regs(packet): + for reg_info in g_register_infos: + nibble_size = reg_info.bit_size() / 4 + hex_value_str = packet.get_hex_chars(nibble_size) + if hex_value_str != None: + value = reg_info.get_value_from_hex_string (hex_value_str) + print '%*s = %s' % (g_max_register_info_name_len, reg_info.name(), value) + else: + return + +def cmd_read_all_regs(cmd, cmd_args): + packet = Packet(cmd_args) + packet.get_char() # toss the 'g' command character + tid = get_thread_from_thread_suffix (packet.str) + if tid != None: + print 'read_all_register(thread = 0x%4.4x)' % tid + else: + print 'read_all_register()' + return False + +def rsp_read_all_regs(options, cmd, cmd_args, rsp): + packet = Packet(rsp) + dump_all_regs (packet) + +def cmd_write_all_regs(options, cmd, args): + packet = Packet(args) + print 'write_all_registers()' + dump_all_regs (packet) + return False + +g_bp_types = [ "software_bp", "hardware_bp", "write_wp", "read_wp", "access_wp" ] + +def cmd_bp(options, cmd, args): + if cmd == 'Z': + s = 'set_' + else: + s = 'clear_' + packet = Packet (args) + bp_type = packet.get_hex_uint('big') + packet.get_char() # Skip , + bp_addr = packet.get_hex_uint('big') + packet.get_char() # Skip , + bp_size = packet.get_hex_uint('big') + s += g_bp_types[bp_type] + s += " (addr = 0x%x, size = %u)" % (bp_addr, bp_size) + print s + return False + +def cmd_mem_rgn_info(options, cmd, args): + packet = Packet(args) + packet.get_char() # skip ':' character + addr = packet.get_hex_uint('big') + print 'get_memory_region_info (addr=0x%x)' % (addr) + return False + +def cmd_kill(options, cmd, args): + print 'kill_process()' + return False + +def cmd_jThreadsInfo(options, cmd, args): + print 'jThreadsInfo()' + return False + +def cmd_jGetLoadedDynamicLibrariesInfos(options, cmd, args): + print 'jGetLoadedDynamicLibrariesInfos()' + return False + +def decode_packet(s, start_index = 0): + #print '\ndecode_packet("%s")' % (s[start_index:]) + index = s.find('}', start_index) + have_escapes = index != -1 + if have_escapes: + normal_s = s[start_index:index] + else: + normal_s = s[start_index:] + #print 'normal_s = "%s"' % (normal_s) + if have_escapes: + escape_char = '%c' % (ord(s[index+1]) ^ 0x20) + #print 'escape_char for "%s" = %c' % (s[index:index+2], escape_char) + return normal_s + escape_char + decode_packet(s, index+2) + else: + return normal_s + +def rsp_json(options, cmd, cmd_args, rsp): + print '%s() reply:' % (cmd) + json_tree = json.loads(rsp) + print json.dumps(json_tree, indent=4, separators=(',', ': ')) + + +def rsp_jGetLoadedDynamicLibrariesInfos(options, cmd, cmd_args, rsp): + if cmd_args: + rsp_json(options, cmd, cmd_args, rsp) + else: + rsp_ok_means_supported(options, cmd, cmd_args, rsp) + +gdb_remote_commands = { + '\\?' : { 'cmd' : cmd_stop_reply , 'rsp' : rsp_stop_reply , 'name' : "stop reply pacpket"}, + 'qThreadStopInfo' : { 'cmd' : cmd_qThreadStopInfo , 'rsp' : rsp_stop_reply , 'name' : "stop reply pacpket"}, + 'QStartNoAckMode' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "query if no ack mode is supported"}, + 'QThreadSuffixSupported' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "query if thread suffix is supported" }, + 'QListThreadsInStopReply' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "query if threads in stop reply packets are supported" }, + 'QSetDetachOnError' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_success , 'name' : "set if we should detach on error" }, + 'QSetDisableASLR' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_success , 'name' : "set if we should disable ASLR" }, + 'qLaunchSuccess' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_success , 'name' : "check on launch success for the A packet" }, + 'A' : { 'cmd' : cmd_A , 'rsp' : rsp_ok_means_success , 'name' : "launch process" }, + 'QLaunchArch' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "set if we should disable ASLR" }, + 'qVAttachOrWaitSupported' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "set the launch architecture" }, + 'qHostInfo' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_dump_key_value_pairs, 'name' : "get host information" }, + 'qC' : { 'cmd' : cmd_qC , 'rsp' : rsp_qC , 'name' : "return the current thread ID" }, + 'vCont' : { 'cmd' : cmd_vCont , 'rsp' : rsp_vCont , 'name' : "extended continue command" }, + 'vAttach' : { 'cmd' : cmd_vAttach , 'rsp' : rsp_stop_reply , 'name' : "attach to process" }, + 'c' : { 'cmd' : cmd_c , 'rsp' : rsp_stop_reply , 'name' : "continue" }, + 's' : { 'cmd' : cmd_s , 'rsp' : rsp_stop_reply , 'name' : "step" }, + 'qRegisterInfo' : { 'cmd' : cmd_qRegisterInfo , 'rsp' : rsp_qRegisterInfo , 'name' : "query register info" }, + 'qfThreadInfo' : { 'cmd' : cmd_qThreadInfo , 'rsp' : rsp_qThreadInfo , 'name' : "get current thread list" }, + 'qsThreadInfo' : { 'cmd' : cmd_qThreadInfo , 'rsp' : rsp_qThreadInfo , 'name' : "get current thread list" }, + 'qShlibInfoAddr' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_hex_big_endian , 'name' : "get shared library info address" }, + 'qMemoryRegionInfo' : { 'cmd' : cmd_mem_rgn_info , 'rsp' : rsp_dump_key_value_pairs, 'name' : "get memory region information" }, + 'qProcessInfo' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_dump_key_value_pairs, 'name' : "get process info" }, + 'qSupported' : { 'cmd' : cmd_query_packet , 'rsp' : rsp_ok_means_supported , 'name' : "query supported" }, + 'qXfer:' : { 'cmd' : cmd_qXfer , 'rsp' : rsp_qXfer , 'name' : "qXfer" }, + 'qSymbol:' : { 'cmd' : cmd_qSymbol , 'rsp' : rsp_qSymbol , 'name' : "qSymbol" }, + 'x' : { 'cmd' : cmd_read_mem_bin , 'rsp' : rsp_mem_bin_bytes , 'name' : "read memory binary" }, + 'X' : { 'cmd' : cmd_write_memory , 'rsp' : rsp_ok_means_success , 'name' : "write memory binary" }, + 'm' : { 'cmd' : cmd_read_memory , 'rsp' : rsp_memory_bytes , 'name' : "read memory" }, + 'M' : { 'cmd' : cmd_write_memory , 'rsp' : rsp_ok_means_success , 'name' : "write memory" }, + '_M' : { 'cmd' : cmd_alloc_memory , 'rsp' : rsp_alloc_memory , 'name' : "allocate memory" }, + '_m' : { 'cmd' : cmd_dealloc_memory , 'rsp' : rsp_ok_means_success , 'name' : "deallocate memory" }, + 'p' : { 'cmd' : cmd_read_one_reg , 'rsp' : rsp_read_one_reg , 'name' : "read single register" }, + 'P' : { 'cmd' : cmd_write_one_reg , 'rsp' : rsp_ok_means_success , 'name' : "write single register" }, + 'g' : { 'cmd' : cmd_read_all_regs , 'rsp' : rsp_read_all_regs , 'name' : "read all registers" }, + 'G' : { 'cmd' : cmd_write_all_regs , 'rsp' : rsp_ok_means_success , 'name' : "write all registers" }, + 'z' : { 'cmd' : cmd_bp , 'rsp' : rsp_ok_means_success , 'name' : "clear breakpoint or watchpoint" }, + 'Z' : { 'cmd' : cmd_bp , 'rsp' : rsp_ok_means_success , 'name' : "set breakpoint or watchpoint" }, + 'k' : { 'cmd' : cmd_kill , 'rsp' : rsp_stop_reply , 'name' : "kill process" }, + 'jThreadsInfo' : { 'cmd' : cmd_jThreadsInfo , 'rsp' : rsp_json , 'name' : "JSON get all threads info" }, + 'jGetLoadedDynamicLibrariesInfos:' : { 'cmd' : cmd_jGetLoadedDynamicLibrariesInfos, 'rsp' : rsp_jGetLoadedDynamicLibrariesInfos, 'name' : 'JSON get loaded dynamic libraries' }, +} + +def calculate_mean_and_standard_deviation(floats): + sum = 0.0 + count = len(floats) + if count == 0: + return (0.0, 0.0) + for f in floats: + sum += f + mean = sum / count + accum = 0.0 + for f in floats: + delta = f - mean + accum += delta * delta + + std_dev = math.sqrt(accum / (count-1)); + return (mean, std_dev) + +def parse_gdb_log_file(path, options): + f = open(path) + parse_gdb_log(f, options) + f.close() + +def parse_gdb_log(file, options): + '''Parse a GDB log file that was generated by enabling logging with: + (lldb) log enable --threadsafe --timestamp --file <FILE> gdb-remote packets + This log file will contain timestamps and this function will then normalize + those packets to be relative to the first value timestamp that is found and + show delta times between log lines and also keep track of how long it takes + for GDB remote commands to make a send/receive round trip. This can be + handy when trying to figure out why some operation in the debugger is taking + a long time during a preset set of debugger commands.''' + + tricky_commands = [ 'qRegisterInfo' ] + timestamp_regex = re.compile('(\s*)([1-9][0-9]+\.[0-9]+)([^0-9].*)$') + packet_name_regex = re.compile('([A-Za-z_]+)[^a-z]') + packet_transmit_name_regex = re.compile('(?P<direction>send|read) packet: (?P<packet>.*)') + packet_contents_name_regex = re.compile('\$([^#]+)#[0-9a-fA-F]{2}') + packet_checksum_regex = re.compile('.*#[0-9a-fA-F]{2}$') + packet_names_regex_str = '(' + '|'.join(gdb_remote_commands.keys()) + ')(.*)'; + packet_names_regex = re.compile(packet_names_regex_str); + + base_time = 0.0 + last_time = 0.0 + packet_send_time = 0.0 + packet_total_times = {} + packet_times = [] + packet_count = {} + lines = file.read().splitlines() + last_command = None + last_command_args = None + last_command_packet = None + hide_next_response = False + num_lines = len(lines) + skip_count = 0 + for (line_index, line) in enumerate(lines): + # See if we need to skip any lines + if skip_count > 0: + skip_count -= 1 + continue + m = packet_transmit_name_regex.search(line) + is_command = False + direction = None + if m: + direction = m.group('direction') + is_command = direction == 'send' + packet = m.group('packet') + sys.stdout.write(options.colors.green()) + if not options.quiet and not hide_next_response: + print '# ', line + sys.stdout.write(options.colors.reset()) + + #print 'direction = "%s", packet = "%s"' % (direction, packet) + + if packet[0] == '+': + if is_command: + print '-->', + else: + print '<--', + if not options.quiet: print 'ACK' + continue + elif packet[0] == '-': + if is_command: + print '-->', + else: + print '<--', + if not options.quiet: print 'NACK' + continue + elif packet[0] == '$': + m = packet_contents_name_regex.match(packet) + if not m and packet[0] == '$': + multiline_packet = packet + idx = line_index + 1 + while idx < num_lines: + if not options.quiet and not hide_next_response: + print '# ', lines[idx] + multiline_packet += lines[idx] + m = packet_contents_name_regex.match(multiline_packet) + if m: + packet = multiline_packet + skip_count = idx - line_index + break + else: + idx += 1 + if m: + if is_command: + print '-->', + else: + print '<--', + contents = decode_packet(m.group(1)) + if is_command: + hide_next_response = False + m = packet_names_regex.match (contents) + if m: + last_command = m.group(1) + if last_command == '?': + last_command = '\\?' + packet_name = last_command + last_command_args = m.group(2) + last_command_packet = contents + hide_next_response = gdb_remote_commands[last_command]['cmd'](options, last_command, last_command_args) + else: + packet_match = packet_name_regex.match (contents) + if packet_match: + packet_name = packet_match.group(1) + for tricky_cmd in tricky_commands: + if packet_name.find (tricky_cmd) == 0: + packet_name = tricky_cmd + else: + packet_name = contents + last_command = None + last_command_args = None + last_command_packet = None + elif last_command: + gdb_remote_commands[last_command]['rsp'](options, last_command, last_command_args, contents) + else: + print 'error: invalid packet: "', packet, '"' + else: + print '???' + else: + print '## ', line + match = timestamp_regex.match (line) + if match: + curr_time = float (match.group(2)) + if last_time and not is_command: + delta = curr_time - last_time + packet_times.append(delta) + delta = 0.0 + if base_time: + delta = curr_time - last_time + else: + base_time = curr_time + + if is_command: + packet_send_time = curr_time + elif line.find('read packet: $') >= 0 and packet_name: + if packet_name in packet_total_times: + packet_total_times[packet_name] += delta + packet_count[packet_name] += 1 + else: + packet_total_times[packet_name] = delta + packet_count[packet_name] = 1 + packet_name = None + + if not options or not options.quiet: + print '%s%.6f %+.6f%s' % (match.group(1), curr_time - base_time, delta, match.group(3)) + last_time = curr_time + # else: + # print line + (average, std_dev) = calculate_mean_and_standard_deviation(packet_times) + if average and std_dev: + print '%u packets with average packet time of %f and standard deviation of %f' % (len(packet_times), average, std_dev) + if packet_total_times: + total_packet_time = 0.0 + total_packet_count = 0 + for key, vvv in packet_total_times.items(): + # print ' key = (%s) "%s"' % (type(key), key) + # print 'value = (%s) %s' % (type(vvv), vvv) + # if type(vvv) == 'float': + total_packet_time += vvv + for key, vvv in packet_count.items(): + total_packet_count += vvv + + print '#---------------------------------------------------' + print '# Packet timing summary:' + print '# Totals: time = %6f, count = %6d' % (total_packet_time, total_packet_count) + print '#---------------------------------------------------' + print '# Packet Time (sec) Percent Count ' + print '#------------------------- ---------- ------- ------' + if options and options.sort_count: + res = sorted(packet_count, key=packet_count.__getitem__, reverse=True) + else: + res = sorted(packet_total_times, key=packet_total_times.__getitem__, reverse=True) + + if last_time > 0.0: + for item in res: + packet_total_time = packet_total_times[item] + packet_percent = (packet_total_time / total_packet_time)*100.0 + if packet_percent >= 10.0: + print " %24s %.6f %.2f%% %6d" % (item, packet_total_time, packet_percent, packet_count[item]) + else: + print " %24s %.6f %.2f%% %6d" % (item, packet_total_time, packet_percent, packet_count[item]) + + + +if __name__ == '__main__': + usage = "usage: gdbremote [options]" + description='''The command disassembles a GDB remote packet log.''' + parser = optparse.OptionParser(description=description, prog='gdbremote',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-q', '--quiet', action='store_true', dest='quiet', help='display verbose debug info', default=False) + parser.add_option('-C', '--color', action='store_true', dest='color', help='add terminal colors', default=False) + parser.add_option('-c', '--sort-by-count', action='store_true', dest='sort_count', help='display verbose debug info', default=False) + parser.add_option('--crashlog', type='string', dest='crashlog', help='symbolicate using a darwin crash log file', default=False) + try: + (options, args) = parser.parse_args(sys.argv[1:]) + except: + print 'error: argument error' + sys.exit(1) + + options.colors = TerminalColors(options.color) + options.symbolicator = None + if options.crashlog: + import lldb + lldb.debugger = lldb.SBDebugger.Create() + import lldb.macosx.crashlog + options.symbolicator = lldb.macosx.crashlog.CrashLog(options.crashlog) + print '%s' % (options.symbolicator) + + # This script is being run from the command line, create a debugger in case we are + # going to use any debugger functions in our function. + if len(args): + for file in args: + print '#----------------------------------------------------------------------' + print "# GDB remote log file: '%s'" % file + print '#----------------------------------------------------------------------' + parse_gdb_log_file (file, options) + if options.symbolicator: + print '%s' % (options.symbolicator) + else: + parse_gdb_log(sys.stdin, options) + +else: + import lldb + if lldb.debugger: + # This initializer is being run from LLDB in the embedded command interpreter + # Add any commands contained in this module to LLDB + lldb.debugger.HandleCommand('command script add -f gdbremote.start_gdb_log start_gdb_log') + lldb.debugger.HandleCommand('command script add -f gdbremote.stop_gdb_log stop_gdb_log') + print 'The "start_gdb_log" and "stop_gdb_log" commands are now installed and ready for use, type "start_gdb_log --help" or "stop_gdb_log --help" for more information' diff --git a/examples/python/globals.py b/examples/python/globals.py new file mode 100755 index 0000000000000..fb2739c8b69c0 --- /dev/null +++ b/examples/python/globals.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# For the shells csh, tcsh: +# ( setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python ; ./globals.py <path> [<path> ...]) +# +# For the shells sh, bash: +# PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python ./globals.py <path> [<path> ...] +#---------------------------------------------------------------------- + +import lldb +import commands +import optparse +import os +import shlex +import sys + +def get_globals(raw_path, options): + error = lldb.SBError() + # Resolve the path if needed + path = os.path.expanduser(raw_path) + # Create a target using path + options + target = lldb.debugger.CreateTarget(path, options.arch, options.platform, False, error) + if target: + # Get the executable module + module = target.module[target.executable.basename] + if module: + # Keep track of which variables we have already looked up + global_names = list() + # Iterate through all symbols in the symbol table and watch for any DATA symbols + for symbol in module.symbols: + if symbol.type == lldb.eSymbolTypeData: + # The symbol is a DATA symbol, lets try and find all global variables + # that match this name and print them + global_name = symbol.name + # Make sure we don't lookup the same variable twice + if global_name not in global_names: + global_names.append(global_name) + # Find all global variables by name + global_variable_list = module.FindGlobalVariables (target, global_name, lldb.UINT32_MAX) + if global_variable_list: + # Print results for anything that matched + for global_variable in global_variable_list: + print 'name = %s' % global_variable.name # returns the global variable name as a string + print 'value = %s' % global_variable.value # Returns the variable value as a string + print 'type = %s' % global_variable.type # Returns an lldb.SBType object + print 'addr = %s' % global_variable.addr # Returns an lldb.SBAddress (section offset address) for this global + print 'file_addr = 0x%x' % global_variable.addr.file_addr # Returns the file virtual address for this global + print 'location = %s' % global_variable.location # returns the global variable value as a string + print 'size = %s' % global_variable.size # Returns the size in bytes of this global variable + print + +def globals(command_args): + '''Extract all globals from any arguments which must be paths to object files.''' + usage = "usage: %prog [options] <PATH> [PATH ...]" + description='''This command will find all globals in the specified object file and return an list() of lldb.SBValue objects (which might be empty).''' + parser = optparse.OptionParser(description=description, prog='globals',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-a', '--arch', type='string', metavar='arch', dest='arch', help='Specify an architecture (or triple) to use when extracting from a file.') + parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + try: + (options, args) = parser.parse_args(command_args) + except: + return + + for path in args: + get_globals (path, options) + +if __name__ == '__main__': + lldb.debugger = lldb.SBDebugger.Create() + globals (sys.argv[1:]) + diff --git a/examples/python/jump.py b/examples/python/jump.py new file mode 100644 index 0000000000000..c904009bb4052 --- /dev/null +++ b/examples/python/jump.py @@ -0,0 +1,173 @@ +import lldb, re + +def parse_linespec (linespec, frame, result): + """Handles a subset of GDB-style linespecs. Specifically: + + number - A line in the current file + +offset - The line /offset/ lines after this line + -offset - The line /offset/ lines before this line + filename:number - Line /number/ in file /filename/ + function - The start of /function/ + *address - The pointer target of /address/, which must be a literal (but see `` in LLDB) + + We explicitly do not handle filename:function because it is ambiguous in Objective-C. + + This function returns a list of addresses.""" + + breakpoint = None + target = frame.GetThread().GetProcess().GetTarget() + + matched = False + + if (not matched): + mo = re.match("^([0-9]+)$", linespec) + if (mo != None): + matched = True + #print "Matched <linenum>" + line_number = int(mo.group(1)) + line_entry = frame.GetLineEntry() + if not line_entry.IsValid(): + result.AppendMessage("Specified a line in the current file, but the current frame doesn't have line table information.") + return + breakpoint = target.BreakpointCreateByLocation (line_entry.GetFileSpec(), line_number) + + if (not matched): + mo = re.match("^\+([0-9]+)$", linespec) + if (mo != None): + matched = True + #print "Matched +<count>" + line_number = int(mo.group(1)) + line_entry = frame.GetLineEntry() + if not line_entry.IsValid(): + result.AppendMessage("Specified a line in the current file, but the current frame doesn't have line table information.") + return + breakpoint = target.BreakpointCreateByLocation(line_entry.GetFileSpec(), (line_entry.GetLine() + line_number)) + + if (not matched): + mo = re.match("^\-([0-9]+)$", linespec) + if (mo != None): + matched = True + #print "Matched -<count>" + line_number = int(mo.group(1)) + line_entry = frame.GetLineEntry() + if not line_entry.IsValid(): + result.AppendMessage("Specified a line in the current file, but the current frame doesn't have line table information.") + return + breakpoint = target.BreakpointCreateByLocation(line_entry.GetFileSpec(), (line_entry.GetLine() - line_number)) + + if (not matched): + mo = re.match("^(.*):([0-9]+)$", linespec) + if (mo != None): + matched = True + #print "Matched <filename>:<linenum>" + file_name = mo.group(1) + line_number = int(mo.group(2)) + breakpoint = target.BreakpointCreateByLocation(file_name, line_number) + + if (not matched): + mo = re.match("\*((0x)?([0-9a-f]+))$", linespec) + if (mo != None): + matched = True + #print "Matched <address-expression>" + address = long(mo.group(1), base=0) + breakpoint = target.BreakpointCreateByAddress(address) + + if (not matched): + #print "Trying <function-name>" + breakpoint = target.BreakpointCreateByName(linespec) + + num_locations = breakpoint.GetNumLocations() + + if (num_locations == 0): + result.AppendMessage("The line specification provided doesn't resolve to any addresses.") + + addr_list = [] + + for location_index in range(num_locations): + location = breakpoint.GetLocationAtIndex(location_index) + addr_list.append(location.GetAddress()) + + target.BreakpointDelete(breakpoint.GetID()) + + return addr_list + +def usage_string(): + return """ Sets the program counter to a specific address. + +Syntax: jump <linespec> [<location-id>] + +Command Options Usage: + jump <linenum> + jump +<count> + jump -<count> + jump <filename>:<linenum> + jump <function-name> + jump *<address-expression> + +<location-id> serves to disambiguate when multiple locations could be meant.""" + +def jump (debugger, command, result, internal_dict): + if (command == ""): + result.AppendMessage(usage_string()) + + args = command.split() + + if not debugger.IsValid(): + result.AppendMessage("Invalid debugger!") + return + + target = debugger.GetSelectedTarget() + if not target.IsValid(): + result.AppendMessage("jump requires a valid target.") + return + + process = target.GetProcess() + if not process.IsValid(): + result.AppendMessage("jump requires a valid process.") + return + + thread = process.GetSelectedThread() + if not thread.IsValid(): + result.AppendMessage("jump requires a valid thread.") + return + + frame = thread.GetSelectedFrame() + if not frame.IsValid(): + result.AppendMessage("jump requires a valid frame.") + return + + addresses = parse_linespec(args[0], frame, result) + + stream = lldb.SBStream() + + if len(addresses) == 0: + return + + desired_address = addresses[0] + + if len(addresses) > 1: + if len(args) == 2: + desired_index = int(args[1]) + if (desired_index >= 0) and (desired_index < len(addresses)): + desired_address = addresses[desired_index] + else: + result.AppendMessage("Desired index " + args[1] + " is not one of the options.") + return + else: + index = 0 + result.AppendMessage("The specified location resolves to multiple targets."); + for address in addresses: + stream.Clear() + address.GetDescription(stream) + result.AppendMessage(" Location ID " + str(index) + ": " + stream.GetData()) + index = index + 1 + result.AppendMessage("Please type 'jump " + command + " <location-id>' to choose one.") + return + + frame.SetPC(desired_address.GetLoadAddress(target)) + +if lldb.debugger: + # Module is being run inside the LLDB interpreter + jump.__doc__ = usage_string() + lldb.debugger.HandleCommand('command script add -f jump.jump jump') + print 'The "jump" command has been installed, type "help jump" or "jump <ENTER>" for detailed help.' diff --git a/examples/python/lldb_module_utils.py b/examples/python/lldb_module_utils.py new file mode 100644 index 0000000000000..37f33ba416a50 --- /dev/null +++ b/examples/python/lldb_module_utils.py @@ -0,0 +1,59 @@ +#!/usr/bin/python + +import lldb +import optparse +import shlex +import string +import sys + +def create_dump_module_line_tables_options (): + usage = "usage: dump_module_line_tables [options] MODULE1 [MODULE2 ...]" + description='''Dumps all line tables from all compile units for any modules specified as arguments. Specifying the --verbose flag will output address ranges for each line entry.''' + parser = optparse.OptionParser(description=description, prog='start_gdb_log',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='Display verbose output.', default=False) + return parser + +def dump_module_line_tables(debugger, command, result, dict): + '''Dumps all line tables from all compile units for any modules specified as arguments.''' + command_args = shlex.split(command) + + parser = create_dump_module_line_tables_options () + try: + (options, args) = parser.parse_args(command_args) + except: + return + if command_args: + target = debugger.GetSelectedTarget() + lldb.target = target + for module_name in command_args: + result.PutCString('Searching for module "%s"' % (module_name,)) + module_fspec = lldb.SBFileSpec (module_name, False) + module = target.FindModule (module_fspec); + if module: + for cu_idx in range (module.GetNumCompileUnits()): + cu = module.GetCompileUnitAtIndex(cu_idx) + result.PutCString("\n%s:" % (cu.file)) + for line_idx in range(cu.GetNumLineEntries()): + line_entry = cu.GetLineEntryAtIndex(line_idx) + start_file_addr = line_entry.addr.file_addr + end_file_addr = line_entry.end_addr.file_addr + # If the two addresses are equal, this line table entry is a termination entry + if options.verbose: + if start_file_addr != end_file_addr: + result.PutCString('[%#x - %#x): %s' % (start_file_addr, end_file_addr, line_entry)) + else: + if start_file_addr == end_file_addr: + result.PutCString('%#x: END' % (start_file_addr)) + else: + result.PutCString('%#x: %s' % (start_file_addr, line_entry)) + if start_file_addr == end_file_addr: + result.Printf("\n") + else: + result.PutCString ("no module for '%s'" % module) + else: + result.PutCString ("error: invalid target") + +parser = create_dump_module_line_tables_options () +dump_module_line_tables.__doc__ = parser.format_help() +lldb.debugger.HandleCommand('command script add -f %s.dump_module_line_tables dump_module_line_tables' % __name__) +print 'Installed "dump_module_line_tables" command'
\ No newline at end of file diff --git a/examples/python/lldbtk.py b/examples/python/lldbtk.py new file mode 100644 index 0000000000000..7ada9d77b30c7 --- /dev/null +++ b/examples/python/lldbtk.py @@ -0,0 +1,544 @@ +#!/usr/bin/python + +import lldb +import shlex +import sys +from Tkinter import * +import ttk + +class ValueTreeItemDelegate(object): + def __init__(self, value): + self.value = value + + def get_item_dictionary(self): + name = self.value.name + if name is None: + name = '' + typename = self.value.type + if typename is None: + typename = '' + value = self.value.value + if value is None: + value = '' + summary = self.value.summary + if summary is None: + summary = '' + has_children = self.value.MightHaveChildren() + return { '#0' : name, + 'typename' : typename, + 'value' : value, + 'summary' : summary, + 'children' : has_children, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + for i in range(self.value.num_children): + item_delegate = ValueTreeItemDelegate(self.value.GetChildAtIndex(i)) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class FrameTreeItemDelegate(object): + def __init__(self, frame): + self.frame = frame + + def get_item_dictionary(self): + id = self.frame.GetFrameID() + name = 'frame #%u' % (id); + value = '0x%16.16x' % (self.frame.GetPC()) + stream = lldb.SBStream() + self.frame.GetDescription(stream) + summary = stream.GetData().split("`")[1] + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : self.frame.GetVariables(True, True, True, True).GetSize() > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + variables = self.frame.GetVariables(True, True, True, True) + n = variables.GetSize() + for i in range(n): + item_delegate = ValueTreeItemDelegate(variables[i]) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class ThreadTreeItemDelegate(object): + def __init__(self, thread): + self.thread = thread + + def get_item_dictionary(self): + num_frames = self.thread.GetNumFrames() + name = 'thread #%u' % (self.thread.GetIndexID()) + value = '0x%x' % (self.thread.GetThreadID()) + summary = '%u frames' % (num_frames) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : num_frames > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + for frame in self.thread: + item_delegate = FrameTreeItemDelegate(frame) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class ProcessTreeItemDelegate(object): + def __init__(self, process): + self.process = process + + def get_item_dictionary(self): + id = self.process.GetProcessID() + num_threads = self.process.GetNumThreads() + value = str(self.process.GetProcessID()) + summary = self.process.target.executable.fullpath + return { '#0' : 'process', + 'value': value, + 'summary': summary, + 'children' : num_threads > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + for thread in self.process: + item_delegate = ThreadTreeItemDelegate(thread) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class TargetTreeItemDelegate(object): + def __init__(self, target): + self.target = target + + def get_item_dictionary(self): + value = str(self.target.triple) + summary = self.target.executable.fullpath + return { '#0' : 'target', + 'value': value, + 'summary': summary, + 'children' : True, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + image_item_delegate = TargetImagesTreeItemDelegate(self.target) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class TargetImagesTreeItemDelegate(object): + def __init__(self, target): + self.target = target + + def get_item_dictionary(self): + value = str(self.target.triple) + summary = self.target.executable.fullpath + num_modules = self.target.GetNumModules() + return { '#0' : 'images', + 'value': '', + 'summary': '%u images' % num_modules, + 'children' : num_modules > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + for i in range(self.target.GetNumModules()): + module = self.target.GetModuleAtIndex(i) + image_item_delegate = ModuleTreeItemDelegate(self.target, module, i) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class ModuleTreeItemDelegate(object): + def __init__(self, target, module, index): + self.target = target + self.module = module + self.index = index + + def get_item_dictionary(self): + name = 'module %u' % (self.index) + value = self.module.file.basename + summary = self.module.file.dirname + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : True, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + sections_item_delegate = ModuleSectionsTreeItemDelegate(self.target, self.module) + item_dicts.append(sections_item_delegate.get_item_dictionary()) + + symbols_item_delegate = ModuleSymbolsTreeItemDelegate(self.target, self.module) + item_dicts.append(symbols_item_delegate.get_item_dictionary()) + + comp_units_item_delegate = ModuleCompileUnitsTreeItemDelegate(self.target, self.module) + item_dicts.append(comp_units_item_delegate.get_item_dictionary()) + return item_dicts + +class ModuleSectionsTreeItemDelegate(object): + def __init__(self, target, module): + self.target = target + self.module = module + + def get_item_dictionary(self): + name = 'sections' + value = '' + summary = '%u sections' % (self.module.GetNumSections()) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : True, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_sections = self.module.GetNumSections() + for i in range(num_sections): + section = self.module.GetSectionAtIndex(i) + image_item_delegate = SectionTreeItemDelegate(self.target, section) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class SectionTreeItemDelegate(object): + def __init__(self, target, section): + self.target = target + self.section = section + + def get_item_dictionary(self): + name = self.section.name + section_load_addr = self.section.GetLoadAddress(self.target) + if section_load_addr != lldb.LLDB_INVALID_ADDRESS: + value = '0x%16.16x' % (section_load_addr) + else: + value = '0x%16.16x *' % (self.section.file_addr) + summary = '' + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : self.section.GetNumSubSections() > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_sections = self.section.GetNumSubSections() + for i in range(num_sections): + section = self.section.GetSubSectionAtIndex(i) + image_item_delegate = SectionTreeItemDelegate(self.target, section) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class ModuleCompileUnitsTreeItemDelegate(object): + def __init__(self, target, module): + self.target = target + self.module = module + + def get_item_dictionary(self): + name = 'compile units' + value = '' + summary = '%u compile units' % (self.module.GetNumSections()) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : self.module.GetNumCompileUnits() > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_cus = self.module.GetNumCompileUnits() + for i in range(num_cus): + cu = self.module.GetCompileUnitAtIndex(i) + image_item_delegate = CompileUnitTreeItemDelegate(self.target, cu) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class CompileUnitTreeItemDelegate(object): + def __init__(self, target, cu): + self.target = target + self.cu = cu + + def get_item_dictionary(self): + name = self.cu.GetFileSpec().basename + value = '' + num_lines = self.cu.GetNumLineEntries() + summary = '' + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : num_lines > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + item_delegate = LineTableTreeItemDelegate(self.target, self.cu) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class LineTableTreeItemDelegate(object): + def __init__(self, target, cu): + self.target = target + self.cu = cu + + def get_item_dictionary(self): + name = 'line table' + value = '' + num_lines = self.cu.GetNumLineEntries() + summary = '%u line entries' % (num_lines) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : num_lines > 0, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_lines = self.cu.GetNumLineEntries() + for i in range(num_lines): + line_entry = self.cu.GetLineEntryAtIndex(i) + item_delegate = LineEntryTreeItemDelegate(self.target, line_entry, i) + item_dicts.append(item_delegate.get_item_dictionary()) + return item_dicts + +class LineEntryTreeItemDelegate(object): + def __init__(self, target, line_entry, index): + self.target = target + self.line_entry = line_entry + self.index = index + + def get_item_dictionary(self): + name = str(self.index) + address = self.line_entry.GetStartAddress() + load_addr = address.GetLoadAddress(self.target) + if load_addr != lldb.LLDB_INVALID_ADDRESS: + value = '0x%16.16x' % (load_addr) + else: + value = '0x%16.16x *' % (address.file_addr) + summary = self.line_entry.GetFileSpec().fullpath + ':' + str(self.line_entry.line) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : False, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + return item_dicts + +class InstructionTreeItemDelegate(object): + def __init__(self, target, instr): + self.target = target + self.instr = instr + + def get_item_dictionary(self): + address = self.instr.GetAddress() + load_addr = address.GetLoadAddress(self.target) + if load_addr != lldb.LLDB_INVALID_ADDRESS: + name = '0x%16.16x' % (load_addr) + else: + name = '0x%16.16x *' % (address.file_addr) + value = self.instr.GetMnemonic(self.target) + ' ' + self.instr.GetOperands(self.target) + summary = self.instr.GetComment(self.target) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : False, + 'tree-item-delegate' : self } + +class ModuleSymbolsTreeItemDelegate(object): + def __init__(self, target, module): + self.target = target + self.module = module + + def get_item_dictionary(self): + name = 'symbols' + value = '' + summary = '%u symbols' % (self.module.GetNumSymbols()) + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : True, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + num_symbols = self.module.GetNumSymbols() + for i in range(num_symbols): + symbol = self.module.GetSymbolAtIndex(i) + image_item_delegate = SymbolTreeItemDelegate(self.target, symbol, i) + item_dicts.append(image_item_delegate.get_item_dictionary()) + return item_dicts + +class SymbolTreeItemDelegate(object): + def __init__(self, target, symbol, index): + self.target = target + self.symbol = symbol + self.index = index + + def get_item_dictionary(self): + address = self.symbol.GetStartAddress() + name = '[%u]' % self.index + symbol_load_addr = address.GetLoadAddress(self.target) + if symbol_load_addr != lldb.LLDB_INVALID_ADDRESS: + value = '0x%16.16x' % (symbol_load_addr) + else: + value = '0x%16.16x *' % (address.file_addr) + summary = self.symbol.name + return { '#0' : name, + 'value': value, + 'summary': summary, + 'children' : False, + 'tree-item-delegate' : self } + + def get_child_item_dictionaries(self): + item_dicts = list() + return item_dicts + + + +class DelegateTree(ttk.Frame): + + def __init__(self, column_dicts, delegate, title, name): + ttk.Frame.__init__(self, name=name) + self.pack(expand=Y, fill=BOTH) + self.master.title(title) + self.delegate = delegate + self.columns_dicts = column_dicts + self.item_id_to_item_dict = dict() + frame = Frame(self) + frame.pack(side=TOP, fill=BOTH, expand=Y) + self._create_treeview(frame) + self._populate_root() + + def _create_treeview(self, parent): + frame = ttk.Frame(parent) + frame.pack(side=TOP, fill=BOTH, expand=Y) + + column_ids = list() + for i in range(1,len(self.columns_dicts)): + column_ids.append(self.columns_dicts[i]['id']) + # create the tree and scrollbars + self.tree = ttk.Treeview(columns=column_ids) + + scroll_bar_v = ttk.Scrollbar(orient=VERTICAL, command= self.tree.yview) + scroll_bar_h = ttk.Scrollbar(orient=HORIZONTAL, command= self.tree.xview) + self.tree['yscroll'] = scroll_bar_v.set + self.tree['xscroll'] = scroll_bar_h.set + + # setup column headings and columns properties + for columns_dict in self.columns_dicts: + self.tree.heading(columns_dict['id'], text=columns_dict['text'], anchor=columns_dict['anchor']) + self.tree.column(columns_dict['id'], stretch=columns_dict['stretch']) + + # add tree and scrollbars to frame + self.tree.grid(in_=frame, row=0, column=0, sticky=NSEW) + scroll_bar_v.grid(in_=frame, row=0, column=1, sticky=NS) + scroll_bar_h.grid(in_=frame, row=1, column=0, sticky=EW) + + # set frame resizing priorities + frame.rowconfigure(0, weight=1) + frame.columnconfigure(0, weight=1) + + # action to perform when a node is expanded + self.tree.bind('<<TreeviewOpen>>', self._update_tree) + + def insert_items(self, parent_id, item_dicts): + for item_dict in item_dicts: + name = None + values = list() + first = True + for columns_dict in self.columns_dicts: + if first: + name = item_dict[columns_dict['id']] + first = False + else: + values.append(item_dict[columns_dict['id']]) + item_id = self.tree.insert (parent_id, # root item has an empty name + END, + text=name, + values=values) + self.item_id_to_item_dict[item_id] = item_dict + if item_dict['children']: + self.tree.insert(item_id, END, text='dummy') + + def _populate_root(self): + # use current directory as root node + self.insert_items('', self.delegate.get_child_item_dictionaries()) + + def _update_tree(self, event): + # user expanded a node - build the related directory + item_id = self.tree.focus() # the id of the expanded node + children = self.tree.get_children (item_id) + if len(children): + first_child = children[0] + # if the node only has a 'dummy' child, remove it and + # build new directory; skip if the node is already + # populated + if self.tree.item(first_child, option='text') == 'dummy': + self.tree.delete(first_child) + item_dict = self.item_id_to_item_dict[item_id] + item_dicts = item_dict['tree-item-delegate'].get_child_item_dictionaries() + self.insert_items(item_id, item_dicts) + +@lldb.command("tk-variables") +def tk_variable_display(debugger, command, result, dict): + sys.argv = ['tk-variables'] # needed for tree creation in TK library as it uses sys.argv... + target = debugger.GetSelectedTarget() + if not target: + print >>result, "invalid target" + return + process = target.GetProcess() + if not process: + print >>result, "invalid process" + return + thread = process.GetSelectedThread() + if not thread: + print >>result, "invalid thread" + return + frame = thread.GetSelectedFrame() + if not frame: + print >>result, "invalid frame" + return + # Parse command line args + command_args = shlex.split(command) + column_dicts = [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'typename', 'text' : 'Type' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'summary' , 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }] + tree = DelegateTree(column_dicts, FrameTreeItemDelegate(frame), 'Variables', 'lldb-tk-variables') + tree.mainloop() + +@lldb.command("tk-process") +def tk_process_display(debugger, command, result, dict): + sys.argv = ['tk-process'] # needed for tree creation in TK library as it uses sys.argv... + target = debugger.GetSelectedTarget() + if not target: + print >>result, "invalid target" + return + process = target.GetProcess() + if not process: + print >>result, "invalid process" + return + # Parse command line args + columnd_dicts = [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'summary', 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }]; + command_args = shlex.split(command) + tree = DelegateTree(columnd_dicts, ProcessTreeItemDelegate(process), 'Process', 'lldb-tk-process') + tree.mainloop() + +@lldb.command("tk-target") +def tk_target_display(debugger, command, result, dict): + sys.argv = ['tk-target'] # needed for tree creation in TK library as it uses sys.argv... + target = debugger.GetSelectedTarget() + if not target: + print >>result, "invalid target" + return + # Parse command line args + columnd_dicts = [{ 'id' : '#0' , 'text' : 'Name' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'value' , 'text' : 'Value' , 'anchor' : W , 'stretch' : 0 }, + { 'id' : 'summary', 'text' : 'Summary', 'anchor' : W , 'stretch' : 1 }]; + command_args = shlex.split(command) + tree = DelegateTree(columnd_dicts, TargetTreeItemDelegate(target), 'Target', 'lldb-tk-target') + tree.mainloop() + diff --git a/examples/python/mach_o.py b/examples/python/mach_o.py new file mode 100755 index 0000000000000..a609b09ed0ebf --- /dev/null +++ b/examples/python/mach_o.py @@ -0,0 +1,1687 @@ +#!/usr/bin/python + +import cmd +import dict_utils +import file_extract +import optparse +import re +import struct +import string +import StringIO +import sys +import uuid + +# Mach header "magic" constants +MH_MAGIC = 0xfeedface +MH_CIGAM = 0xcefaedfe +MH_MAGIC_64 = 0xfeedfacf +MH_CIGAM_64 = 0xcffaedfe +FAT_MAGIC = 0xcafebabe +FAT_CIGAM = 0xbebafeca + +# Mach haeder "filetype" constants +MH_OBJECT = 0x00000001 +MH_EXECUTE = 0x00000002 +MH_FVMLIB = 0x00000003 +MH_CORE = 0x00000004 +MH_PRELOAD = 0x00000005 +MH_DYLIB = 0x00000006 +MH_DYLINKER = 0x00000007 +MH_BUNDLE = 0x00000008 +MH_DYLIB_STUB = 0x00000009 +MH_DSYM = 0x0000000a +MH_KEXT_BUNDLE = 0x0000000b + +# Mach haeder "flag" constant bits +MH_NOUNDEFS = 0x00000001 +MH_INCRLINK = 0x00000002 +MH_DYLDLINK = 0x00000004 +MH_BINDATLOAD = 0x00000008 +MH_PREBOUND = 0x00000010 +MH_SPLIT_SEGS = 0x00000020 +MH_LAZY_INIT = 0x00000040 +MH_TWOLEVEL = 0x00000080 +MH_FORCE_FLAT = 0x00000100 +MH_NOMULTIDEFS = 0x00000200 +MH_NOFIXPREBINDING = 0x00000400 +MH_PREBINDABLE = 0x00000800 +MH_ALLMODSBOUND = 0x00001000 +MH_SUBSECTIONS_VIA_SYMBOLS = 0x00002000 +MH_CANONICAL = 0x00004000 +MH_WEAK_DEFINES = 0x00008000 +MH_BINDS_TO_WEAK = 0x00010000 +MH_ALLOW_STACK_EXECUTION = 0x00020000 +MH_ROOT_SAFE = 0x00040000 +MH_SETUID_SAFE = 0x00080000 +MH_NO_REEXPORTED_DYLIBS = 0x00100000 +MH_PIE = 0x00200000 +MH_DEAD_STRIPPABLE_DYLIB = 0x00400000 +MH_HAS_TLV_DESCRIPTORS = 0x00800000 +MH_NO_HEAP_EXECUTION = 0x01000000 + +# Mach load command constants +LC_REQ_DYLD = 0x80000000 +LC_SEGMENT = 0x00000001 +LC_SYMTAB = 0x00000002 +LC_SYMSEG = 0x00000003 +LC_THREAD = 0x00000004 +LC_UNIXTHREAD = 0x00000005 +LC_LOADFVMLIB = 0x00000006 +LC_IDFVMLIB = 0x00000007 +LC_IDENT = 0x00000008 +LC_FVMFILE = 0x00000009 +LC_PREPAGE = 0x0000000a +LC_DYSYMTAB = 0x0000000b +LC_LOAD_DYLIB = 0x0000000c +LC_ID_DYLIB = 0x0000000d +LC_LOAD_DYLINKER = 0x0000000e +LC_ID_DYLINKER = 0x0000000f +LC_PREBOUND_DYLIB = 0x00000010 +LC_ROUTINES = 0x00000011 +LC_SUB_FRAMEWORK = 0x00000012 +LC_SUB_UMBRELLA = 0x00000013 +LC_SUB_CLIENT = 0x00000014 +LC_SUB_LIBRARY = 0x00000015 +LC_TWOLEVEL_HINTS = 0x00000016 +LC_PREBIND_CKSUM = 0x00000017 +LC_LOAD_WEAK_DYLIB = 0x00000018 | LC_REQ_DYLD +LC_SEGMENT_64 = 0x00000019 +LC_ROUTINES_64 = 0x0000001a +LC_UUID = 0x0000001b +LC_RPATH = 0x0000001c | LC_REQ_DYLD +LC_CODE_SIGNATURE = 0x0000001d +LC_SEGMENT_SPLIT_INFO = 0x0000001e +LC_REEXPORT_DYLIB = 0x0000001f | LC_REQ_DYLD +LC_LAZY_LOAD_DYLIB = 0x00000020 +LC_ENCRYPTION_INFO = 0x00000021 +LC_DYLD_INFO = 0x00000022 +LC_DYLD_INFO_ONLY = 0x00000022 | LC_REQ_DYLD +LC_LOAD_UPWARD_DYLIB = 0x00000023 | LC_REQ_DYLD +LC_VERSION_MIN_MACOSX = 0x00000024 +LC_VERSION_MIN_IPHONEOS = 0x00000025 +LC_FUNCTION_STARTS = 0x00000026 +LC_DYLD_ENVIRONMENT = 0x00000027 + +# Mach CPU constants +CPU_ARCH_MASK = 0xff000000 +CPU_ARCH_ABI64 = 0x01000000 +CPU_TYPE_ANY = 0xffffffff +CPU_TYPE_VAX = 1 +CPU_TYPE_MC680x0 = 6 +CPU_TYPE_I386 = 7 +CPU_TYPE_X86_64 = CPU_TYPE_I386 | CPU_ARCH_ABI64 +CPU_TYPE_MIPS = 8 +CPU_TYPE_MC98000 = 10 +CPU_TYPE_HPPA = 11 +CPU_TYPE_ARM = 12 +CPU_TYPE_MC88000 = 13 +CPU_TYPE_SPARC = 14 +CPU_TYPE_I860 = 15 +CPU_TYPE_ALPHA = 16 +CPU_TYPE_POWERPC = 18 +CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64 + +# VM protection constants +VM_PROT_READ = 1 +VM_PROT_WRITE = 2 +VM_PROT_EXECUTE = 4 + +# VM protection constants +N_STAB = 0xe0 +N_PEXT = 0x10 +N_TYPE = 0x0e +N_EXT = 0x01 + +# Values for nlist N_TYPE bits of the "Mach.NList.type" field. +N_UNDF = 0x0 +N_ABS = 0x2 +N_SECT = 0xe +N_PBUD = 0xc +N_INDR = 0xa + +# Section indexes for the "Mach.NList.sect_idx" fields +NO_SECT = 0 +MAX_SECT = 255 + +# Stab defines +N_GSYM = 0x20 +N_FNAME = 0x22 +N_FUN = 0x24 +N_STSYM = 0x26 +N_LCSYM = 0x28 +N_BNSYM = 0x2e +N_OPT = 0x3c +N_RSYM = 0x40 +N_SLINE = 0x44 +N_ENSYM = 0x4e +N_SSYM = 0x60 +N_SO = 0x64 +N_OSO = 0x66 +N_LSYM = 0x80 +N_BINCL = 0x82 +N_SOL = 0x84 +N_PARAMS = 0x86 +N_VERSION = 0x88 +N_OLEVEL = 0x8A +N_PSYM = 0xa0 +N_EINCL = 0xa2 +N_ENTRY = 0xa4 +N_LBRAC = 0xc0 +N_EXCL = 0xc2 +N_RBRAC = 0xe0 +N_BCOMM = 0xe2 +N_ECOMM = 0xe4 +N_ECOML = 0xe8 +N_LENG = 0xfe + +vm_prot_names = [ '---', 'r--', '-w-', 'rw-', '--x', 'r-x', '-wx', 'rwx' ] + +def dump_memory(base_addr, data, hex_bytes_len, num_per_line): + hex_bytes = data.encode('hex') + if hex_bytes_len == -1: + hex_bytes_len = len(hex_bytes) + addr = base_addr + ascii_str = '' + i = 0 + while i < hex_bytes_len: + if ((i/2) % num_per_line) == 0: + if i > 0: + print ' %s' % (ascii_str) + ascii_str = '' + print '0x%8.8x:' % (addr+i), + hex_byte = hex_bytes[i:i+2] + print hex_byte, + int_byte = int (hex_byte, 16) + ascii_char = '%c' % (int_byte) + if int_byte >= 32 and int_byte < 127: + ascii_str += ascii_char + else: + ascii_str += '.' + i = i + 2 + if ascii_str: + if (i/2) % num_per_line: + padding = num_per_line - ((i/2) % num_per_line) + else: + padding = 0 + print '%*s%s' % (padding*3+1,'',ascii_str) + print + + +class TerminalColors: + '''Simple terminal colors class''' + def __init__(self, enabled = True): + # TODO: discover terminal type from "file" and disable if + # it can't handle the color codes + self.enabled = enabled + + def reset(self): + '''Reset all terminal colors and formatting.''' + if self.enabled: + return "\x1b[0m"; + return '' + + def bold(self, on = True): + '''Enable or disable bold depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[1m"; + else: + return "\x1b[22m"; + return '' + + def italics(self, on = True): + '''Enable or disable italics depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[3m"; + else: + return "\x1b[23m"; + return '' + + def underline(self, on = True): + '''Enable or disable underline depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[4m"; + else: + return "\x1b[24m"; + return '' + + def inverse(self, on = True): + '''Enable or disable inverse depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[7m"; + else: + return "\x1b[27m"; + return '' + + def strike(self, on = True): + '''Enable or disable strike through depending on the "on" parameter.''' + if self.enabled: + if on: + return "\x1b[9m"; + else: + return "\x1b[29m"; + return '' + + def black(self, fg = True): + '''Set the foreground or background color to black. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[30m"; + else: + return "\x1b[40m"; + return '' + + def red(self, fg = True): + '''Set the foreground or background color to red. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[31m"; + else: + return "\x1b[41m"; + return '' + + def green(self, fg = True): + '''Set the foreground or background color to green. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[32m"; + else: + return "\x1b[42m"; + return '' + + def yellow(self, fg = True): + '''Set the foreground or background color to yellow. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[43m"; + else: + return "\x1b[33m"; + return '' + + def blue(self, fg = True): + '''Set the foreground or background color to blue. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[34m"; + else: + return "\x1b[44m"; + return '' + + def magenta(self, fg = True): + '''Set the foreground or background color to magenta. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[35m"; + else: + return "\x1b[45m"; + return '' + + def cyan(self, fg = True): + '''Set the foreground or background color to cyan. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[36m"; + else: + return "\x1b[46m"; + return '' + + def white(self, fg = True): + '''Set the foreground or background color to white. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[37m"; + else: + return "\x1b[47m"; + return '' + + def default(self, fg = True): + '''Set the foreground or background color to the default. + The foreground color will be set if "fg" tests True. The background color will be set if "fg" tests False.''' + if self.enabled: + if fg: + return "\x1b[39m"; + else: + return "\x1b[49m"; + return '' + +def swap_unpack_char(): + """Returns the unpack prefix that will for non-native endian-ness.""" + if struct.pack('H', 1).startswith("\x00"): + return '<' + return '>' + + +def dump_hex_bytes(addr, s, bytes_per_line=16): + i = 0 + line = '' + for ch in s: + if (i % bytes_per_line) == 0: + if line: + print line + line = '%#8.8x: ' % (addr + i) + line += "%02X " % ord(ch) + i += 1 + print line + +def dump_hex_byte_string_diff(addr, a, b, bytes_per_line=16): + i = 0 + line = '' + a_len = len(a) + b_len = len(b) + if a_len < b_len: + max_len = b_len + else: + max_len = a_len + tty_colors = TerminalColors (True) + for i in range(max_len): + ch = None + if i < a_len: + ch_a = a[i] + ch = ch_a + else: + ch_a = None + if i < b_len: + ch_b = b[i] + if not ch: + ch = ch_b + else: + ch_b = None + mismatch = ch_a != ch_b + if (i % bytes_per_line) == 0: + if line: + print line + line = '%#8.8x: ' % (addr + i) + if mismatch: line += tty_colors.red() + line += "%02X " % ord(ch) + if mismatch: line += tty_colors.default() + i += 1 + + print line + +class Mach: + """Class that does everything mach-o related""" + + class Arch: + """Class that implements mach-o architectures""" + + def __init__(self, c=0, s=0): + self.cpu=c + self.sub=s + + def set_cpu_type(self, c): + self.cpu=c + def set_cpu_subtype(self, s): + self.sub=s + def set_arch(self, c, s): + self.cpu=c + self.sub=s + def is_64_bit(self): + return (self.cpu & CPU_ARCH_ABI64) != 0 + + cpu_infos = [ + [ "arm" , CPU_TYPE_ARM , CPU_TYPE_ANY ], + [ "arm" , CPU_TYPE_ARM , 0 ], + [ "armv4" , CPU_TYPE_ARM , 5 ], + [ "armv6" , CPU_TYPE_ARM , 6 ], + [ "armv5" , CPU_TYPE_ARM , 7 ], + [ "xscale" , CPU_TYPE_ARM , 8 ], + [ "armv7" , CPU_TYPE_ARM , 9 ], + [ "armv7f" , CPU_TYPE_ARM , 10 ], + [ "armv7s" , CPU_TYPE_ARM , 11 ], + [ "armv7k" , CPU_TYPE_ARM , 12 ], + [ "armv7m" , CPU_TYPE_ARM , 15 ], + [ "armv7em" , CPU_TYPE_ARM , 16 ], + [ "ppc" , CPU_TYPE_POWERPC , CPU_TYPE_ANY ], + [ "ppc" , CPU_TYPE_POWERPC , 0 ], + [ "ppc601" , CPU_TYPE_POWERPC , 1 ], + [ "ppc602" , CPU_TYPE_POWERPC , 2 ], + [ "ppc603" , CPU_TYPE_POWERPC , 3 ], + [ "ppc603e" , CPU_TYPE_POWERPC , 4 ], + [ "ppc603ev" , CPU_TYPE_POWERPC , 5 ], + [ "ppc604" , CPU_TYPE_POWERPC , 6 ], + [ "ppc604e" , CPU_TYPE_POWERPC , 7 ], + [ "ppc620" , CPU_TYPE_POWERPC , 8 ], + [ "ppc750" , CPU_TYPE_POWERPC , 9 ], + [ "ppc7400" , CPU_TYPE_POWERPC , 10 ], + [ "ppc7450" , CPU_TYPE_POWERPC , 11 ], + [ "ppc970" , CPU_TYPE_POWERPC , 100 ], + [ "ppc64" , CPU_TYPE_POWERPC64 , 0 ], + [ "ppc970-64" , CPU_TYPE_POWERPC64 , 100 ], + [ "i386" , CPU_TYPE_I386 , 3 ], + [ "i486" , CPU_TYPE_I386 , 4 ], + [ "i486sx" , CPU_TYPE_I386 , 0x84 ], + [ "i386" , CPU_TYPE_I386 , CPU_TYPE_ANY ], + [ "x86_64" , CPU_TYPE_X86_64 , 3 ], + [ "x86_64" , CPU_TYPE_X86_64 , CPU_TYPE_ANY ], + ] + + def __str__(self): + for info in self.cpu_infos: + if self.cpu == info[1] and (self.sub & 0x00ffffff) == info[2]: + return info[0] + return "{0}.{1}".format(self.cpu,self.sub) + + + class Magic(dict_utils.Enum): + + enum = { + 'MH_MAGIC' : MH_MAGIC, + 'MH_CIGAM' : MH_CIGAM, + 'MH_MAGIC_64' : MH_MAGIC_64, + 'MH_CIGAM_64' : MH_CIGAM_64, + 'FAT_MAGIC' : FAT_MAGIC, + 'FAT_CIGAM' : FAT_CIGAM + } + + def __init__(self, initial_value = 0): + dict_utils.Enum.__init__(self, initial_value, self.enum) + + def is_skinny_mach_file(self): + return self.value == MH_MAGIC or self.value == MH_CIGAM or self.value == MH_MAGIC_64 or self.value == MH_CIGAM_64 + + def is_universal_mach_file(self): + return self.value == FAT_MAGIC or self.value == FAT_CIGAM + + def unpack(self, data): + data.set_byte_order('native') + self.value = data.get_uint32(); + + def get_byte_order(self): + if self.value == MH_CIGAM or self.value == MH_CIGAM_64 or self.value == FAT_CIGAM: + return swap_unpack_char() + else: + return '=' + + def is_64_bit(self): + return self.value == MH_MAGIC_64 or self.value == MH_CIGAM_64 + + def __init__(self): + self.magic = Mach.Magic() + self.content = None + self.path = None + + def extract (self, path, extractor): + self.path = path; + self.unpack(extractor) + + def parse(self, path): + self.path = path; + try: + f = open(self.path) + file_extractor = file_extract.FileExtract(f, '=') + self.unpack(file_extractor) + #f.close() + except IOError as (errno, strerror): + print "I/O error({0}): {1}".format(errno, strerror) + except ValueError: + print "Could not convert data to an integer." + except: + print "Unexpected error:", sys.exc_info()[0] + raise + + def compare(self, rhs): + self.content.compare(rhs.content) + + def dump(self, options = None): + self.content.dump(options) + + def dump_header(self, dump_description = True, options = None): + self.content.dump_header(dump_description, options) + + def dump_load_commands(self, dump_description = True, options = None): + self.content.dump_load_commands(dump_description, options) + + def dump_sections(self, dump_description = True, options = None): + self.content.dump_sections(dump_description, options) + + def dump_section_contents(self, options): + self.content.dump_section_contents(options) + + def dump_symtab(self, dump_description = True, options = None): + self.content.dump_symtab(dump_description, options) + + def dump_symbol_names_matching_regex(self, regex, file=None): + self.content.dump_symbol_names_matching_regex(regex, file) + + def description(self): + return self.content.description() + + def unpack(self, data): + self.magic.unpack(data) + if self.magic.is_skinny_mach_file(): + self.content = Mach.Skinny(self.path) + elif self.magic.is_universal_mach_file(): + self.content = Mach.Universal(self.path) + else: + self.content = None + + if self.content != None: + self.content.unpack(data, self.magic) + + def is_valid(self): + return self.content != None + + class Universal: + + def __init__(self, path): + self.path = path + self.type = 'universal' + self.file_off = 0 + self.magic = None + self.nfat_arch = 0 + self.archs = list() + + def description(self): + s = '%#8.8x: %s (' % (self.file_off, self.path) + archs_string = '' + for arch in self.archs: + if len(archs_string): + archs_string += ', ' + archs_string += '%s' % arch.arch + s += archs_string + s += ')' + return s + + def unpack(self, data, magic = None): + self.file_off = data.tell() + if magic is None: + self.magic = Mach.Magic() + self.magic.unpack(data) + else: + self.magic = magic + self.file_off = self.file_off - 4 + # Universal headers are always in big endian + data.set_byte_order('big') + self.nfat_arch = data.get_uint32() + for i in range(self.nfat_arch): + self.archs.append(Mach.Universal.ArchInfo()) + self.archs[i].unpack(data) + for i in range(self.nfat_arch): + self.archs[i].mach = Mach.Skinny(self.path) + data.seek (self.archs[i].offset, 0) + skinny_magic = Mach.Magic() + skinny_magic.unpack (data) + self.archs[i].mach.unpack(data, skinny_magic) + + def compare(self, rhs): + print 'error: comparing two universal files is not supported yet' + return False + + def dump(self, options): + if options.dump_header: + print + print "Universal Mach File: magic = %s, nfat_arch = %u" % (self.magic, self.nfat_arch) + print + if self.nfat_arch > 0: + if options.dump_header: + self.archs[0].dump_header(True, options) + for i in range(self.nfat_arch): + self.archs[i].dump_flat(options) + if options.dump_header: + print + for i in range(self.nfat_arch): + self.archs[i].mach.dump(options) + + def dump_header(self, dump_description = True, options = None): + if dump_description: + print self.description() + for i in range(self.nfat_arch): + self.archs[i].mach.dump_header(True, options) + print + + def dump_load_commands(self, dump_description = True, options = None): + if dump_description: + print self.description() + for i in range(self.nfat_arch): + self.archs[i].mach.dump_load_commands(True, options) + print + + def dump_sections(self, dump_description = True, options = None): + if dump_description: + print self.description() + for i in range(self.nfat_arch): + self.archs[i].mach.dump_sections(True, options) + print + + def dump_section_contents(self, options): + for i in range(self.nfat_arch): + self.archs[i].mach.dump_section_contents(options) + print + + def dump_symtab(self, dump_description = True, options = None): + if dump_description: + print self.description() + for i in range(self.nfat_arch): + self.archs[i].mach.dump_symtab(True, options) + print + + def dump_symbol_names_matching_regex(self, regex, file=None): + for i in range(self.nfat_arch): + self.archs[i].mach.dump_symbol_names_matching_regex(regex, file) + + class ArchInfo: + + def __init__(self): + self.arch = Mach.Arch(0,0) + self.offset = 0 + self.size = 0 + self.align = 0 + self.mach = None + + def unpack(self, data): + # Universal headers are always in big endian + data.set_byte_order('big') + self.arch.cpu, self.arch.sub, self.offset, self.size, self.align = data.get_n_uint32(5) + + def dump_header(self, dump_description = True, options = None): + if options.verbose: + print "CPU SUBTYPE OFFSET SIZE ALIGN" + print "---------- ---------- ---------- ---------- ----------" + else: + print "ARCH FILEOFFSET FILESIZE ALIGN" + print "---------- ---------- ---------- ----------" + def dump_flat(self, options): + if options.verbose: + print "%#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) + else: + print "%-10s %#8.8x %#8.8x %#8.8x" % (self.arch, self.offset, self.size, self.align) + def dump(self): + print " cputype: %#8.8x" % self.arch.cpu + print "cpusubtype: %#8.8x" % self.arch.sub + print " offset: %#8.8x" % self.offset + print " size: %#8.8x" % self.size + print " align: %#8.8x" % self.align + def __str__(self): + return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) + def __repr__(self): + return "Mach.Universal.ArchInfo: %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x" % (self.arch.cpu, self.arch.sub, self.offset, self.size, self.align) + + class Flags: + + def __init__(self, b): + self.bits = b + + def __str__(self): + s = '' + if self.bits & MH_NOUNDEFS: + s += 'MH_NOUNDEFS | ' + if self.bits & MH_INCRLINK: + s += 'MH_INCRLINK | ' + if self.bits & MH_DYLDLINK: + s += 'MH_DYLDLINK | ' + if self.bits & MH_BINDATLOAD: + s += 'MH_BINDATLOAD | ' + if self.bits & MH_PREBOUND: + s += 'MH_PREBOUND | ' + if self.bits & MH_SPLIT_SEGS: + s += 'MH_SPLIT_SEGS | ' + if self.bits & MH_LAZY_INIT: + s += 'MH_LAZY_INIT | ' + if self.bits & MH_TWOLEVEL: + s += 'MH_TWOLEVEL | ' + if self.bits & MH_FORCE_FLAT: + s += 'MH_FORCE_FLAT | ' + if self.bits & MH_NOMULTIDEFS: + s += 'MH_NOMULTIDEFS | ' + if self.bits & MH_NOFIXPREBINDING: + s += 'MH_NOFIXPREBINDING | ' + if self.bits & MH_PREBINDABLE: + s += 'MH_PREBINDABLE | ' + if self.bits & MH_ALLMODSBOUND: + s += 'MH_ALLMODSBOUND | ' + if self.bits & MH_SUBSECTIONS_VIA_SYMBOLS: + s += 'MH_SUBSECTIONS_VIA_SYMBOLS | ' + if self.bits & MH_CANONICAL: + s += 'MH_CANONICAL | ' + if self.bits & MH_WEAK_DEFINES: + s += 'MH_WEAK_DEFINES | ' + if self.bits & MH_BINDS_TO_WEAK: + s += 'MH_BINDS_TO_WEAK | ' + if self.bits & MH_ALLOW_STACK_EXECUTION: + s += 'MH_ALLOW_STACK_EXECUTION | ' + if self.bits & MH_ROOT_SAFE: + s += 'MH_ROOT_SAFE | ' + if self.bits & MH_SETUID_SAFE: + s += 'MH_SETUID_SAFE | ' + if self.bits & MH_NO_REEXPORTED_DYLIBS: + s += 'MH_NO_REEXPORTED_DYLIBS | ' + if self.bits & MH_PIE: + s += 'MH_PIE | ' + if self.bits & MH_DEAD_STRIPPABLE_DYLIB: + s += 'MH_DEAD_STRIPPABLE_DYLIB | ' + if self.bits & MH_HAS_TLV_DESCRIPTORS: + s += 'MH_HAS_TLV_DESCRIPTORS | ' + if self.bits & MH_NO_HEAP_EXECUTION: + s += 'MH_NO_HEAP_EXECUTION | ' + # Strip the trailing " |" if we have any flags + if len(s) > 0: + s = s[0:-2] + return s + + class FileType(dict_utils.Enum): + + enum = { + 'MH_OBJECT' : MH_OBJECT , + 'MH_EXECUTE' : MH_EXECUTE , + 'MH_FVMLIB' : MH_FVMLIB , + 'MH_CORE' : MH_CORE , + 'MH_PRELOAD' : MH_PRELOAD , + 'MH_DYLIB' : MH_DYLIB , + 'MH_DYLINKER' : MH_DYLINKER , + 'MH_BUNDLE' : MH_BUNDLE , + 'MH_DYLIB_STUB' : MH_DYLIB_STUB , + 'MH_DSYM' : MH_DSYM , + 'MH_KEXT_BUNDLE' : MH_KEXT_BUNDLE + } + + def __init__(self, initial_value = 0): + dict_utils.Enum.__init__(self, initial_value, self.enum) + + class Skinny: + + def __init__(self, path): + self.path = path + self.type = 'skinny' + self.data = None + self.file_off = 0 + self.magic = 0 + self.arch = Mach.Arch(0,0) + self.filetype = Mach.FileType(0) + self.ncmds = 0 + self.sizeofcmds = 0 + self.flags = Mach.Flags(0) + self.uuid = None + self.commands = list() + self.segments = list() + self.sections = list() + self.symbols = list() + self.sections.append(Mach.Section()) + + def description(self): + return '%#8.8x: %s (%s)' % (self.file_off, self.path, self.arch) + + def unpack(self, data, magic = None): + self.data = data + self.file_off = data.tell() + if magic is None: + self.magic = Mach.Magic() + self.magic.unpack(data) + else: + self.magic = magic + self.file_off = self.file_off - 4 + data.set_byte_order(self.magic.get_byte_order()) + self.arch.cpu, self.arch.sub, self.filetype.value, self.ncmds, self.sizeofcmds, bits = data.get_n_uint32(6) + self.flags.bits = bits + + if self.is_64_bit(): + data.get_uint32() # Skip reserved word in mach_header_64 + + for i in range(0,self.ncmds): + lc = self.unpack_load_command (data) + self.commands.append (lc) + + def get_data(self): + if self.data: + self.data.set_byte_order(self.magic.get_byte_order()) + return self.data + return None + + def unpack_load_command (self, data): + lc = Mach.LoadCommand() + lc.unpack (self, data) + lc_command = lc.command.get_enum_value(); + if (lc_command == LC_SEGMENT or + lc_command == LC_SEGMENT_64): + lc = Mach.SegmentLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_LOAD_DYLIB or + lc_command == LC_ID_DYLIB or + lc_command == LC_LOAD_WEAK_DYLIB or + lc_command == LC_REEXPORT_DYLIB): + lc = Mach.DylibLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_LOAD_DYLINKER or + lc_command == LC_SUB_FRAMEWORK or + lc_command == LC_SUB_CLIENT or + lc_command == LC_SUB_UMBRELLA or + lc_command == LC_SUB_LIBRARY or + lc_command == LC_ID_DYLINKER or + lc_command == LC_RPATH): + lc = Mach.LoadDYLDLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_DYLD_INFO_ONLY): + lc = Mach.DYLDInfoOnlyLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_SYMTAB): + lc = Mach.SymtabLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_DYSYMTAB): + lc = Mach.DYLDSymtabLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_UUID): + lc = Mach.UUIDLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_CODE_SIGNATURE or + lc_command == LC_SEGMENT_SPLIT_INFO or + lc_command == LC_FUNCTION_STARTS): + lc = Mach.DataBlobLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_UNIXTHREAD): + lc = Mach.UnixThreadLoadCommand(lc) + lc.unpack(self, data) + elif (lc_command == LC_ENCRYPTION_INFO): + lc = Mach.EncryptionInfoLoadCommand(lc) + lc.unpack(self, data) + lc.skip(data) + return lc + + def compare(self, rhs): + print "\nComparing:" + print "a) %s %s" % (self.arch, self.path) + print "b) %s %s" % (rhs.arch, rhs.path) + result = True + if self.type == rhs.type: + for lhs_section in self.sections[1:]: + rhs_section = rhs.get_section_by_section(lhs_section) + if rhs_section: + print 'comparing %s.%s...' % (lhs_section.segname, lhs_section.sectname), + sys.stdout.flush() + lhs_data = lhs_section.get_contents (self) + rhs_data = rhs_section.get_contents (rhs) + if lhs_data and rhs_data: + if lhs_data == rhs_data: + print 'ok' + else: + lhs_data_len = len(lhs_data) + rhs_data_len = len(rhs_data) + # if lhs_data_len < rhs_data_len: + # if lhs_data == rhs_data[0:lhs_data_len]: + # print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len) + # else: + # # TODO: check padding + # result = False + # elif lhs_data_len > rhs_data_len: + # if lhs_data[0:rhs_data_len] == rhs_data: + # print 'section data for %s matches the first %u bytes' % (lhs_section.sectname, lhs_data_len) + # else: + # # TODO: check padding + # result = False + # else: + result = False + print 'error: sections differ' + #print 'a) %s' % (lhs_section) + # dump_hex_byte_string_diff(0, lhs_data, rhs_data) + #print 'b) %s' % (rhs_section) + # dump_hex_byte_string_diff(0, rhs_data, lhs_data) + elif lhs_data and not rhs_data: + print 'error: section data missing from b:' + print 'a) %s' % (lhs_section) + print 'b) %s' % (rhs_section) + result = False + elif not lhs_data and rhs_data: + print 'error: section data missing from a:' + print 'a) %s' % (lhs_section) + print 'b) %s' % (rhs_section) + result = False + elif lhs_section.offset or rhs_section.offset: + print 'error: section data missing for both a and b:' + print 'a) %s' % (lhs_section) + print 'b) %s' % (rhs_section) + result = False + else: + print 'ok' + else: + result = False + print 'error: section %s is missing in %s' % (lhs_section.sectname, rhs.path) + else: + print 'error: comaparing a %s mach-o file with a %s mach-o file is not supported' % (self.type, rhs.type) + result = False + if not result: + print 'error: mach files differ' + return result + def dump_header(self, dump_description = True, options = None): + if options.verbose: + print "MAGIC CPU SUBTYPE FILETYPE NUM CMDS SIZE CMDS FLAGS" + print "---------- ---------- ---------- ---------- -------- ---------- ----------" + else: + print "MAGIC ARCH FILETYPE NUM CMDS SIZE CMDS FLAGS" + print "------------ ---------- -------------- -------- ---------- ----------" + + def dump_flat(self, options): + if options.verbose: + print "%#8.8x %#8.8x %#8.8x %#8.8x %#8u %#8.8x %#8.8x" % (self.magic, self.arch.cpu , self.arch.sub, self.filetype.value, self.ncmds, self.sizeofcmds, self.flags.bits) + else: + print "%-12s %-10s %-14s %#8u %#8.8x %s" % (self.magic, self.arch, self.filetype, self.ncmds, self.sizeofcmds, self.flags) + + def dump(self, options): + if options.dump_header: + self.dump_header(True, options) + if options.dump_load_commands: + self.dump_load_commands(False, options) + if options.dump_sections: + self.dump_sections(False, options) + if options.section_names: + self.dump_section_contents(options) + if options.dump_symtab: + self.get_symtab() + if len(self.symbols): + self.dump_sections(False, options) + else: + print "No symbols" + if options.find_mangled: + self.dump_symbol_names_matching_regex (re.compile('^_?_Z')) + + def dump_header(self, dump_description = True, options = None): + if dump_description: + print self.description() + print "Mach Header" + print " magic: %#8.8x %s" % (self.magic.value, self.magic) + print " cputype: %#8.8x %s" % (self.arch.cpu, self.arch) + print " cpusubtype: %#8.8x" % self.arch.sub + print " filetype: %#8.8x %s" % (self.filetype.get_enum_value(), self.filetype.get_enum_name()) + print " ncmds: %#8.8x %u" % (self.ncmds, self.ncmds) + print " sizeofcmds: %#8.8x" % self.sizeofcmds + print " flags: %#8.8x %s" % (self.flags.bits, self.flags) + + def dump_load_commands(self, dump_description = True, options = None): + if dump_description: + print self.description() + for lc in self.commands: + print lc + + def get_section_by_name (self, name): + for section in self.sections: + if section.sectname and section.sectname == name: + return section + return None + + def get_section_by_section (self, other_section): + for section in self.sections: + if section.sectname == other_section.sectname and section.segname == other_section.segname: + return section + return None + + def dump_sections(self, dump_description = True, options = None): + if dump_description: + print self.description() + num_sections = len(self.sections) + if num_sections > 1: + self.sections[1].dump_header() + for sect_idx in range(1,num_sections): + print "%s" % self.sections[sect_idx] + + def dump_section_contents(self, options): + saved_section_to_disk = False + for sectname in options.section_names: + section = self.get_section_by_name(sectname) + if section: + sect_bytes = section.get_contents (self) + if options.outfile: + if not saved_section_to_disk: + outfile = open(options.outfile, 'w') + if options.extract_modules: + #print "Extracting modules from mach file..." + data = file_extract.FileExtract(StringIO.StringIO(sect_bytes), self.data.byte_order) + version = data.get_uint32() + num_modules = data.get_uint32() + #print "version = %u, num_modules = %u" % (version, num_modules) + for i in range(num_modules): + data_offset = data.get_uint64() + data_size = data.get_uint64() + name_offset = data.get_uint32() + language = data.get_uint32() + flags = data.get_uint32() + data.seek (name_offset) + module_name = data.get_c_string() + #print "module[%u] data_offset = %#16.16x, data_size = %#16.16x, name_offset = %#16.16x (%s), language = %u, flags = %#x" % (i, data_offset, data_size, name_offset, module_name, language, flags) + data.seek (data_offset) + outfile.write(data.read_size (data_size)) + else: + print "Saving section %s to '%s'" % (sectname, options.outfile) + outfile.write(sect_bytes) + outfile.close() + saved_section_to_disk = True + else: + print "error: you can only save a single section to disk at a time, skipping section '%s'" % (sectname) + else: + print 'section %s:\n' % (sectname) + section.dump_header() + print '%s\n' % (section) + dump_memory (0, sect_bytes, options.max_count, 16) + else: + print 'error: no section named "%s" was found' % (sectname) + + def get_segment(self, segname): + if len(self.segments) == 1 and self.segments[0].segname == '': + return self.segments[0] + for segment in self.segments: + if segment.segname == segname: + return segment + return None + + def get_first_load_command(self, lc_enum_value): + for lc in self.commands: + if lc.command.value == lc_enum_value: + return lc + return None + + def get_symtab(self): + if self.data and not self.symbols: + lc_symtab = self.get_first_load_command (LC_SYMTAB) + if lc_symtab: + symtab_offset = self.file_off + if self.data.is_in_memory(): + linkedit_segment = self.get_segment('__LINKEDIT') + if linkedit_segment: + linkedit_vmaddr = linkedit_segment.vmaddr + linkedit_fileoff = linkedit_segment.fileoff + symtab_offset = linkedit_vmaddr + lc_symtab.symoff - linkedit_fileoff + symtab_offset = linkedit_vmaddr + lc_symtab.stroff - linkedit_fileoff + else: + symtab_offset += lc_symtab.symoff + + self.data.seek (symtab_offset) + is_64 = self.is_64_bit() + for i in range(lc_symtab.nsyms): + nlist = Mach.NList() + nlist.unpack (self, self.data, lc_symtab) + self.symbols.append(nlist) + else: + print "no LC_SYMTAB" + + def dump_symtab(self, dump_description = True, options = None): + self.get_symtab() + if dump_description: + print self.description() + for i, symbol in enumerate(self.symbols): + print '[%5u] %s' % (i, symbol) + + def dump_symbol_names_matching_regex(self, regex, file=None): + self.get_symtab() + for symbol in self.symbols: + if symbol.name and regex.search (symbol.name): + print symbol.name + if file: + file.write('%s\n' % (symbol.name)) + + def is_64_bit(self): + return self.magic.is_64_bit() + + class LoadCommand: + class Command(dict_utils.Enum): + enum = { + 'LC_SEGMENT' : LC_SEGMENT, + 'LC_SYMTAB' : LC_SYMTAB, + 'LC_SYMSEG' : LC_SYMSEG, + 'LC_THREAD' : LC_THREAD, + 'LC_UNIXTHREAD' : LC_UNIXTHREAD, + 'LC_LOADFVMLIB' : LC_LOADFVMLIB, + 'LC_IDFVMLIB' : LC_IDFVMLIB, + 'LC_IDENT' : LC_IDENT, + 'LC_FVMFILE' : LC_FVMFILE, + 'LC_PREPAGE' : LC_PREPAGE, + 'LC_DYSYMTAB' : LC_DYSYMTAB, + 'LC_LOAD_DYLIB' : LC_LOAD_DYLIB, + 'LC_ID_DYLIB' : LC_ID_DYLIB, + 'LC_LOAD_DYLINKER' : LC_LOAD_DYLINKER, + 'LC_ID_DYLINKER' : LC_ID_DYLINKER, + 'LC_PREBOUND_DYLIB' : LC_PREBOUND_DYLIB, + 'LC_ROUTINES' : LC_ROUTINES, + 'LC_SUB_FRAMEWORK' : LC_SUB_FRAMEWORK, + 'LC_SUB_UMBRELLA' : LC_SUB_UMBRELLA, + 'LC_SUB_CLIENT' : LC_SUB_CLIENT, + 'LC_SUB_LIBRARY' : LC_SUB_LIBRARY, + 'LC_TWOLEVEL_HINTS' : LC_TWOLEVEL_HINTS, + 'LC_PREBIND_CKSUM' : LC_PREBIND_CKSUM, + 'LC_LOAD_WEAK_DYLIB' : LC_LOAD_WEAK_DYLIB, + 'LC_SEGMENT_64' : LC_SEGMENT_64, + 'LC_ROUTINES_64' : LC_ROUTINES_64, + 'LC_UUID' : LC_UUID, + 'LC_RPATH' : LC_RPATH, + 'LC_CODE_SIGNATURE' : LC_CODE_SIGNATURE, + 'LC_SEGMENT_SPLIT_INFO' : LC_SEGMENT_SPLIT_INFO, + 'LC_REEXPORT_DYLIB' : LC_REEXPORT_DYLIB, + 'LC_LAZY_LOAD_DYLIB' : LC_LAZY_LOAD_DYLIB, + 'LC_ENCRYPTION_INFO' : LC_ENCRYPTION_INFO, + 'LC_DYLD_INFO' : LC_DYLD_INFO, + 'LC_DYLD_INFO_ONLY' : LC_DYLD_INFO_ONLY, + 'LC_LOAD_UPWARD_DYLIB' : LC_LOAD_UPWARD_DYLIB, + 'LC_VERSION_MIN_MACOSX' : LC_VERSION_MIN_MACOSX, + 'LC_VERSION_MIN_IPHONEOS' : LC_VERSION_MIN_IPHONEOS, + 'LC_FUNCTION_STARTS' : LC_FUNCTION_STARTS, + 'LC_DYLD_ENVIRONMENT' : LC_DYLD_ENVIRONMENT + } + + def __init__(self, initial_value = 0): + dict_utils.Enum.__init__(self, initial_value, self.enum) + + + def __init__(self, c=None, l=0,o=0): + if c != None: + self.command = c + else: + self.command = Mach.LoadCommand.Command(0) + self.length = l + self.file_off = o + + def unpack(self, mach_file, data): + self.file_off = data.tell() + self.command.value, self.length = data.get_n_uint32(2) + + def skip(self, data): + data.seek (self.file_off + self.length, 0) + + def __str__(self): + lc_name = self.command.get_enum_name() + return '%#8.8x: <%#4.4x> %-24s' % (self.file_off, self.length, lc_name) + + class Section: + + def __init__(self): + self.index = 0 + self.is_64 = False + self.sectname = None + self.segname = None + self.addr = 0 + self.size = 0 + self.offset = 0 + self.align = 0 + self.reloff = 0 + self.nreloc = 0 + self.flags = 0 + self.reserved1 = 0 + self.reserved2 = 0 + self.reserved3 = 0 + + def unpack(self, is_64, data): + self.is_64 = is_64 + self.sectname = data.get_fixed_length_c_string (16, '', True) + self.segname = data.get_fixed_length_c_string (16, '', True) + if self.is_64: + self.addr, self.size = data.get_n_uint64(2) + self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.reserved3 = data.get_n_uint32(8) + else: + self.addr, self.size = data.get_n_uint32(2) + self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2 = data.get_n_uint32(7) + + def dump_header(self): + if self.is_64: + print "INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 RESERVED3 NAME"; + print "===== ------------------ ------------------ ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------"; + else: + print "INDEX ADDRESS SIZE OFFSET ALIGN RELOFF NRELOC FLAGS RESERVED1 RESERVED2 NAME"; + print "===== ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------------------"; + + def __str__(self): + if self.is_64: + return "[%3u] %#16.16x %#16.16x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s" % (self.index, self.addr, self.size, self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.reserved3, self.segname, self.sectname) + else: + return "[%3u] %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %#8.8x %s.%s" % (self.index, self.addr, self.size, self.offset, self.align, self.reloff, self.nreloc, self.flags, self.reserved1, self.reserved2, self.segname, self.sectname) + + def get_contents(self, mach_file): + '''Get the section contents as a python string''' + if self.size > 0 and mach_file.get_segment(self.segname).filesize > 0: + data = mach_file.get_data() + if data: + section_data_offset = mach_file.file_off + self.offset + #print '%s.%s is at offset 0x%x with size 0x%x' % (self.segname, self.sectname, section_data_offset, self.size) + data.push_offset_and_seek (section_data_offset) + bytes = data.read_size(self.size) + data.pop_offset_and_seek() + return bytes + return None + + class DylibLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.name = None + self.timestamp = 0 + self.current_version = 0 + self.compatibility_version = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + name_offset, self.timestamp, self.current_version, self.compatibility_version = data.get_n_uint32(4) + data.seek(self.file_off + name_offset, 0) + self.name = data.get_fixed_length_c_string(self.length - 24) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "%#8.8x %#8.8x %#8.8x " % (self.timestamp, self.current_version, self.compatibility_version) + s += self.name + return s + + class LoadDYLDLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.name = None + + def unpack(self, mach_file, data): + data.get_uint32() + self.name = data.get_fixed_length_c_string(self.length - 12) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "%s" % self.name + return s + + class UnixThreadLoadCommand(LoadCommand): + class ThreadState: + def __init__(self): + self.flavor = 0 + self.count = 0 + self.register_values = list() + + def unpack(self, data): + self.flavor, self.count = data.get_n_uint32(2) + self.register_values = data.get_n_uint32(self.count) + + def __str__(self): + s = "flavor = %u, count = %u, regs =" % (self.flavor, self.count) + i = 0 + for register_value in self.register_values: + if i % 8 == 0: + s += "\n " + s += " %#8.8x" % register_value + i += 1 + return s + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.reg_sets = list() + + def unpack(self, mach_file, data): + reg_set = Mach.UnixThreadLoadCommand.ThreadState() + reg_set.unpack (data) + self.reg_sets.append(reg_set) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + for reg_set in self.reg_sets: + s += "%s" % reg_set + return s + + class DYLDInfoOnlyLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.rebase_off = 0 + self.rebase_size = 0 + self.bind_off = 0 + self.bind_size = 0 + self.weak_bind_off = 0 + self.weak_bind_size = 0 + self.lazy_bind_off = 0 + self.lazy_bind_size = 0 + self.export_off = 0 + self.export_size = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.rebase_off, self.rebase_size, self.bind_off, self.bind_size, self.weak_bind_off, self.weak_bind_size, self.lazy_bind_off, self.lazy_bind_size, self.export_off, self.export_size = data.get_n_uint32(10) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "rebase_off = %#8.8x, rebase_size = %u, " % (self.rebase_off, self.rebase_size) + s += "bind_off = %#8.8x, bind_size = %u, " % (self.bind_off, self.bind_size) + s += "weak_bind_off = %#8.8x, weak_bind_size = %u, " % (self.weak_bind_off, self.weak_bind_size) + s += "lazy_bind_off = %#8.8x, lazy_bind_size = %u, " % (self.lazy_bind_off, self.lazy_bind_size) + s += "export_off = %#8.8x, export_size = %u, " % (self.export_off, self.export_size) + return s + + class DYLDSymtabLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.ilocalsym = 0 + self.nlocalsym = 0 + self.iextdefsym = 0 + self.nextdefsym = 0 + self.iundefsym = 0 + self.nundefsym = 0 + self.tocoff = 0 + self.ntoc = 0 + self.modtaboff = 0 + self.nmodtab = 0 + self.extrefsymoff = 0 + self.nextrefsyms = 0 + self.indirectsymoff = 0 + self.nindirectsyms = 0 + self.extreloff = 0 + self.nextrel = 0 + self.locreloff = 0 + self.nlocrel = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.ilocalsym, self.nlocalsym, self.iextdefsym, self.nextdefsym, self.iundefsym, self.nundefsym, self.tocoff, self.ntoc, self.modtaboff, self.nmodtab, self.extrefsymoff, self.nextrefsyms, self.indirectsymoff, self.nindirectsyms, self.extreloff, self.nextrel, self.locreloff, self.nlocrel = data.get_n_uint32(18) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + # s += "ilocalsym = %u, nlocalsym = %u, " % (self.ilocalsym, self.nlocalsym) + # s += "iextdefsym = %u, nextdefsym = %u, " % (self.iextdefsym, self.nextdefsym) + # s += "iundefsym %u, nundefsym = %u, " % (self.iundefsym, self.nundefsym) + # s += "tocoff = %#8.8x, ntoc = %u, " % (self.tocoff, self.ntoc) + # s += "modtaboff = %#8.8x, nmodtab = %u, " % (self.modtaboff, self.nmodtab) + # s += "extrefsymoff = %#8.8x, nextrefsyms = %u, " % (self.extrefsymoff, self.nextrefsyms) + # s += "indirectsymoff = %#8.8x, nindirectsyms = %u, " % (self.indirectsymoff, self.nindirectsyms) + # s += "extreloff = %#8.8x, nextrel = %u, " % (self.extreloff, self.nextrel) + # s += "locreloff = %#8.8x, nlocrel = %u" % (self.locreloff, self.nlocrel) + s += "ilocalsym = %-10u, nlocalsym = %u\n" % (self.ilocalsym, self.nlocalsym) + s += " iextdefsym = %-10u, nextdefsym = %u\n" % (self.iextdefsym, self.nextdefsym) + s += " iundefsym = %-10u, nundefsym = %u\n" % (self.iundefsym, self.nundefsym) + s += " tocoff = %#8.8x, ntoc = %u\n" % (self.tocoff, self.ntoc) + s += " modtaboff = %#8.8x, nmodtab = %u\n" % (self.modtaboff, self.nmodtab) + s += " extrefsymoff = %#8.8x, nextrefsyms = %u\n" % (self.extrefsymoff, self.nextrefsyms) + s += " indirectsymoff = %#8.8x, nindirectsyms = %u\n" % (self.indirectsymoff, self.nindirectsyms) + s += " extreloff = %#8.8x, nextrel = %u\n" % (self.extreloff, self.nextrel) + s += " locreloff = %#8.8x, nlocrel = %u" % (self.locreloff, self.nlocrel) + return s + + class SymtabLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.symoff = 0 + self.nsyms = 0 + self.stroff = 0 + self.strsize = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.symoff, self.nsyms, self.stroff, self.strsize = data.get_n_uint32(4) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "symoff = %#8.8x, nsyms = %u, stroff = %#8.8x, strsize = %u" % (self.symoff, self.nsyms, self.stroff, self.strsize) + return s + + + class UUIDLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.uuid = None + + def unpack(self, mach_file, data): + uuid_data = data.get_n_uint8(16) + uuid_str = '' + for byte in uuid_data: + uuid_str += '%2.2x' % byte + self.uuid = uuid.UUID(uuid_str) + mach_file.uuid = self.uuid + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += self.uuid.__str__() + return s + + class DataBlobLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.dataoff = 0 + self.datasize = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.dataoff, self.datasize = data.get_n_uint32(2) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "dataoff = %#8.8x, datasize = %u" % (self.dataoff, self.datasize) + return s + + class EncryptionInfoLoadCommand(LoadCommand): + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.cryptoff = 0 + self.cryptsize = 0 + self.cryptid = 0 + + def unpack(self, mach_file, data): + byte_order_char = mach_file.magic.get_byte_order() + self.cryptoff, self.cryptsize, self.cryptid = data.get_n_uint32(3) + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + s += "file-range = [%#8.8x - %#8.8x), cryptsize = %u, cryptid = %u" % (self.cryptoff, self.cryptoff + self.cryptsize, self.cryptsize, self.cryptid) + return s + + class SegmentLoadCommand(LoadCommand): + + def __init__(self, lc): + Mach.LoadCommand.__init__(self, lc.command, lc.length, lc.file_off) + self.segname = None + self.vmaddr = 0 + self.vmsize = 0 + self.fileoff = 0 + self.filesize = 0 + self.maxprot = 0 + self.initprot = 0 + self.nsects = 0 + self.flags = 0 + + def unpack(self, mach_file, data): + is_64 = self.command.get_enum_value() == LC_SEGMENT_64; + self.segname = data.get_fixed_length_c_string (16, '', True) + if is_64: + self.vmaddr, self.vmsize, self.fileoff, self.filesize = data.get_n_uint64(4) + else: + self.vmaddr, self.vmsize, self.fileoff, self.filesize = data.get_n_uint32(4) + self.maxprot, self.initprot, self.nsects, self.flags = data.get_n_uint32(4) + mach_file.segments.append(self) + for i in range(self.nsects): + section = Mach.Section() + section.unpack(is_64, data) + section.index = len (mach_file.sections) + mach_file.sections.append(section) + + + def __str__(self): + s = Mach.LoadCommand.__str__(self); + if self.command.get_enum_value() == LC_SEGMENT: + s += "%#8.8x %#8.8x %#8.8x %#8.8x " % (self.vmaddr, self.vmsize, self.fileoff, self.filesize) + else: + s += "%#16.16x %#16.16x %#16.16x %#16.16x " % (self.vmaddr, self.vmsize, self.fileoff, self.filesize) + s += "%s %s %3u %#8.8x" % (vm_prot_names[self.maxprot], vm_prot_names[self.initprot], self.nsects, self.flags) + s += ' ' + self.segname + return s + + class NList: + class Type: + class Stab(dict_utils.Enum): + enum = { + 'N_GSYM' : N_GSYM , + 'N_FNAME' : N_FNAME , + 'N_FUN' : N_FUN , + 'N_STSYM' : N_STSYM , + 'N_LCSYM' : N_LCSYM , + 'N_BNSYM' : N_BNSYM , + 'N_OPT' : N_OPT , + 'N_RSYM' : N_RSYM , + 'N_SLINE' : N_SLINE , + 'N_ENSYM' : N_ENSYM , + 'N_SSYM' : N_SSYM , + 'N_SO' : N_SO , + 'N_OSO' : N_OSO , + 'N_LSYM' : N_LSYM , + 'N_BINCL' : N_BINCL , + 'N_SOL' : N_SOL , + 'N_PARAMS' : N_PARAMS , + 'N_VERSION' : N_VERSION , + 'N_OLEVEL' : N_OLEVEL , + 'N_PSYM' : N_PSYM , + 'N_EINCL' : N_EINCL , + 'N_ENTRY' : N_ENTRY , + 'N_LBRAC' : N_LBRAC , + 'N_EXCL' : N_EXCL , + 'N_RBRAC' : N_RBRAC , + 'N_BCOMM' : N_BCOMM , + 'N_ECOMM' : N_ECOMM , + 'N_ECOML' : N_ECOML , + 'N_LENG' : N_LENG + } + + def __init__(self, magic = 0): + dict_utils.Enum.__init__(self, magic, self.enum) + + def __init__(self, t = 0): + self.value = t + + def __str__(self): + n_type = self.value + if n_type & N_STAB: + stab = Mach.NList.Type.Stab(self.value) + return '%s' % stab + else: + type = self.value & N_TYPE + type_str = '' + if type == N_UNDF: + type_str = 'N_UNDF' + elif type == N_ABS: + type_str = 'N_ABS ' + elif type == N_SECT: + type_str = 'N_SECT' + elif type == N_PBUD: + type_str = 'N_PBUD' + elif type == N_INDR: + type_str = 'N_INDR' + else: + type_str = "??? (%#2.2x)" % type + if n_type & N_PEXT: + type_str += ' | PEXT' + if n_type & N_EXT: + type_str += ' | EXT ' + return type_str + + + def __init__(self): + self.index = 0 + self.name_offset = 0 + self.name = 0 + self.type = Mach.NList.Type() + self.sect_idx = 0 + self.desc = 0 + self.value = 0 + + def unpack(self, mach_file, data, symtab_lc): + self.index = len(mach_file.symbols) + self.name_offset = data.get_uint32() + self.type.value, self.sect_idx = data.get_n_uint8(2) + self.desc = data.get_uint16() + if mach_file.is_64_bit(): + self.value = data.get_uint64() + else: + self.value = data.get_uint32() + data.push_offset_and_seek (mach_file.file_off + symtab_lc.stroff + self.name_offset) + #print "get string for symbol[%u]" % self.index + self.name = data.get_c_string() + data.pop_offset_and_seek() + + def __str__(self): + name_display = '' + if len(self.name): + name_display = ' "%s"' % self.name + return '%#8.8x %#2.2x (%-20s) %#2.2x %#4.4x %16.16x%s' % (self.name_offset, self.type.value, self.type, self.sect_idx, self.desc, self.value, name_display) + + + class Interactive(cmd.Cmd): + '''Interactive command interpreter to mach-o files.''' + + def __init__(self, mach, options): + cmd.Cmd.__init__(self) + self.intro = 'Interactive mach-o command interpreter' + self.prompt = 'mach-o: %s %% ' % mach.path + self.mach = mach + self.options = options + + def default(self, line): + '''Catch all for unknown command, which will exit the interpreter.''' + print "uknown command: %s" % line + return True + + def do_q(self, line): + '''Quit command''' + return True + + def do_quit(self, line): + '''Quit command''' + return True + + def do_header(self, line): + '''Dump mach-o file headers''' + self.mach.dump_header(True, self.options) + return False + + def do_load(self, line): + '''Dump all mach-o load commands''' + self.mach.dump_load_commands(True, self.options) + return False + + def do_sections(self, line): + '''Dump all mach-o sections''' + self.mach.dump_sections(True, self.options) + return False + + def do_symtab(self, line): + '''Dump all mach-o symbols in the symbol table''' + self.mach.dump_symtab(True, self.options) + return False + +if __name__ == '__main__': + parser = optparse.OptionParser(description='A script that parses skinny and universal mach-o files.') + parser.add_option('--arch', '-a', type='string', metavar='arch', dest='archs', action='append', help='specify one or more architectures by name') + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-H', '--header', action='store_true', dest='dump_header', help='dump the mach-o file header', default=False) + parser.add_option('-l', '--load-commands', action='store_true', dest='dump_load_commands', help='dump the mach-o load commands', default=False) + parser.add_option('-s', '--symtab', action='store_true', dest='dump_symtab', help='dump the mach-o symbol table', default=False) + parser.add_option('-S', '--sections', action='store_true', dest='dump_sections', help='dump the mach-o sections', default=False) + parser.add_option('--section', type='string', metavar='sectname', dest='section_names', action='append', help='Specify one or more section names to dump', default=[]) + parser.add_option('-o', '--out', type='string', dest='outfile', help='Used in conjunction with the --section=NAME option to save a single section\'s data to disk.', default=False) + parser.add_option('-i', '--interactive', action='store_true', dest='interactive', help='enable interactive mode', default=False) + parser.add_option('-m', '--mangled', action='store_true', dest='find_mangled', help='dump all mangled names in a mach file', default=False) + parser.add_option('-c', '--compare', action='store_true', dest='compare', help='compare two mach files', default=False) + parser.add_option('-M', '--extract-modules', action='store_true', dest='extract_modules', help='Extract modules from file', default=False) + parser.add_option('-C', '--count', type='int', dest='max_count', help='Sets the max byte count when dumping section data', default=-1) + + (options, mach_files) = parser.parse_args() + if options.extract_modules: + if options.section_names: + print "error: can't use --section option with the --extract-modules option" + exit(1) + if not options.outfile: + print "error: the --output=FILE option must be specified with the --extract-modules option" + exit(1) + options.section_names.append("__apple_ast") + if options.compare: + if len(mach_files) == 2: + mach_a = Mach() + mach_b = Mach() + mach_a.parse(mach_files[0]) + mach_b.parse(mach_files[1]) + mach_a.compare(mach_b) + else: + print 'error: --compare takes two mach files as arguments' + else: + if not (options.dump_header or options.dump_load_commands or options.dump_symtab or options.dump_sections or options.find_mangled or options.section_names): + options.dump_header = True + options.dump_load_commands = True + if options.verbose: + print 'options', options + print 'mach_files', mach_files + for path in mach_files: + mach = Mach() + mach.parse(path) + if options.interactive: + interpreter = Mach.Interactive(mach, options) + interpreter.cmdloop() + else: + mach.dump(options) + diff --git a/examples/python/memory.py b/examples/python/memory.py new file mode 100755 index 0000000000000..ae78e24e2e29c --- /dev/null +++ b/examples/python/memory.py @@ -0,0 +1,181 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# # To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command +# (lldb) command script import /path/to/cmdtemplate.py +#---------------------------------------------------------------------- + +import commands +import platform +import os +import re +import sys + +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + +import commands +import optparse +import shlex +import string +import struct +import time + +def append_data_callback(option, opt_str, value, parser): + if opt_str == "--uint8": + int8 = int(value, 0) + parser.values.data += struct.pack('1B',int8) + if opt_str == "--uint16": + int16 = int(value, 0) + parser.values.data += struct.pack('1H',int16) + if opt_str == "--uint32": + int32 = int(value, 0) + parser.values.data += struct.pack('1I',int32) + if opt_str == "--uint64": + int64 = int(value, 0) + parser.values.data += struct.pack('1Q',int64) + if opt_str == "--int8": + int8 = int(value, 0) + parser.values.data += struct.pack('1b',int8) + if opt_str == "--int16": + int16 = int(value, 0) + parser.values.data += struct.pack('1h',int16) + if opt_str == "--int32": + int32 = int(value, 0) + parser.values.data += struct.pack('1i',int32) + if opt_str == "--int64": + int64 = int(value, 0) + parser.values.data += struct.pack('1q',int64) + +def create_memfind_options(): + usage = "usage: %prog [options] STARTADDR [ENDADDR]" + description='''This command can find data in a specified address range. +Options are used to specify the data that is to be looked for and the options +can be specified multiple times to look for longer streams of data. +''' + parser = optparse.OptionParser(description=description, prog='memfind',usage=usage) + parser.add_option('-s', '--size', type='int', metavar='BYTESIZE', dest='size', help='Specify the byte size to search.', default=0) + parser.add_option('--int8', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 8 bit signed integer value to search for in memory.', default='') + parser.add_option('--int16', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 16 bit signed integer value to search for in memory.', default='') + parser.add_option('--int32', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 32 bit signed integer value to search for in memory.', default='') + parser.add_option('--int64', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 64 bit signed integer value to search for in memory.', default='') + parser.add_option('--uint8', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 8 bit unsigned integer value to search for in memory.', default='') + parser.add_option('--uint16', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 16 bit unsigned integer value to search for in memory.', default='') + parser.add_option('--uint32', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 32 bit unsigned integer value to search for in memory.', default='') + parser.add_option('--uint64', action="callback", callback=append_data_callback, type='string', metavar='INT', dest='data', help='Specify a 64 bit unsigned integer value to search for in memory.', default='') + return parser + +def memfind_command (debugger, command, result, dict): + # Use the Shell Lexer to properly parse up command options just like a + # shell would + command_args = shlex.split(command) + parser = create_memfind_options() + (options, args) = parser.parse_args(command_args) + # try: + # (options, args) = parser.parse_args(command_args) + # except: + # # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + # result.SetStatus (lldb.eReturnStatusFailed) + # print >>result, "error: option parsing failed" # returning a string is the same as returning an error whose description is the string + # return + memfind (debugger.GetSelectedTarget(), options, args, result) + +def print_error(str, show_usage, result): + print >>result, str + if show_usage: + print >>result, create_memfind_options().format_help() + +def memfind (target, options, args, result): + num_args = len(args) + start_addr = 0 + if num_args == 1: + if options.size > 0: + print_error ("error: --size must be specified if there is no ENDADDR argument", True, result) + return + start_addr = int(args[0], 0) + elif num_args == 2: + if options.size != 0: + print_error ("error: --size can't be specified with an ENDADDR argument", True, result) + return + start_addr = int(args[0], 0) + end_addr = int(args[1], 0) + if start_addr >= end_addr: + print_error ("error: inavlid memory range [%#x - %#x)" % (start_addr, end_addr), True, result) + return + options.size = end_addr - start_addr + else: + print_error ("error: memfind takes 1 or 2 arguments", True, result) + return + + if not options.data: + print >>result, 'error: no data specified to search for' + return + + if not target: + print >>result, 'error: invalid target' + return + process = target.process + if not process: + print >>result, 'error: invalid process' + return + + error = lldb.SBError() + bytes = process.ReadMemory (start_addr, options.size, error) + if error.Success(): + num_matches = 0 + print >>result, "Searching memory range [%#x - %#x) for" % (start_addr, end_addr), + for byte in options.data: + print >>result, '%2.2x' % ord(byte), + print >>result + + match_index = string.find(bytes, options.data) + while match_index != -1: + num_matches = num_matches + 1 + print >>result, '%#x: %#x + %u' % (start_addr + match_index, start_addr, match_index) + match_index = string.find(bytes, options.data, match_index + 1) + + if num_matches == 0: + print >>result, "error: no matches found" + else: + print >>result, 'error: %s' % (error.GetCString()) + + +if __name__ == '__main__': + print 'error: this script is designed to be used within the embedded script interpreter in LLDB' +elif getattr(lldb, 'debugger', None): + memfind_command.__doc__ = create_memfind_options().format_help() + lldb.debugger.HandleCommand('command script add -f memory.memfind_command memfind') + print '"memfind" command installed, use the "--help" option for detailed help' diff --git a/examples/python/operating_system.py b/examples/python/operating_system.py new file mode 100644 index 0000000000000..49cd5ff34398e --- /dev/null +++ b/examples/python/operating_system.py @@ -0,0 +1,104 @@ +#!/usr/bin/python + +import lldb +import struct + +class OperatingSystemPlugIn(object): + """Class that provides data for an instance of a LLDB 'OperatingSystemPython' plug-in class""" + + def __init__(self, process): + '''Initialization needs a valid.SBProcess object. + + This plug-in will get created after a live process is valid and has stopped for the + first time.''' + self.process = None + self.registers = None + self.threads = None + if type(process) is lldb.SBProcess and process.IsValid(): + self.process = process + self.threads = None # Will be an dictionary containing info for each thread + + def get_target(self): + # NOTE: Don't use "lldb.target" when trying to get your target as the "lldb.target" + # tracks the current target in the LLDB command interpreter which isn't the + # correct thing to use for this plug-in. + return self.process.target + + def create_thread(self, tid, context): + if tid == 0x444444444: + thread_info = { 'tid' : tid, 'name' : 'four' , 'queue' : 'queue4', 'state' : 'stopped', 'stop_reason' : 'none' } + self.threads.append(thread_info) + return thread_info + return None + + def get_thread_info(self): + if not self.threads: + # The sample dictionary below shows the values that can be returned for a thread + # tid => thread ID (mandatory) + # name => thread name (optional key/value pair) + # queue => thread dispatch queue name (optional key/value pair) + # state => thred state (mandatory, set to 'stopped' for now) + # stop_reason => thread stop reason. (mandatory, usually set to 'none') + # Possible values include: + # 'breakpoint' if the thread is stopped at a breakpoint + # 'none' thread is just stopped because the process is stopped + # 'trace' the thread just single stepped + # The usual value for this while threads are in memory is 'none' + # register_data_addr => the address of the register data in memory (optional key/value pair) + # Specifying this key/value pair for a thread will avoid a call to get_register_data() + # and can be used when your registers are in a thread context structure that is contiguous + # in memory. Don't specify this if your register layout in memory doesn't match the layout + # described by the dictionary returned from a call to the get_register_info() method. + self.threads = [ + { 'tid' : 0x111111111, 'name' : 'one' , 'queue' : 'queue1', 'state' : 'stopped', 'stop_reason' : 'breakpoint'}, + { 'tid' : 0x222222222, 'name' : 'two' , 'queue' : 'queue2', 'state' : 'stopped', 'stop_reason' : 'none' }, + { 'tid' : 0x333333333, 'name' : 'three', 'queue' : 'queue3', 'state' : 'stopped', 'stop_reason' : 'trace' , 'register_data_addr' : 0x100000000 } + ] + return self.threads + + def get_register_info(self): + if self.registers == None: + self.registers = dict() + triple = self.process.target.triple + if triple: + arch = triple.split('-')[0] + if arch == 'x86_64': + self.registers['sets'] = ['GPR', 'FPU', 'EXC'] + self.registers['registers'] = [ + { 'name':'rax' , 'bitsize' : 64, 'offset' : 0, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 0, 'dwarf' : 0}, + { 'name':'rbx' , 'bitsize' : 64, 'offset' : 8, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 3, 'dwarf' : 3}, + { 'name':'rcx' , 'bitsize' : 64, 'offset' : 16, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 2, 'dwarf' : 2, 'generic':'arg4', 'alt-name':'arg4', }, + { 'name':'rdx' , 'bitsize' : 64, 'offset' : 24, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 1, 'dwarf' : 1, 'generic':'arg3', 'alt-name':'arg3', }, + { 'name':'rdi' , 'bitsize' : 64, 'offset' : 32, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 5, 'dwarf' : 5, 'generic':'arg1', 'alt-name':'arg1', }, + { 'name':'rsi' , 'bitsize' : 64, 'offset' : 40, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 4, 'dwarf' : 4, 'generic':'arg2', 'alt-name':'arg2', }, + { 'name':'rbp' , 'bitsize' : 64, 'offset' : 48, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 6, 'dwarf' : 6, 'generic':'fp' , 'alt-name':'fp', }, + { 'name':'rsp' , 'bitsize' : 64, 'offset' : 56, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 7, 'dwarf' : 7, 'generic':'sp' , 'alt-name':'sp', }, + { 'name':'r8' , 'bitsize' : 64, 'offset' : 64, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 8, 'dwarf' : 8, 'generic':'arg5', 'alt-name':'arg5', }, + { 'name':'r9' , 'bitsize' : 64, 'offset' : 72, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 9, 'dwarf' : 9, 'generic':'arg6', 'alt-name':'arg6', }, + { 'name':'r10' , 'bitsize' : 64, 'offset' : 80, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 10, 'dwarf' : 10}, + { 'name':'r11' , 'bitsize' : 64, 'offset' : 88, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 11, 'dwarf' : 11}, + { 'name':'r12' , 'bitsize' : 64, 'offset' : 96, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 12, 'dwarf' : 12}, + { 'name':'r13' , 'bitsize' : 64, 'offset' : 104, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 13, 'dwarf' : 13}, + { 'name':'r14' , 'bitsize' : 64, 'offset' : 112, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 14, 'dwarf' : 14}, + { 'name':'r15' , 'bitsize' : 64, 'offset' : 120, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 15, 'dwarf' : 15}, + { 'name':'rip' , 'bitsize' : 64, 'offset' : 128, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'gcc' : 16, 'dwarf' : 16, 'generic':'pc', 'alt-name':'pc' }, + { 'name':'rflags' , 'bitsize' : 64, 'offset' : 136, 'encoding':'uint' , 'format':'hex' , 'set': 0, 'generic':'flags', 'alt-name':'flags' }, + { 'name':'cs' , 'bitsize' : 64, 'offset' : 144, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + { 'name':'fs' , 'bitsize' : 64, 'offset' : 152, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + { 'name':'gs' , 'bitsize' : 64, 'offset' : 160, 'encoding':'uint' , 'format':'hex' , 'set': 0 }, + ] + return self.registers + + def get_register_data(self, tid): + if tid == 0x111111111: + return struct.pack('21Q',1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21); + elif tid == 0x222222222: + return struct.pack('21Q',11,12,13,14,15,16,17,18,19,110,111,112,113,114,115,116,117,118,119,120,121); + elif tid == 0x333333333: + return struct.pack('21Q',21,22,23,24,25,26,27,28,29,210,211,212,213,214,215,216,217,218,219,220,221); + elif tid == 0x444444444: + return struct.pack('21Q',31,32,33,34,35,36,37,38,39,310,311,312,313,314,315,316,317,318,319,320,321); + else: + return struct.pack('21Q',41,42,43,44,45,46,47,48,49,410,411,412,413,414,415,416,417,418,419,420,421); + return None + diff --git a/examples/python/performance.py b/examples/python/performance.py new file mode 100755 index 0000000000000..a225d7b731e07 --- /dev/null +++ b/examples/python/performance.py @@ -0,0 +1,335 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# On MacOSX csh, tcsh: +# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +# On MacOSX sh, bash: +# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +#---------------------------------------------------------------------- + +import commands +import optparse +import os +import platform +import re +import resource +import sys +import time +import types + +#---------------------------------------------------------------------- +# Code that auto imports LLDB +#---------------------------------------------------------------------- +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + + +class Timer: + def __enter__(self): + self.start = time.clock() + return self + + def __exit__(self, *args): + self.end = time.clock() + self.interval = self.end - self.start + +class Action(object): + """Class that encapsulates actions to take when a thread stops for a reason.""" + def __init__(self, callback = None, callback_owner = None): + self.callback = callback + self.callback_owner = callback_owner + def ThreadStopped (self, thread): + assert False, "performance.Action.ThreadStopped(self, thread) must be overridden in a subclass" + +class PlanCompleteAction (Action): + def __init__(self, callback = None, callback_owner = None): + Action.__init__(self, callback, callback_owner) + def ThreadStopped (self, thread): + if thread.GetStopReason() == lldb.eStopReasonPlanComplete: + if self.callback: + if self.callback_owner: + self.callback (self.callback_owner, thread) + else: + self.callback (thread) + return True + return False + + +class BreakpointAction (Action): + def __init__(self, callback = None, callback_owner = None, name = None, module = None, file = None, line = None, breakpoint = None): + Action.__init__(self, callback, callback_owner) + self.modules = lldb.SBFileSpecList() + self.files = lldb.SBFileSpecList() + self.breakpoints = list() + # "module" can be a list or a string + if breakpoint: + self.breakpoints.append(breakpoint) + else: + if module: + if isinstance(module, types.ListType): + for module_path in module: + self.modules.Append(lldb.SBFileSpec(module_path, False)) + elif isinstance(module, types.StringTypes): + self.modules.Append(lldb.SBFileSpec(module, False)) + if name: + # "file" can be a list or a string + if file: + if isinstance(file, types.ListType): + self.files = lldb.SBFileSpecList() + for f in file: + self.files.Append(lldb.SBFileSpec(f, False)) + elif isinstance(file, types.StringTypes): + self.files.Append(lldb.SBFileSpec(file, False)) + self.breakpoints.append (self.target.BreakpointCreateByName(name, self.modules, self.files)) + elif file and line: + self.breakpoints.append (self.target.BreakpointCreateByLocation(file, line)) + def ThreadStopped (self, thread): + if thread.GetStopReason() == lldb.eStopReasonBreakpoint: + for bp in self.breakpoints: + if bp.GetID() == thread.GetStopReasonDataAtIndex(0): + if self.callback: + if self.callback_owner: + self.callback (self.callback_owner, thread) + else: + self.callback (thread) + return True + return False +class TestCase: + """Class that aids in running performance tests.""" + def __init__(self): + self.verbose = False + self.debugger = lldb.SBDebugger.Create() + self.target = None + self.process = None + self.thread = None + self.launch_info = None + self.done = False + self.listener = self.debugger.GetListener() + self.user_actions = list() + self.builtin_actions = list() + self.bp_id_to_dict = dict() + + def Setup(self, args): + self.launch_info = lldb.SBLaunchInfo(args) + + def Run (self, args): + assert False, "performance.TestCase.Run(self, args) must be subclassed" + + def Launch(self): + if self.target: + error = lldb.SBError() + self.process = self.target.Launch (self.launch_info, error) + if not error.Success(): + print "error: %s" % error.GetCString() + if self.process: + self.process.GetBroadcaster().AddListener(self.listener, lldb.SBProcess.eBroadcastBitStateChanged | lldb.SBProcess.eBroadcastBitInterrupt) + return True + return False + + def WaitForNextProcessEvent (self): + event = None + if self.process: + while event is None: + process_event = lldb.SBEvent() + if self.listener.WaitForEvent (lldb.UINT32_MAX, process_event): + state = lldb.SBProcess.GetStateFromEvent (process_event) + if self.verbose: + print "event = %s" % (lldb.SBDebugger.StateAsCString(state)) + if lldb.SBProcess.GetRestartedFromEvent(process_event): + continue + if state == lldb.eStateInvalid or state == lldb.eStateDetached or state == lldb.eStateCrashed or state == lldb.eStateUnloaded or state == lldb.eStateExited: + event = process_event + self.done = True + elif state == lldb.eStateConnected or state == lldb.eStateAttaching or state == lldb.eStateLaunching or state == lldb.eStateRunning or state == lldb.eStateStepping or state == lldb.eStateSuspended: + continue + elif state == lldb.eStateStopped: + event = process_event + call_test_step = True + fatal = False + selected_thread = False + for thread in self.process: + frame = thread.GetFrameAtIndex(0) + select_thread = False + + stop_reason = thread.GetStopReason() + if self.verbose: + print "tid = %#x pc = %#x " % (thread.GetThreadID(),frame.GetPC()), + if stop_reason == lldb.eStopReasonNone: + if self.verbose: + print "none" + elif stop_reason == lldb.eStopReasonTrace: + select_thread = True + if self.verbose: + print "trace" + elif stop_reason == lldb.eStopReasonPlanComplete: + select_thread = True + if self.verbose: + print "plan complete" + elif stop_reason == lldb.eStopReasonThreadExiting: + if self.verbose: + print "thread exiting" + elif stop_reason == lldb.eStopReasonExec: + if self.verbose: + print "exec" + elif stop_reason == lldb.eStopReasonInvalid: + if self.verbose: + print "invalid" + elif stop_reason == lldb.eStopReasonException: + select_thread = True + if self.verbose: + print "exception" + fatal = True + elif stop_reason == lldb.eStopReasonBreakpoint: + select_thread = True + bp_id = thread.GetStopReasonDataAtIndex(0) + bp_loc_id = thread.GetStopReasonDataAtIndex(1) + if self.verbose: + print "breakpoint id = %d.%d" % (bp_id, bp_loc_id) + elif stop_reason == lldb.eStopReasonWatchpoint: + select_thread = True + if self.verbose: + print "watchpoint id = %d" % (thread.GetStopReasonDataAtIndex(0)) + elif stop_reason == lldb.eStopReasonSignal: + select_thread = True + if self.verbose: + print "signal %d" % (thread.GetStopReasonDataAtIndex(0)) + + if select_thread and not selected_thread: + self.thread = thread + selected_thread = self.process.SetSelectedThread(thread) + + for action in self.user_actions: + action.ThreadStopped (thread) + + + if fatal: + # if self.verbose: + # Xcode.RunCommand(self.debugger,"bt all",true) + sys.exit(1) + return event + +class Measurement: + '''A class that encapsulates a measurement''' + def __init__(self): + object.__init__(self) + def Measure(self): + assert False, "performance.Measurement.Measure() must be subclassed" + +class MemoryMeasurement(Measurement): + '''A class that can measure memory statistics for a process.''' + def __init__(self, pid): + Measurement.__init__(self) + self.pid = pid + self.stats = ["rprvt","rshrd","rsize","vsize","vprvt","kprvt","kshrd","faults","cow","pageins"] + self.command = "top -l 1 -pid %u -stats %s" % (self.pid, ",".join(self.stats)) + self.value = dict() + + def Measure(self): + output = commands.getoutput(self.command).split("\n")[-1] + values = re.split('[-+\s]+', output) + for (idx, stat) in enumerate(values): + multiplier = 1 + if stat: + if stat[-1] == 'K': + multiplier = 1024 + stat = stat[:-1] + elif stat[-1] == 'M': + multiplier = 1024*1024 + stat = stat[:-1] + elif stat[-1] == 'G': + multiplier = 1024*1024*1024 + elif stat[-1] == 'T': + multiplier = 1024*1024*1024*1024 + stat = stat[:-1] + self.value[self.stats[idx]] = int (stat) * multiplier + + def __str__(self): + '''Dump the MemoryMeasurement current value''' + s = '' + for key in self.value.keys(): + if s: + s += "\n" + s += "%8s = %s" % (key, self.value[key]) + return s + + +class TesterTestCase(TestCase): + def __init__(self): + TestCase.__init__(self) + self.verbose = True + self.num_steps = 5 + + def BreakpointHit (self, thread): + bp_id = thread.GetStopReasonDataAtIndex(0) + loc_id = thread.GetStopReasonDataAtIndex(1) + print "Breakpoint %i.%i hit: %s" % (bp_id, loc_id, thread.process.target.FindBreakpointByID(bp_id)) + thread.StepOver() + + def PlanComplete (self, thread): + if self.num_steps > 0: + thread.StepOver() + self.num_steps = self.num_steps - 1 + else: + thread.process.Kill() + + def Run (self, args): + self.Setup(args) + with Timer() as total_time: + self.target = self.debugger.CreateTarget(args[0]) + if self.target: + with Timer() as breakpoint_timer: + bp = self.target.BreakpointCreateByName("main") + print('Breakpoint time = %.03f sec.' % breakpoint_timer.interval) + + self.user_actions.append (BreakpointAction(breakpoint=bp, callback=TesterTestCase.BreakpointHit, callback_owner=self)) + self.user_actions.append (PlanCompleteAction(callback=TesterTestCase.PlanComplete, callback_owner=self)) + + if self.Launch(): + while not self.done: + self.WaitForNextProcessEvent() + else: + print "error: failed to launch process" + else: + print "error: failed to create target with '%s'" % (args[0]) + print('Total time = %.03f sec.' % total_time.interval) + + +if __name__ == '__main__': + lldb.SBDebugger.Initialize() + test = TesterTestCase() + test.Run (sys.argv[1:]) + mem = MemoryMeasurement(os.getpid()) + mem.Measure() + print str(mem) + lldb.SBDebugger.Terminate() + # print "sleeeping for 100 seconds" + # time.sleep(100) diff --git a/examples/python/process_events.py b/examples/python/process_events.py new file mode 100755 index 0000000000000..e8ccc5f902302 --- /dev/null +++ b/examples/python/process_events.py @@ -0,0 +1,278 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# On MacOSX csh, tcsh: +# setenv PYTHONPATH /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +# On MacOSX sh, bash: +# export PYTHONPATH=/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python +#---------------------------------------------------------------------- + +import commands +import optparse +import os +import platform +import sys + +#---------------------------------------------------------------------- +# Code that auto imports LLDB +#---------------------------------------------------------------------- +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + +def print_threads(process, options): + if options.show_threads: + for thread in process: + print '%s %s' % (thread, thread.GetFrameAtIndex(0)) + +def run_commands(command_interpreter, commands): + return_obj = lldb.SBCommandReturnObject() + for command in commands: + command_interpreter.HandleCommand( command, return_obj ) + if return_obj.Succeeded(): + print return_obj.GetOutput() + else: + print return_obj + if options.stop_on_error: + break + +def main(argv): + description='''Debugs a program using the LLDB python API and uses asynchronous broadcast events to watch for process state changes.''' + epilog='''Examples: + +#---------------------------------------------------------------------- +# Run "/bin/ls" with the arguments "-lAF /tmp/", and set a breakpoint +# at "malloc" and backtrace and read all registers each time we stop +#---------------------------------------------------------------------- +% ./process_events.py --breakpoint malloc --stop-command bt --stop-command 'register read' -- /bin/ls -lAF /tmp/ + +''' + optparse.OptionParser.format_epilog = lambda self, formatter: self.epilog + parser = optparse.OptionParser(description=description, prog='process_events',usage='usage: process_events [options] program [arg1 arg2]', epilog=epilog) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help="Enable verbose logging.", default=False) + parser.add_option('-b', '--breakpoint', action='append', type='string', metavar='BPEXPR', dest='breakpoints', help='Breakpoint commands to create after the target has been created, the values will be sent to the "_regexp-break" command which supports breakpoints by name, file:line, and address.') + parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=None) + parser.add_option('--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".', default=None) + parser.add_option('-l', '--launch-command', action='append', type='string', metavar='CMD', dest='launch_commands', help='LLDB command interpreter commands to run once after the process has launched. This option can be specified more than once.', default=[]) + parser.add_option('-s', '--stop-command', action='append', type='string', metavar='CMD', dest='stop_commands', help='LLDB command interpreter commands to run each time the process stops. This option can be specified more than once.', default=[]) + parser.add_option('-c', '--crash-command', action='append', type='string', metavar='CMD', dest='crash_commands', help='LLDB command interpreter commands to run in case the process crashes. This option can be specified more than once.', default=[]) + parser.add_option('-x', '--exit-command', action='append', type='string', metavar='CMD', dest='exit_commands', help='LLDB command interpreter commands to run once after the process has exited. This option can be specified more than once.', default=[]) + parser.add_option('-T', '--no-threads', action='store_false', dest='show_threads', help="Don't show threads when process stops.", default=True) + parser.add_option('--ignore-errors', action='store_false', dest='stop_on_error', help="Don't stop executing LLDB commands if the command returns an error. This applies to all of the LLDB command interpreter commands that get run for launch, stop, crash and exit.", default=True) + parser.add_option('-n', '--run-count', type='int', dest='run_count', metavar='N', help='How many times to run the process in case the process exits.', default=1) + parser.add_option('-t', '--event-timeout', type='int', dest='event_timeout', metavar='SEC', help='Specify the timeout in seconds to wait for process state change events.', default=lldb.UINT32_MAX) + parser.add_option('-e', '--environment', action='append', type='string', metavar='ENV', dest='env_vars', help='Environment variables to set in the inferior process when launching a process.') + parser.add_option('-d', '--working-dir', type='string', metavar='DIR', dest='working_dir', help='The the current working directory when launching a process.', default=None) + parser.add_option('-p', '--attach-pid', type='int', dest='attach_pid', metavar='PID', help='Specify a process to attach to by process ID.', default=-1) + parser.add_option('-P', '--attach-name', type='string', dest='attach_name', metavar='PROCESSNAME', help='Specify a process to attach to by name.', default=None) + parser.add_option('-w', '--attach-wait', action='store_true', dest='attach_wait', help='Wait for the next process to launch when attaching to a process by name.', default=False) + try: + (options, args) = parser.parse_args(argv) + except: + return + + attach_info = None + launch_info = None + exe = None + if args: + exe = args.pop(0) + launch_info = lldb.SBLaunchInfo (args) + if options.env_vars: + launch_info.SetEnvironmentEntries(options.env_vars, True) + if options.working_dir: + launch_info.SetWorkingDirectory(options.working_dir) + elif options.attach_pid != -1: + if options.run_count == 1: + attach_info = lldb.SBAttachInfo (options.attach_pid) + else: + print "error: --run-count can't be used with the --attach-pid option" + sys.exit(1) + elif not options.attach_name is None: + if options.run_count == 1: + attach_info = lldb.SBAttachInfo (options.attach_name, options.attach_wait) + else: + print "error: --run-count can't be used with the --attach-name option" + sys.exit(1) + else: + print 'error: a program path for a program to debug and its arguments are required' + sys.exit(1) + + + + # Create a new debugger instance + debugger = lldb.SBDebugger.Create() + debugger.SetAsync (True) + command_interpreter = debugger.GetCommandInterpreter() + # Create a target from a file and arch + + if exe: + print "Creating a target for '%s'" % exe + error = lldb.SBError() + target = debugger.CreateTarget (exe, options.arch, options.platform, True, error) + + if target: + + # Set any breakpoints that were specified in the args if we are launching. We use the + # command line command to take advantage of the shorthand breakpoint creation + if launch_info and options.breakpoints: + for bp in options.breakpoints: + debugger.HandleCommand( "_regexp-break %s" % (bp)) + run_commands(command_interpreter, ['breakpoint list']) + + for run_idx in range(options.run_count): + # Launch the process. Since we specified synchronous mode, we won't return + # from this function until we hit the breakpoint at main + error = lldb.SBError() + + if launch_info: + if options.run_count == 1: + print 'Launching "%s"...' % (exe) + else: + print 'Launching "%s"... (launch %u of %u)' % (exe, run_idx + 1, options.run_count) + + process = target.Launch (launch_info, error) + else: + if options.attach_pid != -1: + print 'Attaching to process %i...' % (options.attach_pid) + else: + if options.attach_wait: + print 'Waiting for next to process named "%s" to launch...' % (options.attach_name) + else: + print 'Attaching to existing process named "%s"...' % (options.attach_name) + process = target.Attach (attach_info, error) + + # Make sure the launch went ok + if process and process.GetProcessID() != lldb.LLDB_INVALID_PROCESS_ID: + + pid = process.GetProcessID() + print 'Process is %i' % (pid) + if attach_info: + # continue process if we attached as we won't get an initial event + process.Continue() + + listener = debugger.GetListener() + # sign up for process state change events + stop_idx = 0 + done = False + while not done: + event = lldb.SBEvent() + if listener.WaitForEvent (options.event_timeout, event): + if lldb.SBProcess.EventIsProcessEvent(event): + state = lldb.SBProcess.GetStateFromEvent (event) + if state == lldb.eStateInvalid: + # Not a state event + print 'process event = %s' % (event) + else: + print "process state changed event: %s" % (lldb.SBDebugger.StateAsCString(state)) + if state == lldb.eStateStopped: + if stop_idx == 0: + if launch_info: + print "process %u launched" % (pid) + run_commands(command_interpreter, ['breakpoint list']) + else: + print "attached to process %u" % (pid) + for m in target.modules: + print m + if options.breakpoints: + for bp in options.breakpoints: + debugger.HandleCommand( "_regexp-break %s" % (bp)) + run_commands(command_interpreter, ['breakpoint list']) + run_commands (command_interpreter, options.launch_commands) + else: + if options.verbose: + print "process %u stopped" % (pid) + run_commands (command_interpreter, options.stop_commands) + stop_idx += 1 + print_threads (process, options) + print "continuing process %u" % (pid) + process.Continue() + elif state == lldb.eStateExited: + exit_desc = process.GetExitDescription() + if exit_desc: + print "process %u exited with status %u: %s" % (pid, process.GetExitStatus (), exit_desc) + else: + print "process %u exited with status %u" % (pid, process.GetExitStatus ()) + run_commands (command_interpreter, options.exit_commands) + done = True + elif state == lldb.eStateCrashed: + print "process %u crashed" % (pid) + print_threads (process, options) + run_commands (command_interpreter, options.crash_commands) + done = True + elif state == lldb.eStateDetached: + print "process %u detached" % (pid) + done = True + elif state == lldb.eStateRunning: + # process is running, don't say anything, we will always get one of these after resuming + if options.verbose: + print "process %u resumed" % (pid) + elif state == lldb.eStateUnloaded: + print "process %u unloaded, this shouldn't happen" % (pid) + done = True + elif state == lldb.eStateConnected: + print "process connected" + elif state == lldb.eStateAttaching: + print "process attaching" + elif state == lldb.eStateLaunching: + print "process launching" + else: + print 'event = %s' % (event) + else: + # timeout waiting for an event + print "no process event for %u seconds, killing the process..." % (options.event_timeout) + done = True + # Now that we are done dump the stdout and stderr + process_stdout = process.GetSTDOUT(1024) + if process_stdout: + print "Process STDOUT:\n%s" % (process_stdout) + while process_stdout: + process_stdout = process.GetSTDOUT(1024) + print process_stdout + process_stderr = process.GetSTDERR(1024) + if process_stderr: + print "Process STDERR:\n%s" % (process_stderr) + while process_stderr: + process_stderr = process.GetSTDERR(1024) + print process_stderr + process.Kill() # kill the process + else: + if error: + print error + else: + if launch_info: + print 'error: launch failed' + else: + print 'error: attach failed' + + lldb.SBDebugger.Terminate() + +if __name__ == '__main__': + main(sys.argv[1:])
\ No newline at end of file diff --git a/examples/python/pytracer.py b/examples/python/pytracer.py new file mode 100644 index 0000000000000..61bbceefe0577 --- /dev/null +++ b/examples/python/pytracer.py @@ -0,0 +1,328 @@ +import sys +import inspect +from collections import OrderedDict + +class TracebackFancy: + def __init__(self,traceback): + self.t = traceback + + def getFrame(self): + return FrameFancy(self.t.tb_frame) + + def getLineNumber(self): + return self.t.tb_lineno if self.t != None else None + + def getNext(self): + return TracebackFancy(self.t.tb_next) + + def __str__(self): + if self.t == None: + return "" + str_self = "%s @ %s" % (self.getFrame().getName(), self.getLineNumber()) + return str_self + "\n" + self.getNext().__str__() + +class ExceptionFancy: + def __init__(self,frame): + self.etraceback = frame.f_exc_traceback + self.etype = frame.exc_type + self.evalue = frame.f_exc_value + + def __init__(self,tb,ty,va): + self.etraceback = tb + self.etype = ty + self.evalue = va + + def getTraceback(self): + return TracebackFancy(self.etraceback) + + def __nonzero__(self): + return self.etraceback != None or self.etype != None or self.evalue != None + + def getType(self): + return str(self.etype) + + def getValue(self): + return self.evalue + +class CodeFancy: + def __init__(self,code): + self.c = code + + def getArgCount(self): + return self.c.co_argcount if self.c != None else 0 + + def getFilename(self): + return self.c.co_filename if self.c != None else "" + + def getVariables(self): + return self.c.co_varnames if self.c != None else [] + + def getName(self): + return self.c.co_name if self.c != None else "" + + def getFileName(self): + return self.c.co_filename if self.c != None else "" + +class ArgsFancy: + def __init__(self,frame,arginfo): + self.f = frame + self.a = arginfo + + def __str__(self): + args, varargs, kwargs = self.getArgs(), self.getVarArgs(), self.getKWArgs() + ret = "" + count = 0 + size = len(args) + for arg in args: + ret = ret + ("%s = %s" % (arg, args[arg])) + count = count + 1 + if count < size: + ret = ret + ", " + if varargs: + if size > 0: + ret = ret + " " + ret = ret + "varargs are " + str(varargs) + if kwargs: + if size > 0: + ret = ret + " " + ret = ret + "kwargs are " + str(kwargs) + return ret + + def getNumArgs(wantVarargs = False, wantKWArgs=False): + args, varargs, keywords, values = self.a + size = len(args) + if varargs and wantVarargs: + size = size+len(self.getVarArgs()) + if keywords and wantKWArgs: + size = size+len(self.getKWArgs()) + return size + + def getArgs(self): + args, _, _, values = self.a + argWValues = OrderedDict() + for arg in args: + argWValues[arg] = values[arg] + return argWValues + + def getVarArgs(self): + _, vargs, _, _ = self.a + if vargs: + return self.f.f_locals[vargs] + return () + + def getKWArgs(self): + _, _, kwargs, _ = self.a + if kwargs: + return self.f.f_locals[kwargs] + return {} + +class FrameFancy: + def __init__(self,frame): + self.f = frame + + def getCaller(self): + return FrameFancy(self.f.f_back) + + def getLineNumber(self): + return self.f.f_lineno if self.f != None else 0 + + def getCodeInformation(self): + return CodeFancy(self.f.f_code) if self.f != None else None + + def getExceptionInfo(self): + return ExceptionFancy(self.f) if self.f != None else None + + def getName(self): + return self.getCodeInformation().getName() if self.f != None else "" + + def getFileName(self): + return self.getCodeInformation().getFileName() if self.f != None else "" + + def getLocals(self): + return self.f.f_locals if self.f != None else {} + + def getArgumentInfo(self): + return ArgsFancy(self.f,inspect.getargvalues(self.f)) if self.f != None else None + +class TracerClass: + def callEvent(self,frame): + pass + + def lineEvent(self,frame): + pass + + def returnEvent(self,frame,retval): + pass + + def exceptionEvent(self,frame,exception,value,traceback): + pass + + def cCallEvent(self,frame,cfunct): + pass + + def cReturnEvent(self,frame,cfunct): + pass + + def cExceptionEvent(self,frame,cfunct): + pass + +tracer_impl = TracerClass() + + +def the_tracer_entrypoint(frame,event,args): + if tracer_impl == None: + return None + if event == "call": + call_retval = tracer_impl.callEvent(FrameFancy(frame)) + if call_retval == False: + return None + return the_tracer_entrypoint + elif event == "line": + line_retval = tracer_impl.lineEvent(FrameFancy(frame)) + if line_retval == False: + return None + return the_tracer_entrypoint + elif event == "return": + tracer_impl.returnEvent(FrameFancy(frame),args) + elif event == "exception": + exty,exva,extb = args + exception_retval = tracer_impl.exceptionEvent(FrameFancy(frame),ExceptionFancy(extb,exty,exva)) + if exception_retval == False: + return None + return the_tracer_entrypoint + elif event == "c_call": + tracer_impl.cCallEvent(FrameFancy(frame),args) + elif event == "c_return": + tracer_impl.cReturnEvent(FrameFancy(frame),args) + elif event == "c_exception": + tracer_impl.cExceptionEvent(FrameFancy(frame),args) + return None + +def enable(t=None): + global tracer_impl + if t: + tracer_impl = t + sys.settrace(the_tracer_entrypoint) + +def disable(): + sys.settrace(None) + +class LoggingTracer: + def callEvent(self,frame): + print "call " + frame.getName() + " from " + frame.getCaller().getName() + " @ " + str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo()) + + def lineEvent(self,frame): + print "running " + frame.getName() + " @ " + str(frame.getLineNumber()) + " locals are " + str(frame.getLocals()) + " in " + frame.getFileName() + + def returnEvent(self,frame,retval): + print "return from " + frame.getName() + " value is " + str(retval) + " locals are " + str(frame.getLocals()) + + def exceptionEvent(self,frame,exception): + print "exception %s %s raised from %s @ %s" % (exception.getType(), str(exception.getValue()), frame.getName(), frame.getLineNumber()) + print "tb: " + str(exception.getTraceback()) + +# the same functionality as LoggingTracer, but with a little more lldb-specific smarts +class LLDBAwareTracer: + def callEvent(self,frame): + if frame.getName() == "<module>": + return + if frame.getName() == "run_one_line": + print "call run_one_line(%s)" % (frame.getArgumentInfo().getArgs()["input_string"]) + return + if "Python.framework" in frame.getFileName(): + print "call into Python at " + frame.getName() + return + if frame.getName() == "__init__" and frame.getCaller().getName() == "run_one_line" and frame.getCaller().getLineNumber() == 101: + return False + strout = "call " + frame.getName() + if (frame.getCaller().getFileName() == ""): + strout += " from LLDB - args are " + args = frame.getArgumentInfo().getArgs() + for arg in args: + if arg == "dict" or arg == "internal_dict": + continue + strout = strout + ("%s = %s " % (arg,args[arg])) + else: + strout += " from " + frame.getCaller().getName() + " @ " + str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo()) + print strout + + def lineEvent(self,frame): + if frame.getName() == "<module>": + return + if frame.getName() == "run_one_line": + print "running run_one_line(%s) @ %s" % (frame.getArgumentInfo().getArgs()["input_string"],frame.getLineNumber()) + return + if "Python.framework" in frame.getFileName(): + print "running into Python at " + frame.getName() + " @ " + str(frame.getLineNumber()) + return + strout = "running " + frame.getName() + " @ " + str(frame.getLineNumber()) + " locals are " + if (frame.getCaller().getFileName() == ""): + locals = frame.getLocals() + for local in locals: + if local == "dict" or local == "internal_dict": + continue + strout = strout + ("%s = %s " % (local,locals[local])) + else: + strout = strout + str(frame.getLocals()) + strout = strout + " in " + frame.getFileName() + print strout + + def returnEvent(self,frame,retval): + if frame.getName() == "<module>": + return + if frame.getName() == "run_one_line": + print "return from run_one_line(%s) return value is %s" % (frame.getArgumentInfo().getArgs()["input_string"],retval) + return + if "Python.framework" in frame.getFileName(): + print "return from Python at " + frame.getName() + " return value is " + str(retval) + return + strout = "return from " + frame.getName() + " return value is " + str(retval) + " locals are " + if (frame.getCaller().getFileName() == ""): + locals = frame.getLocals() + for local in locals: + if local == "dict" or local == "internal_dict": + continue + strout = strout + ("%s = %s " % (local,locals[local])) + else: + strout = strout + str(frame.getLocals()) + strout = strout + " in " + frame.getFileName() + print strout + + def exceptionEvent(self,frame,exception): + if frame.getName() == "<module>": + return + print "exception %s %s raised from %s @ %s" % (exception.getType(), str(exception.getValue()), frame.getName(), frame.getLineNumber()) + print "tb: " + str(exception.getTraceback()) + +def f(x,y=None): + if x > 0: + return 2 + f(x-2) + return 35 + +def g(x): + return 1.134 / x + +def print_keyword_args(**kwargs): + # kwargs is a dict of the keyword args passed to the function + for key, value in kwargs.iteritems(): + print "%s = %s" % (key, value) + +def total(initial=5, *numbers, **keywords): + count = initial + for number in numbers: + count += number + for key in keywords: + count += keywords[key] + return count + +if __name__ == "__main__": + enable(LoggingTracer()) + f(5) + f(5,1) + print_keyword_args(first_name="John", last_name="Doe") + total(10, 1, 2, 3, vegetables=50, fruits=100) + try: + g(0) + except: + pass + disable() diff --git a/examples/python/sbvalue.py b/examples/python/sbvalue.py new file mode 100755 index 0000000000000..59c0b61e55283 --- /dev/null +++ b/examples/python/sbvalue.py @@ -0,0 +1,255 @@ +#!/usr/bin/python + +import lldb + +class value(object): + '''A class that wraps an lldb.SBValue object and returns an object that + can be used as an object with attribytes:\n + argv = a.value(lldb.frame.FindVariable('argv'))\n + argv.name - return the name of the value that this object contains\n + argv.type - return the lldb.SBType for this value + argv.type_name - return the name of the type + argv.size - return the byte size of this value + argv.is_in_scope - return true if this value is currently in scope + argv.is_pointer - return true if this value is a pointer + argv.format - return the current format for this value + argv.value - return the value's value as a string + argv.summary - return a summary of this value's value + argv.description - return the runtime description for this value + argv.location - return a string that represents the values location (address, register, etc) + argv.target - return the lldb.SBTarget for this value + argv.process - return the lldb.SBProcess for this value + argv.thread - return the lldb.SBThread for this value + argv.frame - return the lldb.SBFrame for this value + argv.num_children - return the number of children this value has + argv.children - return a list of sbvalue objects that represents all of the children of this value + ''' + def __init__(self, sbvalue): + self.sbvalue = sbvalue + + def __nonzero__(self): + return self.sbvalue.__nonzero__() + + def __repr__(self): + return self.sbvalue.__repr__() + + def __str__(self): + return self.sbvalue.__str__() + + def __getitem__(self, key): + if type(key) is int: + return value(self.sbvalue.GetChildAtIndex(key, lldb.eNoDynamicValues, True)) + raise TypeError + + def __getattr__(self, name): + if name == 'name': + return self.sbvalue.GetName() + if name == 'type': + return self.sbvalue.GetType() + if name == 'type_name': + return self.sbvalue.GetTypeName() + if name == 'size': + return self.sbvalue.GetByteSize() + if name == 'is_in_scope': + return self.sbvalue.IsInScope() + if name == 'is_pointer': + return self.sbvalue.TypeIsPointerType() + if name == 'format': + return self.sbvalue.GetFormat () + if name == 'value': + return self.sbvalue.GetValue () + if name == 'summary': + return self.sbvalue.GetSummary () + if name == 'description': + return self.sbvalue.GetObjectDescription () + if name == 'location': + return self.sbvalue.GetLocation () + if name == 'target': + return self.sbvalue.GetTarget() + if name == 'process': + return self.sbvalue.GetProcess() + if name == 'thread': + return self.sbvalue.GetThread() + if name == 'frame': + return self.sbvalue.GetFrame() + if name == 'num_children': + return self.sbvalue.GetNumChildren() + if name == 'children': + # Returns an array of sbvalue objects, one for each child of + # the value for the lldb.SBValue + children = [] + for i in range (self.sbvalue.GetNumChildren()): + children.append(value(self.sbvalue.GetChildAtIndex(i, lldb.eNoDynamicValues, True))) + return children + raise AttributeError + +class variable(object): + '''A class that treats a lldb.SBValue and allows it to be used just as + a variable would be in code. So if you have a Point structure variable + in your code, you would be able to do: "pt.x + pt.y"''' + def __init__(self, sbvalue): + self.sbvalue = sbvalue + + def __nonzero__(self): + return self.sbvalue.__nonzero__() + + def __repr__(self): + return self.sbvalue.__repr__() + + def __str__(self): + return self.sbvalue.__str__() + + def __getitem__(self, key): + # Allow array access if this value has children... + if type(key) is int: + return variable(self.sbvalue.GetValueForExpressionPath("[%i]" % key)) + raise TypeError + + def __getattr__(self, name): + child_sbvalue = self.sbvalue.GetChildMemberWithName (name) + if child_sbvalue: + return variable(child_sbvalue) + raise AttributeError + + def __add__(self, other): + return int(self) + int(other) + + def __sub__(self, other): + return int(self) - int(other) + + def __mul__(self, other): + return int(self) * int(other) + + def __floordiv__(self, other): + return int(self) // int(other) + + def __mod__(self, other): + return int(self) % int(other) + + def __divmod__(self, other): + return int(self) % int(other) + + def __pow__(self, other): + return int(self) ** int(other) + + def __lshift__(self, other): + return int(self) << int(other) + + def __rshift__(self, other): + return int(self) >> int(other) + + def __and__(self, other): + return int(self) & int(other) + + def __xor__(self, other): + return int(self) ^ int(other) + + def __or__(self, other): + return int(self) | int(other) + + def __div__(self, other): + return int(self) / int(other) + + def __truediv__(self, other): + return int(self) / int(other) + + def __iadd__(self, other): + result = self.__add__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __isub__(self, other): + result = self.__sub__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __imul__(self, other): + result = self.__mul__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __idiv__(self, other): + result = self.__div__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __itruediv__(self, other): + result = self.__truediv__(other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ifloordiv__(self, other): + result = self.__floordiv__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __imod__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ipow__(self, other): + result = self.__pow__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ipow__(self, other, modulo): + result = self.__pow__(self, other, modulo) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ilshift__(self, other): + result = self.__lshift__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __irshift__(self, other): + result = self.__rshift__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __iand__(self, other): + result = self.__and__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ixor__(self, other): + result = self.__xor__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __ior__(self, other): + result = self.__ior__(self, other) + self.sbvalue.SetValueFromCString (str(result)) + return result + + def __neg__(self): + return -int(self) + + def __pos__(self): + return +int(self) + + def __abs__(self): + return abs(int(self)) + + def __invert__(self): + return ~int(self) + + def __complex__(self): + return complex (int(self)) + + def __int__(self): + return self.sbvalue.GetValueAsSigned() + + def __long__(self): + return self.sbvalue.GetValueAsSigned() + + def __float__(self): + return float (self.sbvalue.GetValueAsSigned()) + + def __oct__(self): + return '0%o' % self.sbvalue.GetValueAsSigned() + + def __hex__(self): + return '0x%x' % self.sbvalue.GetValueAsSigned() +
\ No newline at end of file diff --git a/examples/python/scripted_step.py b/examples/python/scripted_step.py new file mode 100644 index 0000000000000..8affb9e83220d --- /dev/null +++ b/examples/python/scripted_step.py @@ -0,0 +1,186 @@ +############################################################################# +# This script contains two trivial examples of simple "scripted step" classes. +# To fully understand how the lldb "Thread Plan" architecture works, read the +# comments at the beginning of ThreadPlan.h in the lldb sources. The python +# interface is a reduced version of the full internal mechanism, but captures +# most of the power with a much simpler interface. +# +# But I'll attempt a brief summary here. +# Stepping in lldb is done independently for each thread. Moreover, the stepping +# operations are stackable. So for instance if you did a "step over", and in +# the course of stepping over you hit a breakpoint, stopped and stepped again, +# the first "step-over" would be suspended, and the new step operation would +# be enqueued. Then if that step over caused the program to hit another breakpoint, +# lldb would again suspend the second step and return control to the user, so +# now there are two pending step overs. Etc. with all the other stepping +# operations. Then if you hit "continue" the bottom-most step-over would complete, +# and another continue would complete the first "step-over". +# +# lldb represents this system with a stack of "Thread Plans". Each time a new +# stepping operation is requested, a new plan is pushed on the stack. When the +# operation completes, it is pushed off the stack. +# +# The bottom-most plan in the stack is the immediate controller of stepping, +# most importantly, when the process resumes, the bottom most plan will get +# asked whether to set the program running freely, or to instruction-single-step +# the current thread. In the scripted interface, you indicate this by returning +# False or True respectively from the should_step method. +# +# Each time the process stops the thread plan stack for each thread that stopped +# "for a reason", Ii.e. a single-step completed on that thread, or a breakpoint +# was hit), is queried to determine how to proceed, starting from the most +# recently pushed plan, in two stages: +# +# 1) Each plan is asked if it "explains" the stop. The first plan to claim the +# stop wins. In scripted Thread Plans, this is done by returning True from +# the "explains_stop method. This is how, for instance, control is returned +# to the User when the "step-over" plan hits a breakpoint. The step-over +# plan doesn't explain the breakpoint stop, so it returns false, and the +# breakpoint hit is propagated up the stack to the "base" thread plan, which +# is the one that handles random breakpoint hits. +# +# 2) Then the plan that won the first round is asked if the process should stop. +# This is done in the "should_stop" method. The scripted plans actually do +# three jobs in should_stop: +# a) They determine if they have completed their job or not. If they have +# they indicate that by calling SetPlanComplete on their thread plan. +# b) They decide whether they want to return control to the user or not. +# They do this by returning True or False respectively. +# c) If they are not done, they set up whatever machinery they will use +# the next time the thread continues. +# +# Note that deciding to return control to the user, and deciding your plan +# is done, are orthgonal operations. You could set up the next phase of +# stepping, and then return True from should_stop, and when the user next +# "continued" the process your plan would resume control. Of course, the +# user might also "step-over" or some other operation that would push a +# different plan, which would take control till it was done. +# +# One other detail you should be aware of, if the plan below you on the +# stack was done, then it will be popped and the next plan will take control +# and its "should_stop" will be called. +# +# Note also, there should be another method called when your plan is popped, +# to allow you to do whatever cleanup is required. I haven't gotten to that +# yet. For now you should do that at the same time you mark your plan complete. +# +# Both examples show stepping through an address range for 20 bytes from the +# current PC. The first one does it by single stepping and checking a condition. +# It doesn't, however handle the case where you step into another frame while +# still in the current range in the starting frame. +# +# That is better handled in the second example by using the built-in StepOverRange +# thread plan. +# +# To use these stepping modes, you would do: +# +# (lldb) command script import scripted_step.py +# (lldb) thread step-scripted -C scripted_step.SimpleStep +# or +# +# (lldb) thread step-scripted -C scripted_step.StepWithPlan + +import lldb + +class SimpleStep: + def __init__ (self, thread_plan, dict): + self.thread_plan = thread_plan + self.start_address = thread_plan.GetThread().GetFrameAtIndex(0).GetPC() + + def explains_stop (self, event): + # We are stepping, so if we stop for any other reason, it isn't + # because of us. + if self.thread_plan.GetThread().GetStopReason()== lldb.eStopReasonTrace: + return True + else: + return False + + def should_stop (self, event): + cur_pc = self.thread_plan.GetThread().GetFrameAtIndex(0).GetPC() + + if cur_pc < self.start_address or cur_pc >= self.start_address + 20: + self.thread_plan.SetPlanComplete(True) + return True + else: + return False + + def should_step (self): + return True + +class StepWithPlan: + def __init__ (self, thread_plan, dict): + self.thread_plan = thread_plan + self.start_address = thread_plan.GetThread().GetFrameAtIndex(0).GetPCAddress() + self.step_thread_plan =thread_plan.QueueThreadPlanForStepOverRange(self.start_address, 20); + + def explains_stop (self, event): + # Since all I'm doing is running a plan, I will only ever get askedthis + # if myplan doesn't explain the stop, and in that caseI don'teither. + return False + + def should_stop (self, event): + if self.step_thread_plan.IsPlanComplete(): + self.thread_plan.SetPlanComplete(True) + return True + else: + return False + + def should_step (self): + return False + +# Here's another example which does "step over" through the current function, +# and when it stops at each line, it checks some condition (in this example the +# value of a variable) and stops if that condition is true. + +class StepCheckingCondition: + def __init__ (self, thread_plan, dict): + self.thread_plan = thread_plan + self.start_frame = thread_plan.GetThread().GetFrameAtIndex(0) + self.queue_next_plan() + + def queue_next_plan (self): + cur_frame = self.thread_plan.GetThread().GetFrameAtIndex(0) + cur_line_entry = cur_frame.GetLineEntry() + start_address = cur_line_entry.GetStartAddress() + end_address = cur_line_entry.GetEndAddress() + line_range = end_address.GetFileAddress() - start_address.GetFileAddress() + self.step_thread_plan = self.thread_plan.QueueThreadPlanForStepOverRange(start_address, line_range) + + def explains_stop (self, event): + # We are stepping, so if we stop for any other reason, it isn't + # because of us. + return False + + def should_stop (self, event): + if not self.step_thread_plan.IsPlanComplete(): + return False + + frame = self.thread_plan.GetThread().GetFrameAtIndex(0) + if not self.start_frame.IsEqual(frame): + self.thread_plan.SetPlanComplete(True) + return True + + # This part checks the condition. In this case we are expecting + # some integer variable called "a", and will stop when it is 20. + a_var = frame.FindVariable("a") + + if not a_var.IsValid(): + print "A was not valid." + return True + + error = lldb.SBError() + a_value = a_var.GetValueAsSigned (error) + if not error.Success(): + print "A value was not good." + return True + + if a_value == 20: + self.thread_plan.SetPlanComplete(True) + return True + else: + self.queue_next_plan() + return False + + def should_step (self): + return True + diff --git a/examples/python/sources.py b/examples/python/sources.py new file mode 100644 index 0000000000000..0eb5858805bea --- /dev/null +++ b/examples/python/sources.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +import lldb +import shlex + +def dump_module_sources(module, result): + if module: + print >> result, "Module: %s" % (module.file) + for compile_unit in module.compile_units: + if compile_unit.file: + print >> result, " %s" % (compile_unit.file) + +def info_sources(debugger, command, result, dict): + description='''This command will dump all compile units in any modules that are listed as arguments, or for all modules if no arguments are supplied.''' + module_names = shlex.split(command) + target = debugger.GetSelectedTarget() + if module_names: + for module_name in module_names: + dump_module_sources(target.module[module_name], result) + else: + for module in target.modules: + dump_module_sources(module, result) + + +def __lldb_init_module (debugger, dict): + # Add any commands contained in this module to LLDB + debugger.HandleCommand('command script add -f sources.info_sources info_sources') + print 'The "info_sources" command has been installed, type "help info_sources" or "info_sources --help" for detailed help.' diff --git a/examples/python/stacks.py b/examples/python/stacks.py new file mode 100755 index 0000000000000..06907e159d7fc --- /dev/null +++ b/examples/python/stacks.py @@ -0,0 +1,59 @@ +#!/usr/bin/python + +import lldb +import commands +import optparse +import shlex + +def stack_frames(debugger, command, result, dict): + command_args = shlex.split(command) + usage = "usage: %prog [options] <PATH> [PATH ...]" + description='''This command will enumerate all stack frames, print the stack size for each, and print an aggregation of which functions have the largest stack frame sizes at the end.''' + parser = optparse.OptionParser(description=description, prog='ls',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + target = debugger.GetSelectedTarget() + process = target.GetProcess() + + frame_info = {} + for thread in process: + last_frame = None + print "thread %u" % (thread.id) + for frame in thread.frames: + if last_frame: + frame_size = 0 + if frame.idx == 1: + if frame.fp == last_frame.fp: + # No frame one the first frame (might be right at the entry point) + first_frame_size = 0 + frame_size = frame.fp - frame.sp + else: + # First frame that has a valid size + first_frame_size = last_frame.fp - last_frame.sp + print "<%#7x> %s" % (first_frame_size, last_frame) + if first_frame_size: + name = last_frame.name + if name not in frame_info: + frame_info[name] = first_frame_size + else: + frame_info[name] += first_frame_size + else: + # Second or higher frame + frame_size = frame.fp - last_frame.fp + print "<%#7x> %s" % (frame_size, frame) + if frame_size > 0: + name = frame.name + if name not in frame_info: + frame_info[name] = frame_size + else: + frame_info[name] += frame_size + last_frame = frame + print frame_info + + +lldb.debugger.HandleCommand("command script add -f stacks.stack_frames stack_frames") +print "A new command called 'stack_frames' was added, type 'stack_frames --help' for more information."
\ No newline at end of file diff --git a/examples/python/symbolication.py b/examples/python/symbolication.py new file mode 100755 index 0000000000000..2f2a274dbc41d --- /dev/null +++ b/examples/python/symbolication.py @@ -0,0 +1,640 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# To use this in the embedded python interpreter using "lldb": +# +# cd /path/containing/crashlog.py +# lldb +# (lldb) script import crashlog +# "crashlog" command installed, type "crashlog --help" for detailed help +# (lldb) crashlog ~/Library/Logs/DiagnosticReports/a.crash +# +# The benefit of running the crashlog command inside lldb in the +# embedded python interpreter is when the command completes, there +# will be a target with all of the files loaded at the locations +# described in the crash log. Only the files that have stack frames +# in the backtrace will be loaded unless the "--load-all" option +# has been specified. This allows users to explore the program in the +# state it was in right at crash time. +# +# On MacOSX csh, tcsh: +# ( setenv PYTHONPATH /path/to/LLDB.framework/Resources/Python ; ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash ) +# +# On MacOSX sh, bash: +# PYTHONPATH=/path/to/LLDB.framework/Resources/Python ./crashlog.py ~/Library/Logs/DiagnosticReports/a.crash +#---------------------------------------------------------------------- + +import lldb +import commands +import optparse +import os +import plistlib +import re +import shlex +import sys +import time +import uuid + +class Address: + """Class that represents an address that will be symbolicated""" + def __init__(self, target, load_addr): + self.target = target + self.load_addr = load_addr # The load address that this object represents + self.so_addr = None # the resolved lldb.SBAddress (if any), named so_addr for section/offset address + self.sym_ctx = None # The cached symbol context for this address + self.description = None # Any original textual description of this address to be used as a backup in case symbolication fails + self.symbolication = None # The cached symbolicated string that describes this address + self.inlined = False + def __str__(self): + s = "%#16.16x" % (self.load_addr) + if self.symbolication: + s += " %s" % (self.symbolication) + elif self.description: + s += " %s" % (self.description) + elif self.so_addr: + s += " %s" % (self.so_addr) + return s + + def resolve_addr(self): + if self.so_addr == None: + self.so_addr = self.target.ResolveLoadAddress (self.load_addr) + return self.so_addr + + def is_inlined(self): + return self.inlined + + def get_symbol_context(self): + if self.sym_ctx == None: + sb_addr = self.resolve_addr() + if sb_addr: + self.sym_ctx = self.target.ResolveSymbolContextForAddress (sb_addr, lldb.eSymbolContextEverything) + else: + self.sym_ctx = lldb.SBSymbolContext() + return self.sym_ctx + + def get_instructions(self): + sym_ctx = self.get_symbol_context() + if sym_ctx: + function = sym_ctx.GetFunction() + if function: + return function.GetInstructions(self.target) + return sym_ctx.GetSymbol().GetInstructions(self.target) + return None + + def symbolicate(self, verbose = False): + if self.symbolication == None: + self.symbolication = '' + self.inlined = False + sym_ctx = self.get_symbol_context() + if sym_ctx: + module = sym_ctx.GetModule() + if module: + # Print full source file path in verbose mode + if verbose: + self.symbolication += str(module.GetFileSpec()) + '`' + else: + self.symbolication += module.GetFileSpec().GetFilename() + '`' + function_start_load_addr = -1 + function = sym_ctx.GetFunction() + block = sym_ctx.GetBlock() + line_entry = sym_ctx.GetLineEntry() + symbol = sym_ctx.GetSymbol() + inlined_block = block.GetContainingInlinedBlock(); + if function: + self.symbolication += function.GetName() + + if inlined_block: + self.inlined = True + self.symbolication += ' [inlined] ' + inlined_block.GetInlinedName(); + block_range_idx = inlined_block.GetRangeIndexForBlockAddress (self.so_addr) + if block_range_idx < lldb.UINT32_MAX: + block_range_start_addr = inlined_block.GetRangeStartAddress (block_range_idx) + function_start_load_addr = block_range_start_addr.GetLoadAddress (self.target) + if function_start_load_addr == -1: + function_start_load_addr = function.GetStartAddress().GetLoadAddress (self.target) + elif symbol: + self.symbolication += symbol.GetName() + function_start_load_addr = symbol.GetStartAddress().GetLoadAddress (self.target) + else: + self.symbolication = '' + return False + + # Dump the offset from the current function or symbol if it is non zero + function_offset = self.load_addr - function_start_load_addr + if function_offset > 0: + self.symbolication += " + %u" % (function_offset) + elif function_offset < 0: + self.symbolication += " %i (invalid negative offset, file a bug) " % function_offset + + # Print out any line information if any is available + if line_entry.GetFileSpec(): + # Print full source file path in verbose mode + if verbose: + self.symbolication += ' at %s' % line_entry.GetFileSpec() + else: + self.symbolication += ' at %s' % line_entry.GetFileSpec().GetFilename() + self.symbolication += ':%u' % line_entry.GetLine () + column = line_entry.GetColumn() + if column > 0: + self.symbolication += ':%u' % column + return True + return False + +class Section: + """Class that represents an load address range""" + sect_info_regex = re.compile('(?P<name>[^=]+)=(?P<range>.*)') + addr_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*$') + range_regex = re.compile('^\s*(?P<start>0x[0-9A-Fa-f]+)\s*(?P<op>[-+])\s*(?P<end>0x[0-9A-Fa-f]+)\s*$') + + def __init__(self, start_addr = None, end_addr = None, name = None): + self.start_addr = start_addr + self.end_addr = end_addr + self.name = name + + @classmethod + def InitWithSBTargetAndSBSection(cls, target, section): + sect_load_addr = section.GetLoadAddress(target) + if sect_load_addr != lldb.LLDB_INVALID_ADDRESS: + obj = cls(sect_load_addr, sect_load_addr + section.size, section.name) + return obj + else: + return None + + def contains(self, addr): + return self.start_addr <= addr and addr < self.end_addr; + + def set_from_string(self, s): + match = self.sect_info_regex.match (s) + if match: + self.name = match.group('name') + range_str = match.group('range') + addr_match = self.addr_regex.match(range_str) + if addr_match: + self.start_addr = int(addr_match.group('start'), 16) + self.end_addr = None + return True + + range_match = self.range_regex.match(range_str) + if range_match: + self.start_addr = int(range_match.group('start'), 16) + self.end_addr = int(range_match.group('end'), 16) + op = range_match.group('op') + if op == '+': + self.end_addr += self.start_addr + return True + print 'error: invalid section info string "%s"' % s + print 'Valid section info formats are:' + print 'Format Example Description' + print '--------------------- -----------------------------------------------' + print '<name>=<base> __TEXT=0x123000 Section from base address only' + print '<name>=<base>-<end> __TEXT=0x123000-0x124000 Section from base address and end address' + print '<name>=<base>+<size> __TEXT=0x123000+0x1000 Section from base address and size' + return False + + def __str__(self): + if self.name: + if self.end_addr != None: + if self.start_addr != None: + return "%s=[0x%16.16x - 0x%16.16x)" % (self.name, self.start_addr, self.end_addr) + else: + if self.start_addr != None: + return "%s=0x%16.16x" % (self.name, self.start_addr) + return self.name + return "<invalid>" + +class Image: + """A class that represents an executable image and any associated data""" + + def __init__(self, path, uuid = None): + self.path = path + self.resolved_path = None + self.resolved = False + self.unavailable = False + self.uuid = uuid + self.section_infos = list() + self.identifier = None + self.version = None + self.arch = None + self.module = None + self.symfile = None + self.slide = None + + @classmethod + def InitWithSBTargetAndSBModule(cls, target, module): + '''Initialize this Image object with a module from a target.''' + obj = cls(module.file.fullpath, module.uuid) + obj.resolved_path = module.platform_file.fullpath + obj.resolved = True + obj.arch = module.triple + for section in module.sections: + symb_section = Section.InitWithSBTargetAndSBSection(target, section) + if symb_section: + obj.section_infos.append (symb_section) + obj.arch = module.triple + obj.module = module + obj.symfile = None + obj.slide = None + return obj + + def dump(self, prefix): + print "%s%s" % (prefix, self) + + def debug_dump(self): + print 'path = "%s"' % (self.path) + print 'resolved_path = "%s"' % (self.resolved_path) + print 'resolved = %i' % (self.resolved) + print 'unavailable = %i' % (self.unavailable) + print 'uuid = %s' % (self.uuid) + print 'section_infos = %s' % (self.section_infos) + print 'identifier = "%s"' % (self.identifier) + print 'version = %s' % (self.version) + print 'arch = %s' % (self.arch) + print 'module = %s' % (self.module) + print 'symfile = "%s"' % (self.symfile) + print 'slide = %i (0x%x)' % (self.slide, self.slide) + + def __str__(self): + s = '' + if self.uuid: + s += "%s " % (self.get_uuid()) + if self.arch: + s += "%s " % (self.arch) + if self.version: + s += "%s " % (self.version) + resolved_path = self.get_resolved_path() + if resolved_path: + s += "%s " % (resolved_path) + for section_info in self.section_infos: + s += ", %s" % (section_info) + if self.slide != None: + s += ', slide = 0x%16.16x' % self.slide + return s + + def add_section(self, section): + #print "added '%s' to '%s'" % (section, self.path) + self.section_infos.append (section) + + def get_section_containing_load_addr (self, load_addr): + for section_info in self.section_infos: + if section_info.contains(load_addr): + return section_info + return None + + def get_resolved_path(self): + if self.resolved_path: + return self.resolved_path + elif self.path: + return self.path + return None + + def get_resolved_path_basename(self): + path = self.get_resolved_path() + if path: + return os.path.basename(path) + return None + + def symfile_basename(self): + if self.symfile: + return os.path.basename(self.symfile) + return None + + def has_section_load_info(self): + return self.section_infos or self.slide != None + + def load_module(self, target): + if self.unavailable: + return None # We already warned that we couldn't find this module, so don't return an error string + # Load this module into "target" using the section infos to + # set the section load addresses + if self.has_section_load_info(): + if target: + if self.module: + if self.section_infos: + num_sections_loaded = 0 + for section_info in self.section_infos: + if section_info.name: + section = self.module.FindSection (section_info.name) + if section: + error = target.SetSectionLoadAddress (section, section_info.start_addr) + if error.Success(): + num_sections_loaded += 1 + else: + return 'error: %s' % error.GetCString() + else: + return 'error: unable to find the section named "%s"' % section_info.name + else: + return 'error: unable to find "%s" section in "%s"' % (range.name, self.get_resolved_path()) + if num_sections_loaded == 0: + return 'error: no sections were successfully loaded' + else: + err = target.SetModuleLoadAddress(self.module, self.slide) + if err.Fail(): + return err.GetCString() + return None + else: + return 'error: invalid module' + else: + return 'error: invalid target' + else: + return 'error: no section infos' + + def add_module(self, target): + '''Add the Image described in this object to "target" and load the sections if "load" is True.''' + if target: + # Try and find using UUID only first so that paths need not match up + uuid_str = self.get_normalized_uuid_string() + if uuid_str: + self.module = target.AddModule (None, None, uuid_str) + if not self.module: + self.locate_module_and_debug_symbols () + if self.unavailable: + return None + resolved_path = self.get_resolved_path() + self.module = target.AddModule (resolved_path, self.arch, uuid_str, self.symfile) + if not self.module: + return 'error: unable to get module for (%s) "%s"' % (self.arch, self.get_resolved_path()) + if self.has_section_load_info(): + return self.load_module(target) + else: + return None # No sections, the module was added to the target, so success + else: + return 'error: invalid target' + + def locate_module_and_debug_symbols (self): + # By default, just use the paths that were supplied in: + # self.path + # self.resolved_path + # self.module + # self.symfile + # Subclasses can inherit from this class and override this function + self.resolved = True + return True + + def get_uuid(self): + if not self.uuid and self.module: + self.uuid = uuid.UUID(self.module.GetUUIDString()) + return self.uuid + + def get_normalized_uuid_string(self): + if self.uuid: + return str(self.uuid).upper() + return None + + def create_target(self): + '''Create a target using the information in this Image object.''' + if self.unavailable: + return None + + if self.locate_module_and_debug_symbols (): + resolved_path = self.get_resolved_path(); + path_spec = lldb.SBFileSpec (resolved_path) + #result.PutCString ('plist[%s] = %s' % (uuid, self.plist)) + error = lldb.SBError() + target = lldb.debugger.CreateTarget (resolved_path, self.arch, None, False, error); + if target: + self.module = target.FindModule(path_spec) + if self.has_section_load_info(): + err = self.load_module(target) + if err: + print 'ERROR: ', err + return target + else: + print 'error: unable to create a valid target for (%s) "%s"' % (self.arch, self.path) + else: + print 'error: unable to locate main executable (%s) "%s"' % (self.arch, self.path) + return None + +class Symbolicator: + + def __init__(self): + """A class the represents the information needed to symbolicate addresses in a program""" + self.target = None + self.images = list() # a list of images to be used when symbolicating + self.addr_mask = 0xffffffffffffffff + + @classmethod + def InitWithSBTarget(cls, target): + obj = cls() + obj.target = target + obj.images = list(); + triple = target.triple + if triple: + arch = triple.split('-')[0] + if "arm" in arch: + obj.addr_mask = 0xfffffffffffffffe + + for module in target.modules: + image = Image.InitWithSBTargetAndSBModule(target, module) + obj.images.append(image) + return obj + + def __str__(self): + s = "Symbolicator:\n" + if self.target: + s += "Target = '%s'\n" % (self.target) + s += "Target modules:\n" + for m in self.target.modules: + s += str(m) + "\n" + s += "Images:\n" + for image in self.images: + s += ' %s\n' % (image) + return s + + def find_images_with_identifier(self, identifier): + images = list() + for image in self.images: + if image.identifier == identifier: + images.append(image) + if len(images) == 0: + regex_text = '^.*\.%s$' % (identifier) + regex = re.compile(regex_text) + for image in self.images: + if regex.match(image.identifier): + images.append(image) + return images + + def find_image_containing_load_addr(self, load_addr): + for image in self.images: + if image.get_section_containing_load_addr (load_addr): + return image + return None + + def create_target(self): + if self.target: + return self.target + + if self.images: + for image in self.images: + self.target = image.create_target () + if self.target: + if self.target.GetAddressByteSize() == 4: + triple = self.target.triple + if triple: + arch = triple.split('-')[0] + if "arm" in arch: + self.addr_mask = 0xfffffffffffffffe + return self.target + return None + + + def symbolicate(self, load_addr, verbose = False): + if not self.target: + self.create_target() + if self.target: + live_process = False + process = self.target.process + if process: + state = process.state + if state > lldb.eStateUnloaded and state < lldb.eStateDetached: + live_process = True + # If we don't have a live process, we can attempt to find the image + # that a load address belongs to and lazily load its module in the + # target, but we shouldn't do any of this if we have a live process + if not live_process: + image = self.find_image_containing_load_addr (load_addr) + if image: + image.add_module (self.target) + symbolicated_address = Address(self.target, load_addr) + if symbolicated_address.symbolicate (verbose): + if symbolicated_address.so_addr: + symbolicated_addresses = list() + symbolicated_addresses.append(symbolicated_address) + # See if we were able to reconstruct anything? + while 1: + inlined_parent_so_addr = lldb.SBAddress() + inlined_parent_sym_ctx = symbolicated_address.sym_ctx.GetParentOfInlinedScope (symbolicated_address.so_addr, inlined_parent_so_addr) + if not inlined_parent_sym_ctx: + break + if not inlined_parent_so_addr: + break + + symbolicated_address = Address(self.target, inlined_parent_so_addr.GetLoadAddress(self.target)) + symbolicated_address.sym_ctx = inlined_parent_sym_ctx + symbolicated_address.so_addr = inlined_parent_so_addr + symbolicated_address.symbolicate (verbose) + + # push the new frame onto the new frame stack + symbolicated_addresses.append (symbolicated_address) + + if symbolicated_addresses: + return symbolicated_addresses + else: + print 'error: no target in Symbolicator' + return None + + +def disassemble_instructions (target, instructions, pc, insts_before_pc, insts_after_pc, non_zeroeth_frame): + lines = list() + pc_index = -1 + comment_column = 50 + for inst_idx, inst in enumerate(instructions): + inst_pc = inst.GetAddress().GetLoadAddress(target); + if pc == inst_pc: + pc_index = inst_idx + mnemonic = inst.GetMnemonic (target) + operands = inst.GetOperands (target) + comment = inst.GetComment (target) + #data = inst.GetData (target) + lines.append ("%#16.16x: %8s %s" % (inst_pc, mnemonic, operands)) + if comment: + line_len = len(lines[-1]) + if line_len < comment_column: + lines[-1] += ' ' * (comment_column - line_len) + lines[-1] += "; %s" % comment + + if pc_index >= 0: + # If we are disassembling the non-zeroeth frame, we need to backup the PC by 1 + if non_zeroeth_frame and pc_index > 0: + pc_index = pc_index - 1 + if insts_before_pc == -1: + start_idx = 0 + else: + start_idx = pc_index - insts_before_pc + if start_idx < 0: + start_idx = 0 + if insts_before_pc == -1: + end_idx = inst_idx + else: + end_idx = pc_index + insts_after_pc + if end_idx > inst_idx: + end_idx = inst_idx + for i in range(start_idx, end_idx+1): + if i == pc_index: + print ' -> ', lines[i] + else: + print ' ', lines[i] + +def print_module_section_data (section): + print section + section_data = section.GetSectionData() + if section_data: + ostream = lldb.SBStream() + section_data.GetDescription (ostream, section.GetFileAddress()) + print ostream.GetData() + +def print_module_section (section, depth): + print section + if depth > 0: + num_sub_sections = section.GetNumSubSections() + for sect_idx in range(num_sub_sections): + print_module_section (section.GetSubSectionAtIndex(sect_idx), depth - 1) + +def print_module_sections (module, depth): + for sect in module.section_iter(): + print_module_section (sect, depth) + +def print_module_symbols (module): + for sym in module: + print sym + +def Symbolicate(command_args): + + usage = "usage: %prog [options] <addr1> [addr2 ...]" + description='''Symbolicate one or more addresses using LLDB's python scripting API..''' + parser = optparse.OptionParser(description=description, prog='crashlog.py',usage=usage) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) + parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + parser.add_option('-f', '--file', type='string', metavar='file', dest='file', help='Specify a file to use when symbolicating') + parser.add_option('-a', '--arch', type='string', metavar='arch', dest='arch', help='Specify a architecture to use when symbolicating') + parser.add_option('-s', '--slide', type='int', metavar='slide', dest='slide', help='Specify the slide to use on the file specified with the --file option', default=None) + parser.add_option('--section', type='string', action='append', dest='section_strings', help='specify <sect-name>=<start-addr> or <sect-name>=<start-addr>-<end-addr>') + try: + (options, args) = parser.parse_args(command_args) + except: + return + symbolicator = Symbolicator() + images = list(); + if options.file: + image = Image(options.file); + image.arch = options.arch + # Add any sections that were specified with one or more --section options + if options.section_strings: + for section_str in options.section_strings: + section = Section() + if section.set_from_string (section_str): + image.add_section (section) + else: + sys.exit(1) + if options.slide != None: + image.slide = options.slide + symbolicator.images.append(image) + + target = symbolicator.create_target() + if options.verbose: + print symbolicator + if target: + for addr_str in args: + addr = int(addr_str, 0) + symbolicated_addrs = symbolicator.symbolicate(addr, options.verbose) + for symbolicated_addr in symbolicated_addrs: + print symbolicated_addr + print + else: + print 'error: no target for %s' % (symbolicator) + +if __name__ == '__main__': + # Create a new debugger instance + lldb.debugger = lldb.SBDebugger.Create() + Symbolicate (sys.argv[1:]) diff --git a/examples/python/types.py b/examples/python/types.py new file mode 100755 index 0000000000000..60ea7514c13a7 --- /dev/null +++ b/examples/python/types.py @@ -0,0 +1,265 @@ +#!/usr/bin/python + +#---------------------------------------------------------------------- +# Be sure to add the python path that points to the LLDB shared library. +# +# # To use this in the embedded python interpreter using "lldb" just +# import it with the full path using the "command script import" +# command +# (lldb) command script import /path/to/cmdtemplate.py +#---------------------------------------------------------------------- + +import commands +import platform +import os +import re +import signal +import sys + +try: + # Just try for LLDB in case PYTHONPATH is already correctly setup + import lldb +except ImportError: + lldb_python_dirs = list() + # lldb is not in the PYTHONPATH, try some defaults for the current platform + platform_system = platform.system() + if platform_system == 'Darwin': + # On Darwin, try the currently selected Xcode directory + xcode_dir = commands.getoutput("xcode-select --print-path") + if xcode_dir: + lldb_python_dirs.append(os.path.realpath(xcode_dir + '/../SharedFrameworks/LLDB.framework/Resources/Python')) + lldb_python_dirs.append(xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + lldb_python_dirs.append('/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') + success = False + for lldb_python_dir in lldb_python_dirs: + if os.path.exists(lldb_python_dir): + if not (sys.path.__contains__(lldb_python_dir)): + sys.path.append(lldb_python_dir) + try: + import lldb + except ImportError: + pass + else: + print 'imported lldb from: "%s"' % (lldb_python_dir) + success = True + break + if not success: + print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" + sys.exit(1) + +import commands +import optparse +import shlex +import time + +def regex_option_callback(option, opt_str, value, parser): + if opt_str == "--std": + value = '^std::' + regex = re.compile(value) + parser.values.skip_type_regexes.append (regex) + +def create_types_options(for_lldb_command): + if for_lldb_command: + usage = "usage: %prog [options]" + description='''This command will help check for padding in between +base classes and members in structs and classes. It will summarize the types +and how much padding was found. If no types are specified with the --types TYPENAME +option, all structure and class types will be verified. If no modules are +specified with the --module option, only the target's main executable will be +searched. +''' + else: + usage = "usage: %prog [options] EXEPATH [EXEPATH ...]" + description='''This command will help check for padding in between +base classes and members in structures and classes. It will summarize the types +and how much padding was found. One or more paths to executable files must be +specified and targets will be created with these modules. If no types are +specified with the --types TYPENAME option, all structure and class types will +be verified in all specified modules. +''' + parser = optparse.OptionParser(description=description, prog='framestats',usage=usage) + if not for_lldb_command: + parser.add_option('-a', '--arch', type='string', dest='arch', help='The architecture to use when creating the debug target.', default=None) + parser.add_option('-p', '--platform', type='string', metavar='platform', dest='platform', help='Specify the platform to use when creating the debug target. Valid values include "localhost", "darwin-kernel", "ios-simulator", "remote-freebsd", "remote-macosx", "remote-ios", "remote-linux".') + parser.add_option('-m', '--module', action='append', type='string', metavar='MODULE', dest='modules', help='Specify one or more modules which will be used to verify the types.', default=[]) + parser.add_option('-d', '--debug', action='store_true', dest='debug', help='Pause 10 seconds to wait for a debugger to attach.', default=False) + parser.add_option('-t', '--type', action='append', type='string', metavar='TYPENAME', dest='typenames', help='Specify one or more type names which should be verified. If no type names are specified, all class and struct types will be verified.', default=[]) + parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='Enable verbose logging and information.', default=False) + parser.add_option('-s', '--skip-type-regex', action="callback", callback=regex_option_callback, type='string', metavar='REGEX', dest='skip_type_regexes', help='Regular expressions that, if they match the current member typename, will cause the type to no be recursively displayed.', default=[]) + parser.add_option('--std', action="callback", callback=regex_option_callback, metavar='REGEX', dest='skip_type_regexes', help="Don't' recurse into types in the std namespace.", default=[]) + return parser + +def verify_type (target, options, type): + print type + typename = type.GetName() + # print 'type: %s' % (typename) + (end_offset, padding) = verify_type_recursive (target, options, type, None, 0, 0, 0) + byte_size = type.GetByteSize() + # if end_offset < byte_size: + # last_member_padding = byte_size - end_offset + # print '%+4u <%u> padding' % (end_offset, last_member_padding) + # padding += last_member_padding + print 'Total byte size: %u' % (byte_size) + print 'Total pad bytes: %u' % (padding) + if padding > 0: + print 'Padding percentage: %2.2f %%' % ((float(padding) / float(byte_size)) * 100.0) + print + +def verify_type_recursive (target, options, type, member_name, depth, base_offset, padding): + prev_end_offset = base_offset + typename = type.GetName() + byte_size = type.GetByteSize() + if member_name and member_name != typename: + print '%+4u <%3u> %s%s %s;' % (base_offset, byte_size, ' ' * depth, typename, member_name) + else: + print '%+4u {%3u} %s%s' % (base_offset, byte_size, ' ' * depth, typename) + + for type_regex in options.skip_type_regexes: + match = type_regex.match (typename) + if match: + return (base_offset + byte_size, padding) + + members = type.members + if members: + for member_idx, member in enumerate(members): + member_type = member.GetType() + member_canonical_type = member_type.GetCanonicalType() + member_type_class = member_canonical_type.GetTypeClass() + member_name = member.GetName() + member_offset = member.GetOffsetInBytes() + member_total_offset = member_offset + base_offset + member_byte_size = member_type.GetByteSize() + member_is_class_or_struct = False + if member_type_class == lldb.eTypeClassStruct or member_type_class == lldb.eTypeClassClass: + member_is_class_or_struct = True + if member_idx == 0 and member_offset == target.GetAddressByteSize() and type.IsPolymorphicClass(): + ptr_size = target.GetAddressByteSize() + print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1)) + prev_end_offset = ptr_size + else: + if prev_end_offset < member_total_offset: + member_padding = member_total_offset - prev_end_offset + padding = padding + member_padding + print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, member_padding, ' ' * (depth + 1)) + + if member_is_class_or_struct: + (prev_end_offset, padding) = verify_type_recursive (target, options, member_canonical_type, member_name, depth + 1, member_total_offset, padding) + else: + prev_end_offset = member_total_offset + member_byte_size + member_typename = member_type.GetName() + if member.IsBitfield(): + print '%+4u <%3u> %s%s:%u %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member.GetBitfieldSizeInBits(), member_name) + else: + print '%+4u <%3u> %s%s %s;' % (member_total_offset, member_byte_size, ' ' * (depth + 1), member_typename, member_name) + + if prev_end_offset < byte_size: + last_member_padding = byte_size - prev_end_offset + print '%+4u <%3u> %s<PADDING>' % (prev_end_offset, last_member_padding, ' ' * (depth + 1)) + padding += last_member_padding + else: + if type.IsPolymorphicClass(): + ptr_size = target.GetAddressByteSize() + print '%+4u <%3u> %s__vtbl_ptr_type * _vptr;' % (prev_end_offset, ptr_size, ' ' * (depth + 1)) + prev_end_offset = ptr_size + prev_end_offset = base_offset + byte_size + + return (prev_end_offset, padding) + +def check_padding_command (debugger, command, result, dict): + # Use the Shell Lexer to properly parse up command options just like a + # shell would + command_args = shlex.split(command) + parser = create_types_options(True) + try: + (options, args) = parser.parse_args(command_args) + except: + # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit + # (courtesy of OptParse dealing with argument errors by throwing SystemExit) + result.SetStatus (lldb.eReturnStatusFailed) + return "option parsing failed" # returning a string is the same as returning an error whose description is the string + verify_types(debugger.GetSelectedTarget(), options) + +@lldb.command("parse_all_struct_class_types") +def parse_all_struct_class_types (debugger, command, result, dict): + command_args = shlex.split(command) + for f in command_args: + error = lldb.SBError() + target = debugger.CreateTarget (f, None, None, False, error) + module = target.GetModuleAtIndex(0) + print "Parsing all types in '%s'" % (module) + types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) + for t in types: + print t + print "" + + +def verify_types (target, options): + + if not target: + print 'error: invalid target' + return + + modules = list() + if len(options.modules) == 0: + # Append just the main executable if nothing was specified + module = target.modules[0] + if module: + modules.append(module) + else: + for module_name in options.modules: + module = lldb.target.module[module_name] + if module: + modules.append(module) + + if modules: + for module in modules: + print 'module: %s' % (module.file) + if options.typenames: + for typename in options.typenames: + types = module.FindTypes(typename) + if types.GetSize(): + print 'Found %u types matching "%s" in "%s"' % (len(types), typename, module.file) + for type in types: + verify_type (target, options, type) + else: + print 'error: no type matches "%s" in "%s"' % (typename, module.file) + else: + types = module.GetTypes(lldb.eTypeClassClass | lldb.eTypeClassStruct) + print 'Found %u types in "%s"' % (len(types), module.file) + for type in types: + verify_type (target, options, type) + else: + print 'error: no modules' + +if __name__ == '__main__': + debugger = lldb.SBDebugger.Create() + parser = create_types_options(False) + + # try: + (options, args) = parser.parse_args(sys.argv[1:]) + # except: + # print "error: option parsing failed" + # sys.exit(1) + + if options.debug: + print "Waiting for debugger to attach to process %d" % os.getpid() + os.kill(os.getpid(), signal.SIGSTOP) + + for path in args: + # in a command - the lldb.* convenience variables are not to be used + # and their values (if any) are undefined + # this is the best practice to access those objects from within a command + error = lldb.SBError() + target = debugger.CreateTarget (path, + options.arch, + options.platform, + True, + error) + if error.Fail(): + print error.GetCString() + continue + verify_types (target, options) + +elif getattr(lldb, 'debugger', None): + lldb.debugger.HandleCommand('command script add -f types.check_padding_command check_padding') + print '"check_padding" command installed, use the "--help" option for detailed help'
\ No newline at end of file diff --git a/examples/python/x86_64_linux_target_definition.py b/examples/python/x86_64_linux_target_definition.py new file mode 100644 index 0000000000000..06cbe4c829635 --- /dev/null +++ b/examples/python/x86_64_linux_target_definition.py @@ -0,0 +1,353 @@ +#!/usr/bin/python +#===-- x86_64_linux_target_definition.py -----------------------------*- C++ -*-===// +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the GDB server +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the darwin version of +# GDB and allows you to connect to servers that use this register set. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_linux_target_definition.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB server. +#---------------------------------------------------------------------- +from lldb import * + +# Compiler and DWARF register numbers +name_to_gcc_dwarf_regnum = { + 'rax' : 0 , + 'rdx' : 1 , + 'rcx' : 2 , + 'rbx' : 3 , + 'rsi' : 4 , + 'rdi' : 5 , + 'rbp' : 6 , + 'rsp' : 7 , + 'r8' : 8 , + 'r9' : 9 , + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'xmm0' : 17, + 'xmm1' : 18, + 'xmm2' : 19, + 'xmm3' : 20, + 'xmm4' : 21, + 'xmm5' : 22, + 'xmm6' : 23, + 'xmm7' : 24, + 'xmm8' : 25, + 'xmm9' : 26, + 'xmm10' : 27, + 'xmm11' : 28, + 'xmm12' : 29, + 'xmm13' : 30, + 'xmm14' : 31, + 'xmm15' : 32, + 'stmm0' : 33, + 'stmm1' : 34, + 'stmm2' : 35, + 'stmm3' : 36, + 'stmm4' : 37, + 'stmm5' : 38, + 'stmm6' : 39, + 'stmm7' : 30, + 'ymm0' : 41, + 'ymm1' : 42, + 'ymm2' : 43, + 'ymm3' : 44, + 'ymm4' : 45, + 'ymm5' : 46, + 'ymm6' : 47, + 'ymm7' : 48, + 'ymm8' : 49, + 'ymm9' : 40, + 'ymm10' : 41, + 'ymm11' : 42, + 'ymm12' : 43, + 'ymm13' : 44, + 'ymm14' : 45, + 'ymm15' : 46 +}; + +name_to_gdb_regnum = { + 'rax' : 0, + 'rbx' : 1, + 'rcx' : 2, + 'rdx' : 3, + 'rsi' : 4, + 'rdi' : 5, + 'rbp' : 6, + 'rsp' : 7, + 'r8' : 8, + 'r9' : 9, + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'rflags': 17, + 'cs' : 18, + 'ss' : 19, + 'ds' : 20, + 'es' : 21, + 'fs' : 22, + 'gs' : 23, + 'stmm0' : 24, + 'stmm1' : 25, + 'stmm2' : 26, + 'stmm3' : 27, + 'stmm4' : 28, + 'stmm5' : 29, + 'stmm6' : 30, + 'stmm7' : 31, + 'fctrl' : 32, + 'fstat' : 33, + 'ftag' : 34, + 'fiseg' : 35, + 'fioff' : 36, + 'foseg' : 37, + 'fooff' : 38, + 'fop' : 39, + 'xmm0' : 40, + 'xmm1' : 41, + 'xmm2' : 42, + 'xmm3' : 43, + 'xmm4' : 44, + 'xmm5' : 45, + 'xmm6' : 46, + 'xmm7' : 47, + 'xmm8' : 48, + 'xmm9' : 49, + 'xmm10' : 50, + 'xmm11' : 51, + 'xmm12' : 52, + 'xmm13' : 53, + 'xmm14' : 54, + 'xmm15' : 55, + 'mxcsr' : 56, + 'ymm0' : 57, + 'ymm1' : 58, + 'ymm2' : 59, + 'ymm3' : 60, + 'ymm4' : 61, + 'ymm5' : 62, + 'ymm6' : 63, + 'ymm7' : 64, + 'ymm8' : 65, + 'ymm9' : 66, + 'ymm10' : 67, + 'ymm11' : 68, + 'ymm12' : 69, + 'ymm13' : 70, + 'ymm14' : 71, + 'ymm15' : 72 +}; + +name_to_generic_regnum = { + 'rip' : LLDB_REGNUM_GENERIC_PC, + 'rsp' : LLDB_REGNUM_GENERIC_SP, + 'rbp' : LLDB_REGNUM_GENERIC_FP, + 'rdi' : LLDB_REGNUM_GENERIC_ARG1, + 'rsi' : LLDB_REGNUM_GENERIC_ARG2, + 'rdx' : LLDB_REGNUM_GENERIC_ARG3, + 'rcx' : LLDB_REGNUM_GENERIC_ARG4, + 'r8' : LLDB_REGNUM_GENERIC_ARG5, + 'r9' : LLDB_REGNUM_GENERIC_ARG6 +}; + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +x86_64_register_infos = [ +{ 'name':'rax' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rbx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rcx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg4' }, +{ 'name':'rdx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg3' }, +{ 'name':'rsi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg2' }, +{ 'name':'rdi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg1' }, +{ 'name':'rbp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'fp' }, +{ 'name':'rsp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'sp' }, +{ 'name':'r8' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg5' }, +{ 'name':'r9' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg6' }, +{ 'name':'r10' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r11' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r12' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r13' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r14' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r15' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rip' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'pc' }, +{ 'name':'rflags', 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'cs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ss' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ds' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'es' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'gs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'stmm0' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm1' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm2' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm3' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm4' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm5' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm6' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm7' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'fctrl' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fstat' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ftag' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fiseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fioff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'foseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fooff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fop' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'xmm0' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm1' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm2' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm3' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm4' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm5' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm6' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm7' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm8' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm9' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm10' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm11' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm12' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm13' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm14' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm15' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'mxcsr' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'orig_rax' , 'set':1, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatHex }, +# Registers that are contained in or composed of one of more other registers +{ 'name':'eax' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[31:0]' }, +{ 'name':'ebx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[31:0]' }, +{ 'name':'ecx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[31:0]' }, +{ 'name':'edx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[31:0]' }, +{ 'name':'edi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[31:0]' }, +{ 'name':'esi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[31:0]' }, +{ 'name':'ebp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[31:0]' }, +{ 'name':'esp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[31:0]' }, +{ 'name':'r8d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[31:0]' }, +{ 'name':'r9d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[31:0]' }, +{ 'name':'r10d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[31:0]' }, +{ 'name':'r11d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[31:0]' }, +{ 'name':'r12d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[31:0]' }, +{ 'name':'r13d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[31:0]' }, +{ 'name':'r14d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[31:0]' }, +{ 'name':'r15d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[31:0]' }, + +{ 'name':'ax' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:0]' }, +{ 'name':'bx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:0]' }, +{ 'name':'cx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:0]' }, +{ 'name':'dx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:0]' }, +{ 'name':'di' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[15:0]' }, +{ 'name':'si' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[15:0]' }, +{ 'name':'bp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[15:0]' }, +{ 'name':'sp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[15:0]' }, +{ 'name':'r8w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[15:0]' }, +{ 'name':'r9w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[15:0]' }, +{ 'name':'r10w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[15:0]' }, +{ 'name':'r11w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[15:0]' }, +{ 'name':'r12w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[15:0]' }, +{ 'name':'r13w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[15:0]' }, +{ 'name':'r14w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[15:0]' }, +{ 'name':'r15w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[15:0]' }, + +{ 'name':'ah' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:8]' }, +{ 'name':'bh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:8]' }, +{ 'name':'ch' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:8]' }, +{ 'name':'dh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:8]' }, + +{ 'name':'al' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[7:0]' }, +{ 'name':'bl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[7:0]' }, +{ 'name':'cl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[7:0]' }, +{ 'name':'dl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[7:0]' }, +{ 'name':'dil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[7:0]' }, +{ 'name':'sil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[7:0]' }, +{ 'name':'bpl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[7:0]' }, +{ 'name':'spl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[7:0]' }, +{ 'name':'r8l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[7:0]' }, +{ 'name':'r9l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[7:0]' }, +{ 'name':'r10l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[7:0]' }, +{ 'name':'r11l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[7:0]' }, +{ 'name':'r12l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[7:0]' }, +{ 'name':'r13l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[7:0]' }, +{ 'name':'r14l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[7:0]' }, +{ 'name':'r15l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[7:0]' }, +]; + +g_target_definition = None + +def get_target_definition (): + global g_target_definition + if g_target_definition == None: + g_target_definition = {} + offset = 0 + for reg_info in x86_64_register_infos: + reg_name = reg_info['name'] + + # Only fill in the offset if there is no 'slice' in the register info + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize']/8 + + # Set the GCC/DWARF register number for this register if it has one + reg_num = get_reg_num(name_to_gcc_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['dwarf'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + # Set the GDB register number for this register if it has one + reg_num = get_reg_num(name_to_gdb_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gdb'] = reg_num + + g_target_definition['sets'] = ['General Purpose Registers', 'Floating Point Registers'] + g_target_definition['registers'] = x86_64_register_infos + g_target_definition['host-info'] = { 'triple' : 'x86_64-*-linux', 'endian': eByteOrderLittle } + g_target_definition['g-packet-size'] = offset + g_target_definition['breakpoint-pc-offset'] = -1 + return g_target_definition + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition()
\ No newline at end of file diff --git a/examples/python/x86_64_qemu_target_definition.py b/examples/python/x86_64_qemu_target_definition.py new file mode 100644 index 0000000000000..7b246896d8bfa --- /dev/null +++ b/examples/python/x86_64_qemu_target_definition.py @@ -0,0 +1,352 @@ +#!/usr/bin/python +#===-- x86_64_qemu_target_definition.py -----------------------------*- C++ -*-===// +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the remote stub +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the user mode qemu on linux. +# The only difference with the Linux file is the absense of orig_rax register. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_qemu_target_definition.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB stub. +#---------------------------------------------------------------------- +from lldb import * + +# Compiler and DWARF register numbers +name_to_gcc_dwarf_regnum = { + 'rax' : 0 , + 'rdx' : 1 , + 'rcx' : 2 , + 'rbx' : 3 , + 'rsi' : 4 , + 'rdi' : 5 , + 'rbp' : 6 , + 'rsp' : 7 , + 'r8' : 8 , + 'r9' : 9 , + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'xmm0' : 17, + 'xmm1' : 18, + 'xmm2' : 19, + 'xmm3' : 20, + 'xmm4' : 21, + 'xmm5' : 22, + 'xmm6' : 23, + 'xmm7' : 24, + 'xmm8' : 25, + 'xmm9' : 26, + 'xmm10' : 27, + 'xmm11' : 28, + 'xmm12' : 29, + 'xmm13' : 30, + 'xmm14' : 31, + 'xmm15' : 32, + 'stmm0' : 33, + 'stmm1' : 34, + 'stmm2' : 35, + 'stmm3' : 36, + 'stmm4' : 37, + 'stmm5' : 38, + 'stmm6' : 39, + 'stmm7' : 30, + 'ymm0' : 41, + 'ymm1' : 42, + 'ymm2' : 43, + 'ymm3' : 44, + 'ymm4' : 45, + 'ymm5' : 46, + 'ymm6' : 47, + 'ymm7' : 48, + 'ymm8' : 49, + 'ymm9' : 40, + 'ymm10' : 41, + 'ymm11' : 42, + 'ymm12' : 43, + 'ymm13' : 44, + 'ymm14' : 45, + 'ymm15' : 46 +}; + +name_to_gdb_regnum = { + 'rax' : 0, + 'rbx' : 1, + 'rcx' : 2, + 'rdx' : 3, + 'rsi' : 4, + 'rdi' : 5, + 'rbp' : 6, + 'rsp' : 7, + 'r8' : 8, + 'r9' : 9, + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'rflags': 17, + 'cs' : 18, + 'ss' : 19, + 'ds' : 20, + 'es' : 21, + 'fs' : 22, + 'gs' : 23, + 'stmm0' : 24, + 'stmm1' : 25, + 'stmm2' : 26, + 'stmm3' : 27, + 'stmm4' : 28, + 'stmm5' : 29, + 'stmm6' : 30, + 'stmm7' : 31, + 'fctrl' : 32, + 'fstat' : 33, + 'ftag' : 34, + 'fiseg' : 35, + 'fioff' : 36, + 'foseg' : 37, + 'fooff' : 38, + 'fop' : 39, + 'xmm0' : 40, + 'xmm1' : 41, + 'xmm2' : 42, + 'xmm3' : 43, + 'xmm4' : 44, + 'xmm5' : 45, + 'xmm6' : 46, + 'xmm7' : 47, + 'xmm8' : 48, + 'xmm9' : 49, + 'xmm10' : 50, + 'xmm11' : 51, + 'xmm12' : 52, + 'xmm13' : 53, + 'xmm14' : 54, + 'xmm15' : 55, + 'mxcsr' : 56, + 'ymm0' : 57, + 'ymm1' : 58, + 'ymm2' : 59, + 'ymm3' : 60, + 'ymm4' : 61, + 'ymm5' : 62, + 'ymm6' : 63, + 'ymm7' : 64, + 'ymm8' : 65, + 'ymm9' : 66, + 'ymm10' : 67, + 'ymm11' : 68, + 'ymm12' : 69, + 'ymm13' : 70, + 'ymm14' : 71, + 'ymm15' : 72 +}; + +name_to_generic_regnum = { + 'rip' : LLDB_REGNUM_GENERIC_PC, + 'rsp' : LLDB_REGNUM_GENERIC_SP, + 'rbp' : LLDB_REGNUM_GENERIC_FP, + 'rdi' : LLDB_REGNUM_GENERIC_ARG1, + 'rsi' : LLDB_REGNUM_GENERIC_ARG2, + 'rdx' : LLDB_REGNUM_GENERIC_ARG3, + 'rcx' : LLDB_REGNUM_GENERIC_ARG4, + 'r8' : LLDB_REGNUM_GENERIC_ARG5, + 'r9' : LLDB_REGNUM_GENERIC_ARG6 +}; + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +x86_64_register_infos = [ +{ 'name':'rax' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rbx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rcx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg4' }, +{ 'name':'rdx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg3' }, +{ 'name':'rsi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg2' }, +{ 'name':'rdi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg1' }, +{ 'name':'rbp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'fp' }, +{ 'name':'rsp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'sp' }, +{ 'name':'r8' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg5' }, +{ 'name':'r9' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg6' }, +{ 'name':'r10' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r11' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r12' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r13' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r14' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r15' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rip' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'pc' }, +{ 'name':'rflags', 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'cs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ss' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ds' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'es' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'gs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'stmm0' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm1' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm2' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm3' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm4' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm5' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm6' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm7' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'fctrl' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fstat' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ftag' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fiseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fioff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'foseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fooff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fop' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'xmm0' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm1' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm2' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm3' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm4' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm5' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm6' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm7' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm8' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm9' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm10' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm11' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm12' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm13' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm14' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm15' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'mxcsr' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +# Registers that are contained in or composed of one of more other registers +{ 'name':'eax' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[31:0]' }, +{ 'name':'ebx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[31:0]' }, +{ 'name':'ecx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[31:0]' }, +{ 'name':'edx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[31:0]' }, +{ 'name':'edi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[31:0]' }, +{ 'name':'esi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[31:0]' }, +{ 'name':'ebp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[31:0]' }, +{ 'name':'esp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[31:0]' }, +{ 'name':'r8d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[31:0]' }, +{ 'name':'r9d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[31:0]' }, +{ 'name':'r10d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[31:0]' }, +{ 'name':'r11d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[31:0]' }, +{ 'name':'r12d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[31:0]' }, +{ 'name':'r13d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[31:0]' }, +{ 'name':'r14d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[31:0]' }, +{ 'name':'r15d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[31:0]' }, + +{ 'name':'ax' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:0]' }, +{ 'name':'bx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:0]' }, +{ 'name':'cx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:0]' }, +{ 'name':'dx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:0]' }, +{ 'name':'di' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[15:0]' }, +{ 'name':'si' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[15:0]' }, +{ 'name':'bp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[15:0]' }, +{ 'name':'sp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[15:0]' }, +{ 'name':'r8w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[15:0]' }, +{ 'name':'r9w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[15:0]' }, +{ 'name':'r10w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[15:0]' }, +{ 'name':'r11w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[15:0]' }, +{ 'name':'r12w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[15:0]' }, +{ 'name':'r13w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[15:0]' }, +{ 'name':'r14w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[15:0]' }, +{ 'name':'r15w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[15:0]' }, + +{ 'name':'ah' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:8]' }, +{ 'name':'bh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:8]' }, +{ 'name':'ch' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:8]' }, +{ 'name':'dh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:8]' }, + +{ 'name':'al' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[7:0]' }, +{ 'name':'bl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[7:0]' }, +{ 'name':'cl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[7:0]' }, +{ 'name':'dl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[7:0]' }, +{ 'name':'dil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[7:0]' }, +{ 'name':'sil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[7:0]' }, +{ 'name':'bpl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[7:0]' }, +{ 'name':'spl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[7:0]' }, +{ 'name':'r8l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[7:0]' }, +{ 'name':'r9l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[7:0]' }, +{ 'name':'r10l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[7:0]' }, +{ 'name':'r11l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[7:0]' }, +{ 'name':'r12l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[7:0]' }, +{ 'name':'r13l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[7:0]' }, +{ 'name':'r14l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[7:0]' }, +{ 'name':'r15l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[7:0]' }, +]; + +g_target_definition = None + +def get_target_definition (): + global g_target_definition + if g_target_definition == None: + g_target_definition = {} + offset = 0 + for reg_info in x86_64_register_infos: + reg_name = reg_info['name'] + + # Only fill in the offset if there is no 'slice' in the register info + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize']/8 + + # Set the GCC/DWARF register number for this register if it has one + reg_num = get_reg_num(name_to_gcc_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['dwarf'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + # Set the GDB register number for this register if it has one + reg_num = get_reg_num(name_to_gdb_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gdb'] = reg_num + + g_target_definition['sets'] = ['General Purpose Registers', 'Floating Point Registers'] + g_target_definition['registers'] = x86_64_register_infos + g_target_definition['host-info'] = { 'triple' : 'x86_64-*-linux', 'endian': eByteOrderLittle } + g_target_definition['g-packet-size'] = offset + g_target_definition['breakpoint-pc-offset'] = -1 + return g_target_definition + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition() diff --git a/examples/python/x86_64_target_definition.py b/examples/python/x86_64_target_definition.py new file mode 100644 index 0000000000000..3a1290b62f809 --- /dev/null +++ b/examples/python/x86_64_target_definition.py @@ -0,0 +1,357 @@ +#!/usr/bin/python +#===-- x86_64_target_definition.py -----------------------------*- C++ -*-===// +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===----------------------------------------------------------------------===// + +#---------------------------------------------------------------------- +# DESCRIPTION +# +# This file can be used with the following setting: +# plugin.process.gdb-remote.target-definition-file +# This setting should be used when you are trying to connect to a +# remote GDB server that doesn't support any of the register discovery +# packets that LLDB normally uses. +# +# Why is this necessary? LLDB doesn't require a new build of LLDB that +# targets each new architecture you will debug with. Instead, all +# architectures are supported and LLDB relies on extra GDB server +# packets to discover the target we are connecting to so that is can +# show the right registers for each target. This allows the GDB server +# to change and add new registers without requiring a new LLDB build +# just so we can see new registers. +# +# This file implements the x86_64 registers for the darwin version of +# GDB and allows you to connect to servers that use this register set. +# +# USAGE +# +# (lldb) settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_target_definition.py +# (lldb) gdb-remote other.baz.com:1234 +# +# The target definition file will get used if and only if the +# qRegisterInfo packets are not supported when connecting to a remote +# GDB server. +#---------------------------------------------------------------------- +from lldb import * + +# Compiler and DWARF register numbers +name_to_gcc_dwarf_regnum = { + 'rax' : 0 , + 'rdx' : 1 , + 'rcx' : 2 , + 'rbx' : 3 , + 'rsi' : 4 , + 'rdi' : 5 , + 'rbp' : 6 , + 'rsp' : 7 , + 'r8' : 8 , + 'r9' : 9 , + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'xmm0' : 17, + 'xmm1' : 18, + 'xmm2' : 19, + 'xmm3' : 20, + 'xmm4' : 21, + 'xmm5' : 22, + 'xmm6' : 23, + 'xmm7' : 24, + 'xmm8' : 25, + 'xmm9' : 26, + 'xmm10' : 27, + 'xmm11' : 28, + 'xmm12' : 29, + 'xmm13' : 30, + 'xmm14' : 31, + 'xmm15' : 32, + 'stmm0' : 33, + 'stmm1' : 34, + 'stmm2' : 35, + 'stmm3' : 36, + 'stmm4' : 37, + 'stmm5' : 38, + 'stmm6' : 39, + 'stmm7' : 30, + 'ymm0' : 41, + 'ymm1' : 42, + 'ymm2' : 43, + 'ymm3' : 44, + 'ymm4' : 45, + 'ymm5' : 46, + 'ymm6' : 47, + 'ymm7' : 48, + 'ymm8' : 49, + 'ymm9' : 40, + 'ymm10' : 41, + 'ymm11' : 42, + 'ymm12' : 43, + 'ymm13' : 44, + 'ymm14' : 45, + 'ymm15' : 46 +}; + +name_to_gdb_regnum = { + 'rax' : 0, + 'rbx' : 1, + 'rcx' : 2, + 'rdx' : 3, + 'rsi' : 4, + 'rdi' : 5, + 'rbp' : 6, + 'rsp' : 7, + 'r8' : 8, + 'r9' : 9, + 'r10' : 10, + 'r11' : 11, + 'r12' : 12, + 'r13' : 13, + 'r14' : 14, + 'r15' : 15, + 'rip' : 16, + 'rflags': 17, + 'cs' : 18, + 'ss' : 19, + 'ds' : 20, + 'es' : 21, + 'fs' : 22, + 'gs' : 23, + 'stmm0' : 24, + 'stmm1' : 25, + 'stmm2' : 26, + 'stmm3' : 27, + 'stmm4' : 28, + 'stmm5' : 29, + 'stmm6' : 30, + 'stmm7' : 31, + 'fctrl' : 32, + 'fstat' : 33, + 'ftag' : 34, + 'fiseg' : 35, + 'fioff' : 36, + 'foseg' : 37, + 'fooff' : 38, + 'fop' : 39, + 'xmm0' : 40, + 'xmm1' : 41, + 'xmm2' : 42, + 'xmm3' : 43, + 'xmm4' : 44, + 'xmm5' : 45, + 'xmm6' : 46, + 'xmm7' : 47, + 'xmm8' : 48, + 'xmm9' : 49, + 'xmm10' : 50, + 'xmm11' : 51, + 'xmm12' : 52, + 'xmm13' : 53, + 'xmm14' : 54, + 'xmm15' : 55, + 'mxcsr' : 56, + 'ymm0' : 57, + 'ymm1' : 58, + 'ymm2' : 59, + 'ymm3' : 60, + 'ymm4' : 61, + 'ymm5' : 62, + 'ymm6' : 63, + 'ymm7' : 64, + 'ymm8' : 65, + 'ymm9' : 66, + 'ymm10' : 67, + 'ymm11' : 68, + 'ymm12' : 69, + 'ymm13' : 70, + 'ymm14' : 71, + 'ymm15' : 72 +}; + +name_to_generic_regnum = { + 'rip' : LLDB_REGNUM_GENERIC_PC, + 'rsp' : LLDB_REGNUM_GENERIC_SP, + 'rbp' : LLDB_REGNUM_GENERIC_FP, + 'rdi' : LLDB_REGNUM_GENERIC_ARG1, + 'rsi' : LLDB_REGNUM_GENERIC_ARG2, + 'rdx' : LLDB_REGNUM_GENERIC_ARG3, + 'rcx' : LLDB_REGNUM_GENERIC_ARG4, + 'r8' : LLDB_REGNUM_GENERIC_ARG5, + 'r9' : LLDB_REGNUM_GENERIC_ARG6 +}; + + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +def get_reg_num (reg_num_dict, reg_name): + if reg_name in reg_num_dict: + return reg_num_dict[reg_name] + return LLDB_INVALID_REGNUM + +x86_64_register_infos = [ +{ 'name':'rax' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rbx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rcx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg4' }, +{ 'name':'rdx' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg3' }, +{ 'name':'rsi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg2' }, +{ 'name':'rdi' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg1' }, +{ 'name':'rbp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'fp' }, +{ 'name':'rsp' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'sp' }, +{ 'name':'r8' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg5' }, +{ 'name':'r9' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'arg6' }, +{ 'name':'r10' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r11' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r12' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r13' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r14' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'r15' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo }, +{ 'name':'rip' , 'set':0, 'bitsize':64 , 'encoding':eEncodingUint , 'format':eFormatAddressInfo, 'alt-name':'pc' }, +{ 'name':'rflags', 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'cs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ss' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ds' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'es' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'gs' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'stmm0' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm1' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm2' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm3' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm4' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm5' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm6' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'stmm7' , 'set':1, 'bitsize':80 , 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'fctrl' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fstat' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'ftag' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fiseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fioff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'foseg' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fooff' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'fop' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +{ 'name':'xmm0' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm1' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm2' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm3' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm4' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm5' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm6' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm7' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm8' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm9' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm10' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm11' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm12' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm13' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm14' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'xmm15' , 'set':1, 'bitsize':128, 'encoding':eEncodingVector, 'format':eFormatVectorOfUInt8 }, +{ 'name':'mxcsr' , 'set':1, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex }, +# Registers that are contained in or composed of one of more other registers +{ 'name':'eax' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[31:0]' }, +{ 'name':'ebx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[31:0]' }, +{ 'name':'ecx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[31:0]' }, +{ 'name':'edx' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[31:0]' }, +{ 'name':'edi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[31:0]' }, +{ 'name':'esi' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[31:0]' }, +{ 'name':'ebp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[31:0]' }, +{ 'name':'esp' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[31:0]' }, +{ 'name':'r8d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[31:0]' }, +{ 'name':'r9d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[31:0]' }, +{ 'name':'r10d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[31:0]' }, +{ 'name':'r11d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[31:0]' }, +{ 'name':'r12d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[31:0]' }, +{ 'name':'r13d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[31:0]' }, +{ 'name':'r14d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[31:0]' }, +{ 'name':'r15d' , 'set':0, 'bitsize':32 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[31:0]' }, + +{ 'name':'ax' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:0]' }, +{ 'name':'bx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:0]' }, +{ 'name':'cx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:0]' }, +{ 'name':'dx' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:0]' }, +{ 'name':'di' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[15:0]' }, +{ 'name':'si' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[15:0]' }, +{ 'name':'bp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[15:0]' }, +{ 'name':'sp' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[15:0]' }, +{ 'name':'r8w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[15:0]' }, +{ 'name':'r9w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[15:0]' }, +{ 'name':'r10w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[15:0]' }, +{ 'name':'r11w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[15:0]' }, +{ 'name':'r12w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[15:0]' }, +{ 'name':'r13w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[15:0]' }, +{ 'name':'r14w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[15:0]' }, +{ 'name':'r15w' , 'set':0, 'bitsize':16 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[15:0]' }, + +{ 'name':'ah' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[15:8]' }, +{ 'name':'bh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[15:8]' }, +{ 'name':'ch' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[15:8]' }, +{ 'name':'dh' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[15:8]' }, + +{ 'name':'al' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rax[7:0]' }, +{ 'name':'bl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbx[7:0]' }, +{ 'name':'cl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rcx[7:0]' }, +{ 'name':'dl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdx[7:0]' }, +{ 'name':'dil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rdi[7:0]' }, +{ 'name':'sil' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsi[7:0]' }, +{ 'name':'bpl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rbp[7:0]' }, +{ 'name':'spl' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'rsp[7:0]' }, +{ 'name':'r8l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r8[7:0]' }, +{ 'name':'r9l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r9[7:0]' }, +{ 'name':'r10l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r10[7:0]' }, +{ 'name':'r11l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r11[7:0]' }, +{ 'name':'r12l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r12[7:0]' }, +{ 'name':'r13l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r13[7:0]' }, +{ 'name':'r14l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r14[7:0]' }, +{ 'name':'r15l' , 'set':0, 'bitsize':8 , 'encoding':eEncodingUint , 'format':eFormatHex , 'slice': 'r15[7:0]' }, +]; + +g_target_definition = None + +def get_target_definition (): + global g_target_definition + if g_target_definition == None: + g_target_definition = {} + offset = 0 + for reg_info in x86_64_register_infos: + reg_name = reg_info['name'] + + # Only fill in the offset if there is no 'slice' in the register info + if 'slice' not in reg_info and 'composite' not in reg_info: + reg_info['offset'] = offset + offset += reg_info['bitsize']/8 + + # Set the GCC/DWARF register number for this register if it has one + reg_num = get_reg_num(name_to_gcc_dwarf_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gcc'] = reg_num + reg_info['dwarf'] = reg_num + + # Set the generic register number for this register if it has one + reg_num = get_reg_num(name_to_generic_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['generic'] = reg_num + + # Set the GDB register number for this register if it has one + reg_num = get_reg_num(name_to_gdb_regnum, reg_name) + if reg_num != LLDB_INVALID_REGNUM: + reg_info['gdb'] = reg_num + + g_target_definition['sets'] = ['General Purpose Registers', 'Floating Point Registers'] + g_target_definition['registers'] = x86_64_register_infos + g_target_definition['host-info'] = { 'triple' : 'x86_64-apple-macosx', 'endian': eByteOrderLittle } + g_target_definition['g-packet-size'] = offset + return g_target_definition + +def get_dynamic_setting(target, setting_name): + if setting_name == 'gdb-server-target-definition': + return get_target_definition()
\ No newline at end of file |