xref: /llvm-project/lldb/utils/lui/sourcewin.py (revision 602e47c5f9fd2e14c7bfb6111e6558fa0d27c87f)
193f50594SJonas Devlieghere##===-- sourcewin.py -----------------------------------------*- Python -*-===##
293f50594SJonas Devlieghere##
393f50594SJonas Devlieghere# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
493f50594SJonas Devlieghere# See https://llvm.org/LICENSE.txt for license information.
593f50594SJonas Devlieghere# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
693f50594SJonas Devlieghere##
793f50594SJonas Devlieghere##===----------------------------------------------------------------------===##
893f50594SJonas Devlieghere
993f50594SJonas Devlieghereimport cui
1093f50594SJonas Devlieghereimport curses
1193f50594SJonas Devlieghereimport lldb
1293f50594SJonas Devlieghereimport lldbutil
1393f50594SJonas Devlieghereimport re
1493f50594SJonas Devlieghereimport os
1593f50594SJonas Devlieghere
1693f50594SJonas Devlieghere
1793f50594SJonas Devlieghereclass SourceWin(cui.TitledWin):
1893f50594SJonas Devlieghere    def __init__(self, driver, x, y, w, h):
1993f50594SJonas Devlieghere        super(SourceWin, self).__init__(x, y, w, h, "Source")
2093f50594SJonas Devlieghere        self.sourceman = driver.getSourceManager()
2193f50594SJonas Devlieghere        self.sources = {}
2293f50594SJonas Devlieghere
2393f50594SJonas Devlieghere        self.filename = None
2493f50594SJonas Devlieghere        self.pc_line = None
2593f50594SJonas Devlieghere        self.viewline = 0
2693f50594SJonas Devlieghere
2793f50594SJonas Devlieghere        self.breakpoints = {}
2893f50594SJonas Devlieghere
2993f50594SJonas Devlieghere        self.win.scrollok(1)
3093f50594SJonas Devlieghere
3193f50594SJonas Devlieghere        self.markerPC = ":) "
3293f50594SJonas Devlieghere        self.markerBP = "B> "
3393f50594SJonas Devlieghere        self.markerNone = "   "
3493f50594SJonas Devlieghere
3593f50594SJonas Devlieghere        try:
3693f50594SJonas Devlieghere            from pygments.formatters import TerminalFormatter
37*602e47c5SDavid Spickett
3893f50594SJonas Devlieghere            self.formatter = TerminalFormatter()
3993f50594SJonas Devlieghere        except ImportError:
4093f50594SJonas Devlieghere            # self.win.addstr("\nWarning: no 'pygments' library found. Syntax highlighting is disabled.")
4193f50594SJonas Devlieghere            self.lexer = None
4293f50594SJonas Devlieghere            self.formatter = None
4393f50594SJonas Devlieghere            pass
4493f50594SJonas Devlieghere
4593f50594SJonas Devlieghere        # FIXME: syntax highlight broken
4693f50594SJonas Devlieghere        self.formatter = None
4793f50594SJonas Devlieghere        self.lexer = None
4893f50594SJonas Devlieghere
4993f50594SJonas Devlieghere    def handleEvent(self, event):
5093f50594SJonas Devlieghere        if isinstance(event, int):
5193f50594SJonas Devlieghere            self.handleKey(event)
5293f50594SJonas Devlieghere            return
5393f50594SJonas Devlieghere
5493f50594SJonas Devlieghere        if isinstance(event, lldb.SBEvent):
5593f50594SJonas Devlieghere            if lldb.SBBreakpoint.EventIsBreakpointEvent(event):
5693f50594SJonas Devlieghere                self.handleBPEvent(event)
5793f50594SJonas Devlieghere
58*602e47c5SDavid Spickett            if lldb.SBProcess.EventIsProcessEvent(
59*602e47c5SDavid Spickett                event
60*602e47c5SDavid Spickett            ) and not lldb.SBProcess.GetRestartedFromEvent(event):
6193f50594SJonas Devlieghere                process = lldb.SBProcess.GetProcessFromEvent(event)
6293f50594SJonas Devlieghere                if not process.IsValid():
6393f50594SJonas Devlieghere                    return
6493f50594SJonas Devlieghere                if process.GetState() == lldb.eStateStopped:
6593f50594SJonas Devlieghere                    self.refreshSource(process)
6693f50594SJonas Devlieghere                elif process.GetState() == lldb.eStateExited:
6793f50594SJonas Devlieghere                    self.notifyExited(process)
6893f50594SJonas Devlieghere
6993f50594SJonas Devlieghere    def notifyExited(self, process):
7093f50594SJonas Devlieghere        self.win.erase()
7193f50594SJonas Devlieghere        target = lldbutil.get_description(process.GetTarget())
7293f50594SJonas Devlieghere        pid = process.GetProcessID()
7393f50594SJonas Devlieghere        ec = process.GetExitStatus()
7493f50594SJonas Devlieghere        self.win.addstr(
75*602e47c5SDavid Spickett            "\nProcess %s [%d] has exited with exit-code %d" % (target, pid, ec)
76*602e47c5SDavid Spickett        )
7793f50594SJonas Devlieghere
7893f50594SJonas Devlieghere    def pageUp(self):
7993f50594SJonas Devlieghere        if self.viewline > 0:
8093f50594SJonas Devlieghere            self.viewline = self.viewline - 1
8193f50594SJonas Devlieghere            self.refreshSource()
8293f50594SJonas Devlieghere
8393f50594SJonas Devlieghere    def pageDown(self):
8493f50594SJonas Devlieghere        if self.viewline < len(self.content) - self.height + 1:
8593f50594SJonas Devlieghere            self.viewline = self.viewline + 1
8693f50594SJonas Devlieghere            self.refreshSource()
8793f50594SJonas Devlieghere        pass
8893f50594SJonas Devlieghere
8993f50594SJonas Devlieghere    def handleKey(self, key):
9093f50594SJonas Devlieghere        if key == curses.KEY_DOWN:
9193f50594SJonas Devlieghere            self.pageDown()
9293f50594SJonas Devlieghere        elif key == curses.KEY_UP:
9393f50594SJonas Devlieghere            self.pageUp()
9493f50594SJonas Devlieghere
9593f50594SJonas Devlieghere    def updateViewline(self):
9693f50594SJonas Devlieghere        half = self.height / 2
9793f50594SJonas Devlieghere        if self.pc_line < half:
9893f50594SJonas Devlieghere            self.viewline = 0
9993f50594SJonas Devlieghere        else:
10093f50594SJonas Devlieghere            self.viewline = self.pc_line - half + 1
10193f50594SJonas Devlieghere
10293f50594SJonas Devlieghere        if self.viewline < 0:
10393f50594SJonas Devlieghere            raise Exception(
104*602e47c5SDavid Spickett                "negative viewline: pc=%d viewline=%d" % (self.pc_line, self.viewline)
105*602e47c5SDavid Spickett            )
10693f50594SJonas Devlieghere
10793f50594SJonas Devlieghere    def refreshSource(self, process=None):
10893f50594SJonas Devlieghere        (self.height, self.width) = self.win.getmaxyx()
10993f50594SJonas Devlieghere
11093f50594SJonas Devlieghere        if process is not None:
11193f50594SJonas Devlieghere            loc = process.GetSelectedThread().GetSelectedFrame().GetLineEntry()
11293f50594SJonas Devlieghere            f = loc.GetFileSpec()
11393f50594SJonas Devlieghere            self.pc_line = loc.GetLine()
11493f50594SJonas Devlieghere
11593f50594SJonas Devlieghere            if not f.IsValid():
11693f50594SJonas Devlieghere                self.win.addstr(0, 0, "Invalid source file")
11793f50594SJonas Devlieghere                return
11893f50594SJonas Devlieghere
11993f50594SJonas Devlieghere            self.filename = f.GetFilename()
12093f50594SJonas Devlieghere            path = os.path.join(f.GetDirectory(), self.filename)
12193f50594SJonas Devlieghere            self.setTitle(path)
12293f50594SJonas Devlieghere            self.content = self.getContent(path)
12393f50594SJonas Devlieghere            self.updateViewline()
12493f50594SJonas Devlieghere
12593f50594SJonas Devlieghere        if self.filename is None:
12693f50594SJonas Devlieghere            return
12793f50594SJonas Devlieghere
12893f50594SJonas Devlieghere        if self.formatter is not None:
12993f50594SJonas Devlieghere            from pygments.lexers import get_lexer_for_filename
130*602e47c5SDavid Spickett
13193f50594SJonas Devlieghere            self.lexer = get_lexer_for_filename(self.filename)
13293f50594SJonas Devlieghere
133*602e47c5SDavid Spickett        bps = (
134*602e47c5SDavid Spickett            []
135*602e47c5SDavid Spickett            if not self.filename in self.breakpoints
136*602e47c5SDavid Spickett            else self.breakpoints[self.filename]
137*602e47c5SDavid Spickett        )
13893f50594SJonas Devlieghere        self.win.erase()
13993f50594SJonas Devlieghere        if self.content:
14093f50594SJonas Devlieghere            self.formatContent(self.content, self.pc_line, bps)
14193f50594SJonas Devlieghere
14293f50594SJonas Devlieghere    def getContent(self, path):
14393f50594SJonas Devlieghere        content = []
14493f50594SJonas Devlieghere        if path in self.sources:
14593f50594SJonas Devlieghere            content = self.sources[path]
14693f50594SJonas Devlieghere        else:
14793f50594SJonas Devlieghere            if os.path.exists(path):
14893f50594SJonas Devlieghere                with open(path) as x:
14993f50594SJonas Devlieghere                    content = x.readlines()
15093f50594SJonas Devlieghere                self.sources[path] = content
15193f50594SJonas Devlieghere        return content
15293f50594SJonas Devlieghere
15393f50594SJonas Devlieghere    def formatContent(self, content, pc_line, breakpoints):
15493f50594SJonas Devlieghere        source = ""
15593f50594SJonas Devlieghere        count = 1
15693f50594SJonas Devlieghere        self.win.erase()
15793f50594SJonas Devlieghere        end = min(len(content), self.viewline + self.height)
15893f50594SJonas Devlieghere        for i in range(self.viewline, end):
15993f50594SJonas Devlieghere            line_num = i + 1
16093f50594SJonas Devlieghere            marker = self.markerNone
16193f50594SJonas Devlieghere            attr = curses.A_NORMAL
16293f50594SJonas Devlieghere            if line_num == pc_line:
16393f50594SJonas Devlieghere                attr = curses.A_REVERSE
16493f50594SJonas Devlieghere            if line_num in breakpoints:
16593f50594SJonas Devlieghere                marker = self.markerBP
16693f50594SJonas Devlieghere            line = "%s%3d %s" % (marker, line_num, self.highlight(content[i]))
16793f50594SJonas Devlieghere            if len(line) >= self.width:
16893f50594SJonas Devlieghere                line = line[0 : self.width - 1] + "\n"
16993f50594SJonas Devlieghere            self.win.addstr(line, attr)
17093f50594SJonas Devlieghere            source += line
17193f50594SJonas Devlieghere            count = count + 1
17293f50594SJonas Devlieghere        return source
17393f50594SJonas Devlieghere
17493f50594SJonas Devlieghere    def highlight(self, source):
17593f50594SJonas Devlieghere        if self.lexer and self.formatter:
17693f50594SJonas Devlieghere            from pygments import highlight
177*602e47c5SDavid Spickett
17893f50594SJonas Devlieghere            return highlight(source, self.lexer, self.formatter)
17993f50594SJonas Devlieghere        else:
18093f50594SJonas Devlieghere            return source
18193f50594SJonas Devlieghere
18293f50594SJonas Devlieghere    def addBPLocations(self, locations):
18393f50594SJonas Devlieghere        for path in locations:
18493f50594SJonas Devlieghere            lines = locations[path]
18593f50594SJonas Devlieghere            if path in self.breakpoints:
18693f50594SJonas Devlieghere                self.breakpoints[path].update(lines)
18793f50594SJonas Devlieghere            else:
18893f50594SJonas Devlieghere                self.breakpoints[path] = lines
18993f50594SJonas Devlieghere
19093f50594SJonas Devlieghere    def removeBPLocations(self, locations):
19193f50594SJonas Devlieghere        for path in locations:
19293f50594SJonas Devlieghere            lines = locations[path]
19393f50594SJonas Devlieghere            if path in self.breakpoints:
19493f50594SJonas Devlieghere                self.breakpoints[path].difference_update(lines)
19593f50594SJonas Devlieghere            else:
19693f50594SJonas Devlieghere                raise "Removing locations that were never added...no good"
19793f50594SJonas Devlieghere
19893f50594SJonas Devlieghere    def handleBPEvent(self, event):
19993f50594SJonas Devlieghere        def getLocations(event):
20093f50594SJonas Devlieghere            locs = {}
20193f50594SJonas Devlieghere
20293f50594SJonas Devlieghere            bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event)
20393f50594SJonas Devlieghere
20493f50594SJonas Devlieghere            if bp.IsInternal():
20593f50594SJonas Devlieghere                # don't show anything for internal breakpoints
20693f50594SJonas Devlieghere                return
20793f50594SJonas Devlieghere
20893f50594SJonas Devlieghere            for location in bp:
20993f50594SJonas Devlieghere                # hack! getting the LineEntry via SBBreakpointLocation.GetAddress.GetLineEntry does not work good for
21093f50594SJonas Devlieghere                # inlined frames, so we get the description (which does take
21193f50594SJonas Devlieghere                # into account inlined functions) and parse it.
212*602e47c5SDavid Spickett                desc = lldbutil.get_description(location, lldb.eDescriptionLevelFull)
213*602e47c5SDavid Spickett                match = re.search("at\ ([^:]+):([\d]+)", desc)
21493f50594SJonas Devlieghere                try:
21593f50594SJonas Devlieghere                    path = match.group(1)
21693f50594SJonas Devlieghere                    line = int(match.group(2).strip())
21793f50594SJonas Devlieghere                except ValueError as e:
21893f50594SJonas Devlieghere                    # bp loc unparsable
21993f50594SJonas Devlieghere                    continue
22093f50594SJonas Devlieghere
22193f50594SJonas Devlieghere                if path in locs:
22293f50594SJonas Devlieghere                    locs[path].add(line)
22393f50594SJonas Devlieghere                else:
22493f50594SJonas Devlieghere                    locs[path] = set([line])
22593f50594SJonas Devlieghere            return locs
22693f50594SJonas Devlieghere
22793f50594SJonas Devlieghere        event_type = lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event)
228*602e47c5SDavid Spickett        if (
229*602e47c5SDavid Spickett            event_type == lldb.eBreakpointEventTypeEnabled
230*602e47c5SDavid Spickett            or event_type == lldb.eBreakpointEventTypeAdded
231*602e47c5SDavid Spickett            or event_type == lldb.eBreakpointEventTypeLocationsResolved
232*602e47c5SDavid Spickett            or event_type == lldb.eBreakpointEventTypeLocationsAdded
233*602e47c5SDavid Spickett        ):
23493f50594SJonas Devlieghere            self.addBPLocations(getLocations(event))
235*602e47c5SDavid Spickett        elif (
236*602e47c5SDavid Spickett            event_type == lldb.eBreakpointEventTypeRemoved
237*602e47c5SDavid Spickett            or event_type == lldb.eBreakpointEventTypeLocationsRemoved
238*602e47c5SDavid Spickett            or event_type == lldb.eBreakpointEventTypeDisabled
239*602e47c5SDavid Spickett        ):
24093f50594SJonas Devlieghere            self.removeBPLocations(getLocations(event))
241*602e47c5SDavid Spickett        elif (
242*602e47c5SDavid Spickett            event_type == lldb.eBreakpointEventTypeCommandChanged
243*602e47c5SDavid Spickett            or event_type == lldb.eBreakpointEventTypeConditionChanged
244*602e47c5SDavid Spickett            or event_type == lldb.eBreakpointEventTypeIgnoreChanged
245*602e47c5SDavid Spickett            or event_type == lldb.eBreakpointEventTypeThreadChanged
246*602e47c5SDavid Spickett            or event_type == lldb.eBreakpointEventTypeInvalidType
247*602e47c5SDavid Spickett        ):
24893f50594SJonas Devlieghere            # no-op
24993f50594SJonas Devlieghere            pass
25093f50594SJonas Devlieghere        self.refreshSource()
251