1525cd59fSSerge Gueltonfrom __future__ import print_function
2525cd59fSSerge Guelton
3b9c1b51eSKate Stoneimport lldb
4b9c1b51eSKate Stoneimport re
5b9c1b51eSKate Stone
660e390cbSSean Callanan
760e390cbSSean Callanandef parse_linespec(linespec, frame, result):
860e390cbSSean Callanan    """Handles a subset of GDB-style linespecs.  Specifically:
960e390cbSSean Callanan
1060e390cbSSean Callanan       number           - A line in the current file
1160e390cbSSean Callanan       +offset          - The line /offset/ lines after this line
1260e390cbSSean Callanan       -offset          - The line /offset/ lines before this line
1360e390cbSSean Callanan       filename:number  - Line /number/ in file /filename/
1460e390cbSSean Callanan       function         - The start of /function/
1560e390cbSSean Callanan       *address         - The pointer target of /address/, which must be a literal (but see `` in LLDB)
1660e390cbSSean Callanan
1760e390cbSSean Callanan       We explicitly do not handle filename:function because it is ambiguous in Objective-C.
1860e390cbSSean Callanan
1960e390cbSSean Callanan       This function returns a list of addresses."""
2060e390cbSSean Callanan
2160e390cbSSean Callanan    breakpoint = None
2260e390cbSSean Callanan    target = frame.GetThread().GetProcess().GetTarget()
2360e390cbSSean Callanan
2460e390cbSSean Callanan    matched = False
2560e390cbSSean Callanan
2660e390cbSSean Callanan    if (not matched):
2760e390cbSSean Callanan        mo = re.match("^([0-9]+)$", linespec)
28b9c1b51eSKate Stone        if (mo is not None):
2960e390cbSSean Callanan            matched = True
3060e390cbSSean Callanan            # print "Matched <linenum>"
3160e390cbSSean Callanan            line_number = int(mo.group(1))
3260e390cbSSean Callanan            line_entry = frame.GetLineEntry()
3360e390cbSSean Callanan            if not line_entry.IsValid():
34b9c1b51eSKate Stone                result.AppendMessage(
35b9c1b51eSKate Stone                    "Specified a line in the current file, but the current frame doesn't have line table information.")
3660e390cbSSean Callanan                return
37b9c1b51eSKate Stone            breakpoint = target.BreakpointCreateByLocation(
38b9c1b51eSKate Stone                line_entry.GetFileSpec(), line_number)
3960e390cbSSean Callanan
4060e390cbSSean Callanan    if (not matched):
4160e390cbSSean Callanan        mo = re.match("^\+([0-9]+)$", linespec)
42b9c1b51eSKate Stone        if (mo is not None):
4360e390cbSSean Callanan            matched = True
4460e390cbSSean Callanan            # print "Matched +<count>"
4560e390cbSSean Callanan            line_number = int(mo.group(1))
4660e390cbSSean Callanan            line_entry = frame.GetLineEntry()
4760e390cbSSean Callanan            if not line_entry.IsValid():
48b9c1b51eSKate Stone                result.AppendMessage(
49b9c1b51eSKate Stone                    "Specified a line in the current file, but the current frame doesn't have line table information.")
5060e390cbSSean Callanan                return
51b9c1b51eSKate Stone            breakpoint = target.BreakpointCreateByLocation(
52b9c1b51eSKate Stone                line_entry.GetFileSpec(), (line_entry.GetLine() + line_number))
5360e390cbSSean Callanan
5460e390cbSSean Callanan    if (not matched):
5560e390cbSSean Callanan        mo = re.match("^\-([0-9]+)$", linespec)
56b9c1b51eSKate Stone        if (mo is not None):
5760e390cbSSean Callanan            matched = True
5860e390cbSSean Callanan            # print "Matched -<count>"
5960e390cbSSean Callanan            line_number = int(mo.group(1))
6060e390cbSSean Callanan            line_entry = frame.GetLineEntry()
6160e390cbSSean Callanan            if not line_entry.IsValid():
62b9c1b51eSKate Stone                result.AppendMessage(
63b9c1b51eSKate Stone                    "Specified a line in the current file, but the current frame doesn't have line table information.")
6460e390cbSSean Callanan                return
65b9c1b51eSKate Stone            breakpoint = target.BreakpointCreateByLocation(
66b9c1b51eSKate Stone                line_entry.GetFileSpec(), (line_entry.GetLine() - line_number))
6760e390cbSSean Callanan
6860e390cbSSean Callanan    if (not matched):
6960e390cbSSean Callanan        mo = re.match("^(.*):([0-9]+)$", linespec)
70b9c1b51eSKate Stone        if (mo is not None):
7160e390cbSSean Callanan            matched = True
7260e390cbSSean Callanan            # print "Matched <filename>:<linenum>"
7360e390cbSSean Callanan            file_name = mo.group(1)
7460e390cbSSean Callanan            line_number = int(mo.group(2))
75b9c1b51eSKate Stone            breakpoint = target.BreakpointCreateByLocation(
76b9c1b51eSKate Stone                file_name, line_number)
7760e390cbSSean Callanan
7860e390cbSSean Callanan    if (not matched):
7960e390cbSSean Callanan        mo = re.match("\*((0x)?([0-9a-f]+))$", linespec)
80b9c1b51eSKate Stone        if (mo is not None):
8160e390cbSSean Callanan            matched = True
8260e390cbSSean Callanan            # print "Matched <address-expression>"
83bfb77918SSerge Guelton            address = int(mo.group(1), base=0)
8460e390cbSSean Callanan            breakpoint = target.BreakpointCreateByAddress(address)
8560e390cbSSean Callanan
8660e390cbSSean Callanan    if (not matched):
8760e390cbSSean Callanan        # print "Trying <function-name>"
8860e390cbSSean Callanan        breakpoint = target.BreakpointCreateByName(linespec)
8960e390cbSSean Callanan
9060e390cbSSean Callanan    num_locations = breakpoint.GetNumLocations()
9160e390cbSSean Callanan
9260e390cbSSean Callanan    if (num_locations == 0):
93b9c1b51eSKate Stone        result.AppendMessage(
94b9c1b51eSKate Stone            "The line specification provided doesn't resolve to any addresses.")
9560e390cbSSean Callanan
9660e390cbSSean Callanan    addr_list = []
9760e390cbSSean Callanan
9860e390cbSSean Callanan    for location_index in range(num_locations):
9960e390cbSSean Callanan        location = breakpoint.GetLocationAtIndex(location_index)
10060e390cbSSean Callanan        addr_list.append(location.GetAddress())
10160e390cbSSean Callanan
10260e390cbSSean Callanan    target.BreakpointDelete(breakpoint.GetID())
10360e390cbSSean Callanan
10460e390cbSSean Callanan    return addr_list
10560e390cbSSean Callanan
106b9c1b51eSKate Stone
10760e390cbSSean Callanandef usage_string():
10860e390cbSSean Callanan    return """   Sets the program counter to a specific address.
10960e390cbSSean Callanan
11060e390cbSSean CallananSyntax: jump <linespec> [<location-id>]
11160e390cbSSean Callanan
11260e390cbSSean CallananCommand Options Usage:
11360e390cbSSean Callanan  jump <linenum>
11460e390cbSSean Callanan  jump +<count>
11560e390cbSSean Callanan  jump -<count>
11660e390cbSSean Callanan  jump <filename>:<linenum>
11760e390cbSSean Callanan  jump <function-name>
11860e390cbSSean Callanan  jump *<address-expression>
11960e390cbSSean Callanan
12060e390cbSSean Callanan<location-id> serves to disambiguate when multiple locations could be meant."""
12160e390cbSSean Callanan
122b9c1b51eSKate Stone
12360e390cbSSean Callanandef jump(debugger, command, result, internal_dict):
12460e390cbSSean Callanan    if (command == ""):
12560e390cbSSean Callanan        result.AppendMessage(usage_string())
12660e390cbSSean Callanan
12760e390cbSSean Callanan    args = command.split()
12860e390cbSSean Callanan
12960e390cbSSean Callanan    if not debugger.IsValid():
13060e390cbSSean Callanan        result.AppendMessage("Invalid debugger!")
13160e390cbSSean Callanan        return
13260e390cbSSean Callanan
13360e390cbSSean Callanan    target = debugger.GetSelectedTarget()
13460e390cbSSean Callanan    if not target.IsValid():
13560e390cbSSean Callanan        result.AppendMessage("jump requires a valid target.")
13660e390cbSSean Callanan        return
13760e390cbSSean Callanan
13860e390cbSSean Callanan    process = target.GetProcess()
13960e390cbSSean Callanan    if not process.IsValid():
14060e390cbSSean Callanan        result.AppendMessage("jump requires a valid process.")
14160e390cbSSean Callanan        return
14260e390cbSSean Callanan
14360e390cbSSean Callanan    thread = process.GetSelectedThread()
14460e390cbSSean Callanan    if not thread.IsValid():
14560e390cbSSean Callanan        result.AppendMessage("jump requires a valid thread.")
14660e390cbSSean Callanan        return
14760e390cbSSean Callanan
14860e390cbSSean Callanan    frame = thread.GetSelectedFrame()
14960e390cbSSean Callanan    if not frame.IsValid():
15060e390cbSSean Callanan        result.AppendMessage("jump requires a valid frame.")
15160e390cbSSean Callanan        return
15260e390cbSSean Callanan
15360e390cbSSean Callanan    addresses = parse_linespec(args[0], frame, result)
15460e390cbSSean Callanan
15560e390cbSSean Callanan    stream = lldb.SBStream()
15660e390cbSSean Callanan
15760e390cbSSean Callanan    if len(addresses) == 0:
15860e390cbSSean Callanan        return
15960e390cbSSean Callanan
16060e390cbSSean Callanan    desired_address = addresses[0]
16160e390cbSSean Callanan
16260e390cbSSean Callanan    if len(addresses) > 1:
16360e390cbSSean Callanan        if len(args) == 2:
16460e390cbSSean Callanan            desired_index = int(args[1])
16560e390cbSSean Callanan            if (desired_index >= 0) and (desired_index < len(addresses)):
16660e390cbSSean Callanan                desired_address = addresses[desired_index]
16760e390cbSSean Callanan            else:
168b9c1b51eSKate Stone                result.AppendMessage(
169b9c1b51eSKate Stone                    "Desired index " +
170b9c1b51eSKate Stone                    args[1] +
171b9c1b51eSKate Stone                    " is not one of the options.")
17260e390cbSSean Callanan                return
17360e390cbSSean Callanan        else:
17460e390cbSSean Callanan            index = 0
175b9c1b51eSKate Stone            result.AppendMessage(
176b9c1b51eSKate Stone                "The specified location resolves to multiple targets.")
17760e390cbSSean Callanan            for address in addresses:
17860e390cbSSean Callanan                stream.Clear()
17960e390cbSSean Callanan                address.GetDescription(stream)
180b9c1b51eSKate Stone                result.AppendMessage(
181b9c1b51eSKate Stone                    "  Location ID " +
182b9c1b51eSKate Stone                    str(index) +
183b9c1b51eSKate Stone                    ": " +
184b9c1b51eSKate Stone                    stream.GetData())
18560e390cbSSean Callanan                index = index + 1
186b9c1b51eSKate Stone            result.AppendMessage(
187b9c1b51eSKate Stone                "Please type 'jump " +
188b9c1b51eSKate Stone                command +
189b9c1b51eSKate Stone                " <location-id>' to choose one.")
19060e390cbSSean Callanan            return
19160e390cbSSean Callanan
19260e390cbSSean Callanan    frame.SetPC(desired_address.GetLoadAddress(target))
19360e390cbSSean Callanan
194*1441ffe6SDave Leedef __lldb_init_module(debugger, internal_dict):
19560e390cbSSean Callanan    # Module is being run inside the LLDB interpreter
19660e390cbSSean Callanan    jump.__doc__ = usage_string()
197*1441ffe6SDave Lee    debugger.HandleCommand('command script add -f jump.jump jump')
198525cd59fSSerge Guelton    print('The "jump" command has been installed, type "help jump" or "jump <ENTER>" for detailed help.')
199