1import lldb, re
2
3def parse_linespec (linespec, frame, result):
4    """Handles a subset of GDB-style linespecs.  Specifically:
5
6       number           - A line in the current file
7       +offset          - The line /offset/ lines after this line
8       -offset          - The line /offset/ lines before this line
9       filename:number  - Line /number/ in file /filename/
10       function         - The start of /function/
11       *address         - The pointer target of /address/, which must be a literal (but see `` in LLDB)
12
13       We explicitly do not handle filename:function because it is ambiguous in Objective-C.
14
15       This function returns a list of addresses."""
16
17    breakpoint = None
18    target = frame.GetThread().GetProcess().GetTarget()
19
20    matched = False
21
22    if (not matched):
23        mo = re.match("^([0-9]+)$", linespec)
24        if (mo != None):
25            matched = True
26            #print "Matched <linenum>"
27            line_number = int(mo.group(1))
28            line_entry = frame.GetLineEntry()
29            if not line_entry.IsValid():
30                result.AppendMessage("Specified a line in the current file, but the current frame doesn't have line table information.")
31                return
32            breakpoint = target.BreakpointCreateByLocation (line_entry.GetFileSpec(), line_number)
33
34    if (not matched):
35        mo = re.match("^\+([0-9]+)$", linespec)
36        if (mo != None):
37            matched = True
38            #print "Matched +<count>"
39            line_number = int(mo.group(1))
40            line_entry = frame.GetLineEntry()
41            if not line_entry.IsValid():
42                result.AppendMessage("Specified a line in the current file, but the current frame doesn't have line table information.")
43                return
44            breakpoint = target.BreakpointCreateByLocation(line_entry.GetFileSpec(), (line_entry.GetLine() + line_number))
45
46    if (not matched):
47        mo = re.match("^\-([0-9]+)$", linespec)
48        if (mo != None):
49            matched = True
50            #print "Matched -<count>"
51            line_number = int(mo.group(1))
52            line_entry = frame.GetLineEntry()
53            if not line_entry.IsValid():
54                result.AppendMessage("Specified a line in the current file, but the current frame doesn't have line table information.")
55                return
56            breakpoint = target.BreakpointCreateByLocation(line_entry.GetFileSpec(), (line_entry.GetLine() - line_number))
57
58    if (not matched):
59        mo = re.match("^(.*):([0-9]+)$", linespec)
60        if (mo != None):
61            matched = True
62            #print "Matched <filename>:<linenum>"
63            file_name = mo.group(1)
64            line_number = int(mo.group(2))
65            breakpoint = target.BreakpointCreateByLocation(file_name, line_number)
66
67    if (not matched):
68        mo = re.match("\*((0x)?([0-9a-f]+))$", linespec)
69        if (mo != None):
70            matched = True
71            #print "Matched <address-expression>"
72            address = long(mo.group(1), base=0)
73            breakpoint = target.BreakpointCreateByAddress(address)
74
75    if (not matched):
76        #print "Trying <function-name>"
77        breakpoint = target.BreakpointCreateByName(linespec)
78
79    num_locations = breakpoint.GetNumLocations()
80
81    if (num_locations == 0):
82        result.AppendMessage("The line specification provided doesn't resolve to any addresses.")
83
84    addr_list = []
85
86    for location_index in range(num_locations):
87        location = breakpoint.GetLocationAtIndex(location_index)
88        addr_list.append(location.GetAddress())
89
90    target.BreakpointDelete(breakpoint.GetID())
91
92    return addr_list
93
94def usage_string():
95    return """   Sets the program counter to a specific address.
96
97Syntax: jump <linespec> [<location-id>]
98
99Command Options Usage:
100  jump <linenum>
101  jump +<count>
102  jump -<count>
103  jump <filename>:<linenum>
104  jump <function-name>
105  jump *<address-expression>
106
107<location-id> serves to disambiguate when multiple locations could be meant."""
108
109def jump (debugger, command, result, internal_dict):
110    if (command == ""):
111        result.AppendMessage(usage_string())
112
113    args = command.split()
114
115    if not debugger.IsValid():
116        result.AppendMessage("Invalid debugger!")
117        return
118
119    target = debugger.GetSelectedTarget()
120    if not target.IsValid():
121        result.AppendMessage("jump requires a valid target.")
122        return
123
124    process = target.GetProcess()
125    if not process.IsValid():
126        result.AppendMessage("jump requires a valid process.")
127        return
128
129    thread = process.GetSelectedThread()
130    if not thread.IsValid():
131        result.AppendMessage("jump requires a valid thread.")
132        return
133
134    frame = thread.GetSelectedFrame()
135    if not frame.IsValid():
136        result.AppendMessage("jump requires a valid frame.")
137        return
138
139    addresses = parse_linespec(args[0], frame, result)
140
141    stream = lldb.SBStream()
142
143    if len(addresses) == 0:
144        return
145
146    desired_address = addresses[0]
147
148    if len(addresses) > 1:
149        if len(args) == 2:
150            desired_index = int(args[1])
151            if (desired_index >= 0) and (desired_index < len(addresses)):
152                desired_address = addresses[desired_index]
153            else:
154                result.AppendMessage("Desired index " + args[1] + " is not one of the options.")
155                return
156        else:
157            index = 0
158            result.AppendMessage("The specified location resolves to multiple targets.");
159            for address in addresses:
160                stream.Clear()
161                address.GetDescription(stream)
162                result.AppendMessage("  Location ID " + str(index) + ": " + stream.GetData())
163                index = index + 1
164            result.AppendMessage("Please type 'jump " + command + " <location-id>' to choose one.")
165            return
166
167    frame.SetPC(desired_address.GetLoadAddress(target))
168
169if lldb.debugger:
170    # Module is being run inside the LLDB interpreter
171    jump.__doc__ = usage_string()
172    lldb.debugger.HandleCommand('command script add -f jump.jump jump')
173    print 'The "jump" command has been installed, type "help jump" or "jump <ENTER>" for detailed help.'
174