464 lines · python
1"""2This module implements a couple of utility classes to make writing3lldb parsed commands more Pythonic.4The way to use it is to make a class for your command that inherits from ParsedCommandBase.5That will make an LLDBOptionValueParser which you will use for your6option definition, and to fetch option values for the current invocation7of your command. For concision, I'll call this the `OVParser`. 8Access to the `OVParser` is through:9 10ParsedCommandBase.get_parser()11 12Next, implement setup_command_definition() in your new command class, and call:13 14 self.get_parser().add_option()15 16to add all your options. The order doesn't matter for options, lldb will sort them17alphabetically for you when it prints help.18 19Similarly you can define the arguments with:20 21 self.get_parser().add_argument()22 23At present, lldb doesn't do as much work as it should verifying arguments, it24only checks that commands that take no arguments don't get passed arguments.25 26Then implement the execute function for your command as:27 28 def __call__(self, debugger, args_list, exe_ctx, result):29 30The arguments will be a list of strings. 31 32You can access the option values using the 'dest' string you passed in when defining the option.33And if you need to know whether a given option was set by the user or not, you can34use the was_set API. 35 36So for instance, if you have an option whose "dest" is "my_option", then:37 38 self.get_parser().my_option39 40will fetch the value, and:41 42 self.get_parser().was_set("my_option")43 44will return True if the user set this option, and False if it was left at its default45value.46 47Custom Completions:48 49You can also implement custom completers for your custom command, either for the50arguments to your command or to the option values in your command. If you use enum51values or if your option/argument uses is one of the types we have completers for,52you should not need to do this. But if you have your own completeable types, or if53you want completion of one option to be conditioned by other options on the command54line, you can use this interface to take over the completion. 55 56You can choose to add a completion for the option values defined for your command,57or for the arguments, separately. For the option values, define:58 59def handle_option_argument_completion(self, long_option, cursor_pos):60 61The line to be completed will be parsed up to the option containint the cursor position, 62and the values will be set in the OptionValue parser object. long_option will be63the option name containing the cursor, and cursor_pos will be the position of the cursor64in that option's value. You can call the `OVParser` method: `dest_for_option(long_option)` 65to get the value for that option. The other options that came before the cursor in the command66line will also be set in the `OVParser` when the completion handler is called.67 68For argument values, define:69 70def handle_argument_completion(self, args, arg_pos, cursor_pos):71 72Again, the command line will be parsed up to the cursor position, and all the options73before the cursor pose will be set in the `OVParser`. args is a python list of the74arguments, arg_pos is the index of the argument with the cursor, and cursor_pos is75the position of the cursor in the argument.76 77In both cases, the return value determines the completion.78 79Return False to mean "Not Handled" - in which case lldb will fall back on the80standard completion machinery.81 82Return True to mean "Handled with no completions".83 84If there is a single unique completion, return a Python dictionary with two elements:85 86return {"completion" : "completed_value", "mode" : <"partial", "complete">}87 88If the mode is "partial", then the completion is to a common base, if it is "complete"89then the argument is considered done - mostly meaning lldb will put a space after the90completion string. "complete" is the default if no "mode" is specified.91 92If there are multiple completion options, then return:93 94return {"values" : ["option1", "option2"]}95 96Optionally, you can return a parallel array of "descriptions" which the completer will 97print alongside the options:98 99return {"values" : ["option1", "option2"], "descriptions" : ["the first option", "the second option"]}100 101The cmdtemplate example currently uses the parsed command infrastructure:102 103llvm-project/lldb/examples/python/cmdtemplate.py104 105There are also a few example commands in the lldb testsuite at:106 107llvm-project/lldb/test/API/commands/command/script/add/test_commands.py108"""109import inspect110import lldb111import sys112from abc import abstractmethod113 114# Some methods to translate common value types. Should return a115# tuple of the value and an error value (True => error) if the116# type can't be converted. These are called internally when the117# command line is parsed into the 'dest' properties, you should118# not need to call them directly.119# FIXME: Need a way to push the conversion error string back to lldb.120def to_bool(in_value):121 error = True122 value = False123 if type(in_value) != str or len(in_value) == 0:124 return (value, error)125 126 low_in = in_value.lower()127 if low_in in ["y", "yes", "t", "true", "1"]:128 value = True129 error = False130 131 if not value and low_in in ["n", "no", "f", "false", "0"]:132 value = False133 error = False134 135 return (value, error)136 137def to_int(in_value):138 #FIXME: Not doing errors yet...139 return (int(in_value), False)140 141def to_unsigned(in_value):142 # FIXME: find an unsigned converter...143 # And handle errors.144 return (int(in_value), False)145 146translators = {147 lldb.eArgTypeBoolean : to_bool,148 lldb.eArgTypeBreakpointID : to_unsigned,149 lldb.eArgTypeByteSize : to_unsigned,150 lldb.eArgTypeCount : to_unsigned,151 lldb.eArgTypeFrameIndex : to_unsigned,152 lldb.eArgTypeIndex : to_unsigned,153 lldb.eArgTypeLineNum : to_unsigned,154 lldb.eArgTypeNumLines : to_unsigned,155 lldb.eArgTypeNumberPerLine : to_unsigned,156 lldb.eArgTypeOffset : to_int,157 lldb.eArgTypeThreadIndex : to_unsigned,158 lldb.eArgTypeUnsignedInteger : to_unsigned,159 lldb.eArgTypeWatchpointID : to_unsigned,160 lldb.eArgTypeColumnNum : to_unsigned,161 lldb.eArgTypeRecognizerID : to_unsigned,162 lldb.eArgTypeTargetID : to_unsigned,163 lldb.eArgTypeStopHookID : to_unsigned164}165 166def translate_value(value_type, value):167 try:168 return translators[value_type](value)169 except KeyError:170 # If we don't have a translator, return the string value.171 return (value, False)172 173class LLDBOptionValueParser:174 """175 This class holds the option definitions for the command, and when176 the command is run, you can ask the parser for the current values. """177 178 def __init__(self):179 # This is a dictionary of dictionaries. The key is the long option180 # name, and the value is the rest of the definition.181 self.options_dict = {}182 self.args_array = []183 184 185 # FIXME: would this be better done on the C++ side?186 # The common completers are missing some useful ones.187 # For instance there really should be a common Type completer188 # And an "lldb command name" completer.189 completion_table = {190 lldb.eArgTypeAddressOrExpression : lldb.eVariablePathCompletion,191 lldb.eArgTypeArchitecture : lldb.eArchitectureCompletion,192 lldb.eArgTypeBreakpointID : lldb.eBreakpointCompletion,193 lldb.eArgTypeBreakpointIDRange : lldb.eBreakpointCompletion,194 lldb.eArgTypeBreakpointName : lldb.eBreakpointNameCompletion,195 lldb.eArgTypeClassName : lldb.eSymbolCompletion,196 lldb.eArgTypeDirectoryName : lldb.eDiskDirectoryCompletion,197 lldb.eArgTypeExpression : lldb.eVariablePathCompletion,198 lldb.eArgTypeExpressionPath : lldb.eVariablePathCompletion,199 lldb.eArgTypeFilename : lldb.eDiskFileCompletion,200 lldb.eArgTypeFrameIndex : lldb.eFrameIndexCompletion,201 lldb.eArgTypeFunctionName : lldb.eSymbolCompletion,202 lldb.eArgTypeFunctionOrSymbol : lldb.eSymbolCompletion,203 lldb.eArgTypeLanguage : lldb.eTypeLanguageCompletion,204 lldb.eArgTypePath : lldb.eDiskFileCompletion,205 lldb.eArgTypePid : lldb.eProcessIDCompletion,206 lldb.eArgTypeProcessName : lldb.eProcessNameCompletion,207 lldb.eArgTypeRegisterName : lldb.eRegisterCompletion,208 lldb.eArgTypeRunArgs : lldb.eDiskFileCompletion,209 lldb.eArgTypeShlibName : lldb.eModuleCompletion,210 lldb.eArgTypeSourceFile : lldb.eSourceFileCompletion,211 lldb.eArgTypeSymbol : lldb.eSymbolCompletion,212 lldb.eArgTypeThreadIndex : lldb.eThreadIndexCompletion,213 lldb.eArgTypeVarName : lldb.eVariablePathCompletion,214 lldb.eArgTypePlatform : lldb.ePlatformPluginCompletion,215 lldb.eArgTypeWatchpointID : lldb.eWatchpointIDCompletion,216 lldb.eArgTypeWatchpointIDRange : lldb.eWatchpointIDCompletion,217 lldb.eArgTypeModuleUUID : lldb.eModuleUUIDCompletion,218 lldb.eArgTypeStopHookID : lldb.eStopHookIDCompletion219 }220 221 @classmethod222 def determine_completion(cls, arg_type):223 return cls.completion_table.get(arg_type, lldb.eNoCompletion)224 225 def add_argument_set(self, arguments):226 self.args_array.append(arguments)227 228 def get_option_element(self, long_name):229 return self.options_dict.get(long_name, None)230 231 def is_enum_opt(self, opt_name):232 elem = self.get_option_element(opt_name)233 if not elem:234 return False235 return "enum_values" in elem236 237 def option_parsing_started(self):238 """ This makes the ivars for all the "dest" values in the array and gives them239 their default values. You should not have to call this by hand, though if240 you have some option that needs to do some work when a new command invocation241 starts, you can override this to handle your special option. """242 for key, elem in self.options_dict.items():243 elem['_value_set'] = False244 # If there's no value_type, then there can't be a dest.245 if not "value_type" in elem:246 continue247 248 try:249 object.__setattr__(self, elem["dest"], elem["default"])250 except AttributeError:251 # It isn't an error not to have a "dest" variable name, you'll252 # just have to manage this option's value on your own.253 continue254 except KeyError:255 continue256 257 def set_enum_value(self, enum_values, input):258 """ This sets the value for an enum option, you should not have to call this259 by hand. """260 candidates = []261 for candidate in enum_values:262 # The enum_values are a two element list of value & help string.263 value = candidate[0]264 if value.startswith(input):265 candidates.append(value)266 267 if len(candidates) == 1:268 return (candidates[0], False)269 else:270 return (input, True)271 272 def set_option_value(self, exe_ctx, opt_name, opt_value):273 """ This sets a single option value. This will handle most option274 value types, but if you have an option that has some complex behavior,275 you can override this to implement that behavior, and then pass the276 rest of the options to the base class implementation. """277 elem = self.get_option_element(opt_name)278 if not elem:279 return False280 281 # If there's no value_type in element, then it has no value, so just mark282 # it set and return:283 if not "value_type" in elem:284 elem["_value_set"] = True285 return True286 287 if "enum_values" in elem:288 (value, error) = self.set_enum_value(elem["enum_values"], opt_value)289 else:290 (value, error) = translate_value(elem["value_type"], opt_value)291 292 if error:293 return False294 295 object.__setattr__(self, elem["dest"], value)296 elem["_value_set"] = True297 return True298 299 def was_set(self, opt_name):300 """Call this in the __call__ method of your command to determine301 whether this option was set on the command line. It is sometimes302 useful to know whether an option has the default value because the303 user set it explicitly (was_set -> True) or not.304 You can also call this in a handle_completion method, but it will305 currently only report true values for the options mentioned306 BEFORE the cursor point in the command line.307 """308 309 elem = self.get_option_element(opt_name)310 if not elem:311 return False312 try:313 return elem["_value_set"]314 except AttributeError:315 return False316 317 def dest_for_option(self, opt_name):318 """This will return the value of the dest variable you defined for opt_name.319 Mostly useful for handle_completion where you get passed the long option.320 """321 elem = self.get_option_element(opt_name)322 if not elem:323 return None324 value = self.__dict__[elem["dest"]]325 return value326 327 def add_option(328 self,329 short_option,330 long_option,331 help,332 default=None,333 dest=None,334 required=False,335 groups=None,336 value_type=None,337 completion_type=None,338 enum_values=None,339 ):340 """341 short_option: one character, must be unique, not required342 long_option: no spaces, must be unique, required343 help: a usage string for this option, will print in the command help344 default: the initial value for this option (if it has a value)345 dest: the name of the property that gives you access to the value for346 this value. Defaults to the long option if not provided.347 required: if true, this option must be provided or the command will error out348 groups: Which "option groups" does this option belong to. This can either be349 a simple list (e.g. [1, 3, 4, 5]) or you can specify ranges by sublists:350 so [1, [3,5]] is the same as [1, 3, 4, 5].351 value_type: one of the lldb.eArgType enum values. Some of the common arg352 types also have default completers, which will be applied automatically.353 completion_type: currently these are values form the lldb.CompletionType enum. If354 you need custom completions, implement handle_option_argument_completion.355 enum_values: An array of duples: ["element_name", "element_help"]. If provided,356 only one of the enum elements is allowed. The value will be the357 element_name for the chosen enum element as a string.358 """359 if not dest:360 dest = long_option361 362 if not completion_type:363 completion_type = self.determine_completion(value_type)364 365 dict = {366 "short_option": short_option,367 "required": required,368 "help": help,369 }370 371 if enum_values:372 if not value_type:373 print("I am setting value type for an enum value")374 value_type = lldb.eArgTypeNone375 else:376 print(f"An enum value had a type: {value_type}")377 dict["enum_values"] = enum_values378 379 if value_type:380 dict["value_type"] = value_type381 dict["completion_type"] = completion_type382 dict["dest"] = dest383 dict["default"] = default384 385 if groups:386 dict["groups"] = groups387 388 self.options_dict[long_option] = dict389 390 def make_argument_element(self, arg_type, repeat = "optional", groups = None):391 element = {"arg_type" : arg_type, "repeat" : repeat}392 if groups:393 element["groups"] = groups394 return element395 396class ParsedCommand:397 def __init__(self, debugger, unused):398 self.debugger = debugger399 self.ov_parser = LLDBOptionValueParser()400 self.setup_command_definition()401 402 def get_options_definition(self):403 return self.get_parser().options_dict404 405 def get_flags(self):406 return 0407 408 def get_args_definition(self):409 return self.get_parser().args_array410 411 # The base class will handle calling these methods412 # when appropriate.413 414 def option_parsing_started(self):415 self.get_parser().option_parsing_started()416 417 def set_option_value(self, exe_ctx, opt_name, opt_value):418 return self.get_parser().set_option_value(exe_ctx, opt_name, opt_value)419 420 def get_parser(self):421 """Returns the option value parser for this command.422 When defining the command, use the parser to add423 argument and option definitions to the command.424 When you are in the command callback, the parser425 gives you access to the options passes to this426 invocation"""427 428 return self.ov_parser429 430 # These are the two "pure virtual" methods:431 @abstractmethod432 def __call__(self, debugger, args_array, exe_ctx, result):433 """This is the command callback. The option values are434 provided by the 'dest' properties on the parser.435 436 args_array: This is the list of arguments provided.437 exe_ctx: Gives the SBExecutionContext on which the438 command should operate.439 result: Any results of the command should be440 written into this SBCommandReturnObject.441 """442 raise NotImplementedError()443 444 @abstractmethod445 def setup_command_definition(self):446 """This will be called when your command is added to447 the command interpreter. Here is where you add your448 options and argument definitions for the command."""449 raise NotImplementedError()450 451 @staticmethod452 def do_register_cmd(cls, debugger, module_name):453 """ Add any commands contained in this module to LLDB """454 command = "command script add -o -p -c %s.%s %s" % (455 module_name,456 cls.__name__,457 cls.program,458 )459 debugger.HandleCommand(command)460 print(461 'The "{0}" command has been installed, type "help {0}"'462 'for detailed help.'.format(cls.program)463 )464