fblldb.py (143 lines of code) (raw):

#!/usr/bin/python # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved # # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. import imp import os from optparse import OptionParser import lldb def __lldb_init_module(debugger, dict): filePath = os.path.realpath(__file__) lldbHelperDir = os.path.dirname(filePath) commandsDirectory = os.path.join(lldbHelperDir, "commands") loadCommandsInDirectory(commandsDirectory) def loadCommandsInDirectory(commandsDirectory): for file in os.listdir(commandsDirectory): fileName, fileExtension = os.path.splitext(file) if fileExtension == ".py": module = imp.load_source(fileName, os.path.join(commandsDirectory, file)) if hasattr(module, "lldbinit"): module.lldbinit() if hasattr(module, "lldbcommands"): module._loadedFunctions = {} for command in module.lldbcommands(): loadCommand( module, command, commandsDirectory, fileName, fileExtension ) def loadCommand(module, command, directory, filename, extension): func = makeRunCommand(command, os.path.join(directory, filename + extension)) name = command.name() helpText = ( command.description().strip().splitlines()[0] ) # first line of description key = filename + "_" + name module._loadedFunctions[key] = func functionName = "__" + key lldb.debugger.HandleCommand( "script " + functionName + " = sys.modules['" + module.__name__ + "']._loadedFunctions['" + key + "']" ) lldb.debugger.HandleCommand( 'command script add --help "{help}" --function {function} {name}'.format( help=helpText.replace('"', '\\"'), # escape quotes function=functionName, name=name, ) ) def makeRunCommand(command, filename): def runCommand(debugger, input, exe_ctx, result, _): command.result = result command.context = exe_ctx splitInput = command.lex(input) # OptionParser will throw in the case where you want just one # big long argument and no options and you enter something # that starts with '-' in the argument. e.g.: # somecommand -[SomeClass someSelector:] # This solves that problem by prepending a '--' so that # OptionParser does the right thing. options = command.options() if len(options) == 0: if "--" not in splitInput: splitInput.insert(0, "--") parser = optionParserForCommand(command) (options, args) = parser.parse_args(splitInput) # When there are more args than the command has declared, assume # the initial args form an expression and combine them into a single arg. if len(args) > len(command.args()): overhead = len(args) - len(command.args()) head = args[: overhead + 1] # Take N+1 and reduce to 1. args = [" ".join(head)] + args[-overhead:] if validateArgsForCommand(args, command): command.run(args, options) runCommand.__doc__ = helpForCommand(command, filename) return runCommand def validateArgsForCommand(args, command): if len(args) < len(command.args()): defaultArgs = [arg.default for arg in command.args()] defaultArgsToAppend = defaultArgs[len(args) :] index = len(args) for defaultArg in defaultArgsToAppend: if not defaultArg: arg = command.args()[index] print("Whoops! You are missing the <" + arg.argName + "> argument.") print("\nUsage: " + usageForCommand(command)) return index += 1 args.extend(defaultArgsToAppend) return True def optionParserForCommand(command): parser = OptionParser() for argument in command.options(): if argument.boolean: parser.add_option( argument.shortName, argument.longName, dest=argument.argName, help=argument.help, action=("store_false" if argument.default else "store_true"), ) else: parser.add_option( argument.shortName, argument.longName, dest=argument.argName, help=argument.help, default=argument.default, ) return parser def helpForCommand(command, filename): help = command.description() argSyntax = "" optionSyntax = "" if command.args(): help += "\n\nArguments:" for arg in command.args(): help += "\n <" + arg.argName + ">; " if arg.argType: help += "Type: " + arg.argType + "; " help += arg.help argSyntax += " <" + arg.argName + ">" if command.options(): help += "\n\nOptions:" for option in command.options(): if option.longName and option.shortName: optionFlag = option.longName + "/" + option.shortName elif option.longName: optionFlag = option.longName else: optionFlag = option.shortName help += "\n " + optionFlag + " " if not option.boolean: help += "<" + option.argName + ">; Type: " + option.argType help += "; " + option.help optionSyntax += " [{name}{arg}]".format( name=(option.longName or option.shortName), arg=("" if option.boolean else ("=" + option.argName)), ) help += "\n\nSyntax: " + command.name() + optionSyntax + argSyntax help += "\n\nThis command is implemented as %s in %s." % ( command.__class__.__name__, filename, ) return help def usageForCommand(command): usage = command.name() for arg in command.args(): if arg.default: usage += " [" + arg.argName + "]" else: usage += " " + arg.argName return usage