| import lldb | |
| import 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 is not 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(r"^\+([0-9]+)$", linespec) | |
| if mo is not 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(r"^\-([0-9]+)$", linespec) | |
| if mo is not 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 is not 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(r"\*((0x)?([0-9a-f]+))$", linespec) | |
| if mo is not None: | |
| matched = True | |
| # print "Matched <address-expression>" | |
| address = int(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, exe_ctx, result, internal_dict): | |
| if command == "": | |
| result.AppendMessage(usage_string()) | |
| args = command.split() | |
| if not debugger.IsValid(): | |
| result.AppendMessage("Invalid debugger!") | |
| return | |
| target = exe_ctx.target | |
| if not target.IsValid(): | |
| result.AppendMessage("jump requires a valid target.") | |
| return | |
| if not exe_ctx.process.IsValid(): | |
| result.AppendMessage("jump requires a valid process.") | |
| return | |
| if not exe_ctx.thread.IsValid(): | |
| result.AppendMessage("jump requires a valid thread.") | |
| return | |
| frame = exe_ctx.frame | |
| 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)) | |
| def __lldb_init_module(debugger, internal_dict): | |
| # Module is being run inside the LLDB interpreter | |
| jump.__doc__ = usage_string() | |
| debugger.HandleCommand("command script add -o -f jump.jump jump") | |
| print( | |
| 'The "jump" command has been installed, type "help jump" or "jump <ENTER>" for detailed help.' | |
| ) |