1*061da546Spatrick##===-- sourcewin.py -----------------------------------------*- Python -*-===## 2*061da546Spatrick## 3*061da546Spatrick# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*061da546Spatrick# See https://llvm.org/LICENSE.txt for license information. 5*061da546Spatrick# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*061da546Spatrick## 7*061da546Spatrick##===----------------------------------------------------------------------===## 8*061da546Spatrick 9*061da546Spatrickimport cui 10*061da546Spatrickimport curses 11*061da546Spatrickimport lldb 12*061da546Spatrickimport lldbutil 13*061da546Spatrickimport re 14*061da546Spatrickimport os 15*061da546Spatrick 16*061da546Spatrick 17*061da546Spatrickclass SourceWin(cui.TitledWin): 18*061da546Spatrick 19*061da546Spatrick def __init__(self, driver, x, y, w, h): 20*061da546Spatrick super(SourceWin, self).__init__(x, y, w, h, "Source") 21*061da546Spatrick self.sourceman = driver.getSourceManager() 22*061da546Spatrick self.sources = {} 23*061da546Spatrick 24*061da546Spatrick self.filename = None 25*061da546Spatrick self.pc_line = None 26*061da546Spatrick self.viewline = 0 27*061da546Spatrick 28*061da546Spatrick self.breakpoints = {} 29*061da546Spatrick 30*061da546Spatrick self.win.scrollok(1) 31*061da546Spatrick 32*061da546Spatrick self.markerPC = ":) " 33*061da546Spatrick self.markerBP = "B> " 34*061da546Spatrick self.markerNone = " " 35*061da546Spatrick 36*061da546Spatrick try: 37*061da546Spatrick from pygments.formatters import TerminalFormatter 38*061da546Spatrick self.formatter = TerminalFormatter() 39*061da546Spatrick except ImportError: 40*061da546Spatrick #self.win.addstr("\nWarning: no 'pygments' library found. Syntax highlighting is disabled.") 41*061da546Spatrick self.lexer = None 42*061da546Spatrick self.formatter = None 43*061da546Spatrick pass 44*061da546Spatrick 45*061da546Spatrick # FIXME: syntax highlight broken 46*061da546Spatrick self.formatter = None 47*061da546Spatrick self.lexer = None 48*061da546Spatrick 49*061da546Spatrick def handleEvent(self, event): 50*061da546Spatrick if isinstance(event, int): 51*061da546Spatrick self.handleKey(event) 52*061da546Spatrick return 53*061da546Spatrick 54*061da546Spatrick if isinstance(event, lldb.SBEvent): 55*061da546Spatrick if lldb.SBBreakpoint.EventIsBreakpointEvent(event): 56*061da546Spatrick self.handleBPEvent(event) 57*061da546Spatrick 58*061da546Spatrick if lldb.SBProcess.EventIsProcessEvent(event) and \ 59*061da546Spatrick not lldb.SBProcess.GetRestartedFromEvent(event): 60*061da546Spatrick process = lldb.SBProcess.GetProcessFromEvent(event) 61*061da546Spatrick if not process.IsValid(): 62*061da546Spatrick return 63*061da546Spatrick if process.GetState() == lldb.eStateStopped: 64*061da546Spatrick self.refreshSource(process) 65*061da546Spatrick elif process.GetState() == lldb.eStateExited: 66*061da546Spatrick self.notifyExited(process) 67*061da546Spatrick 68*061da546Spatrick def notifyExited(self, process): 69*061da546Spatrick self.win.erase() 70*061da546Spatrick target = lldbutil.get_description(process.GetTarget()) 71*061da546Spatrick pid = process.GetProcessID() 72*061da546Spatrick ec = process.GetExitStatus() 73*061da546Spatrick self.win.addstr( 74*061da546Spatrick "\nProcess %s [%d] has exited with exit-code %d" % 75*061da546Spatrick (target, pid, ec)) 76*061da546Spatrick 77*061da546Spatrick def pageUp(self): 78*061da546Spatrick if self.viewline > 0: 79*061da546Spatrick self.viewline = self.viewline - 1 80*061da546Spatrick self.refreshSource() 81*061da546Spatrick 82*061da546Spatrick def pageDown(self): 83*061da546Spatrick if self.viewline < len(self.content) - self.height + 1: 84*061da546Spatrick self.viewline = self.viewline + 1 85*061da546Spatrick self.refreshSource() 86*061da546Spatrick pass 87*061da546Spatrick 88*061da546Spatrick def handleKey(self, key): 89*061da546Spatrick if key == curses.KEY_DOWN: 90*061da546Spatrick self.pageDown() 91*061da546Spatrick elif key == curses.KEY_UP: 92*061da546Spatrick self.pageUp() 93*061da546Spatrick 94*061da546Spatrick def updateViewline(self): 95*061da546Spatrick half = self.height / 2 96*061da546Spatrick if self.pc_line < half: 97*061da546Spatrick self.viewline = 0 98*061da546Spatrick else: 99*061da546Spatrick self.viewline = self.pc_line - half + 1 100*061da546Spatrick 101*061da546Spatrick if self.viewline < 0: 102*061da546Spatrick raise Exception( 103*061da546Spatrick "negative viewline: pc=%d viewline=%d" % 104*061da546Spatrick (self.pc_line, self.viewline)) 105*061da546Spatrick 106*061da546Spatrick def refreshSource(self, process=None): 107*061da546Spatrick (self.height, self.width) = self.win.getmaxyx() 108*061da546Spatrick 109*061da546Spatrick if process is not None: 110*061da546Spatrick loc = process.GetSelectedThread().GetSelectedFrame().GetLineEntry() 111*061da546Spatrick f = loc.GetFileSpec() 112*061da546Spatrick self.pc_line = loc.GetLine() 113*061da546Spatrick 114*061da546Spatrick if not f.IsValid(): 115*061da546Spatrick self.win.addstr(0, 0, "Invalid source file") 116*061da546Spatrick return 117*061da546Spatrick 118*061da546Spatrick self.filename = f.GetFilename() 119*061da546Spatrick path = os.path.join(f.GetDirectory(), self.filename) 120*061da546Spatrick self.setTitle(path) 121*061da546Spatrick self.content = self.getContent(path) 122*061da546Spatrick self.updateViewline() 123*061da546Spatrick 124*061da546Spatrick if self.filename is None: 125*061da546Spatrick return 126*061da546Spatrick 127*061da546Spatrick if self.formatter is not None: 128*061da546Spatrick from pygments.lexers import get_lexer_for_filename 129*061da546Spatrick self.lexer = get_lexer_for_filename(self.filename) 130*061da546Spatrick 131*061da546Spatrick bps = [] if not self.filename in self.breakpoints else self.breakpoints[self.filename] 132*061da546Spatrick self.win.erase() 133*061da546Spatrick if self.content: 134*061da546Spatrick self.formatContent(self.content, self.pc_line, bps) 135*061da546Spatrick 136*061da546Spatrick def getContent(self, path): 137*061da546Spatrick content = [] 138*061da546Spatrick if path in self.sources: 139*061da546Spatrick content = self.sources[path] 140*061da546Spatrick else: 141*061da546Spatrick if os.path.exists(path): 142*061da546Spatrick with open(path) as x: 143*061da546Spatrick content = x.readlines() 144*061da546Spatrick self.sources[path] = content 145*061da546Spatrick return content 146*061da546Spatrick 147*061da546Spatrick def formatContent(self, content, pc_line, breakpoints): 148*061da546Spatrick source = "" 149*061da546Spatrick count = 1 150*061da546Spatrick self.win.erase() 151*061da546Spatrick end = min(len(content), self.viewline + self.height) 152*061da546Spatrick for i in range(self.viewline, end): 153*061da546Spatrick line_num = i + 1 154*061da546Spatrick marker = self.markerNone 155*061da546Spatrick attr = curses.A_NORMAL 156*061da546Spatrick if line_num == pc_line: 157*061da546Spatrick attr = curses.A_REVERSE 158*061da546Spatrick if line_num in breakpoints: 159*061da546Spatrick marker = self.markerBP 160*061da546Spatrick line = "%s%3d %s" % (marker, line_num, self.highlight(content[i])) 161*061da546Spatrick if len(line) >= self.width: 162*061da546Spatrick line = line[0:self.width - 1] + "\n" 163*061da546Spatrick self.win.addstr(line, attr) 164*061da546Spatrick source += line 165*061da546Spatrick count = count + 1 166*061da546Spatrick return source 167*061da546Spatrick 168*061da546Spatrick def highlight(self, source): 169*061da546Spatrick if self.lexer and self.formatter: 170*061da546Spatrick from pygments import highlight 171*061da546Spatrick return highlight(source, self.lexer, self.formatter) 172*061da546Spatrick else: 173*061da546Spatrick return source 174*061da546Spatrick 175*061da546Spatrick def addBPLocations(self, locations): 176*061da546Spatrick for path in locations: 177*061da546Spatrick lines = locations[path] 178*061da546Spatrick if path in self.breakpoints: 179*061da546Spatrick self.breakpoints[path].update(lines) 180*061da546Spatrick else: 181*061da546Spatrick self.breakpoints[path] = lines 182*061da546Spatrick 183*061da546Spatrick def removeBPLocations(self, locations): 184*061da546Spatrick for path in locations: 185*061da546Spatrick lines = locations[path] 186*061da546Spatrick if path in self.breakpoints: 187*061da546Spatrick self.breakpoints[path].difference_update(lines) 188*061da546Spatrick else: 189*061da546Spatrick raise "Removing locations that were never added...no good" 190*061da546Spatrick 191*061da546Spatrick def handleBPEvent(self, event): 192*061da546Spatrick def getLocations(event): 193*061da546Spatrick locs = {} 194*061da546Spatrick 195*061da546Spatrick bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event) 196*061da546Spatrick 197*061da546Spatrick if bp.IsInternal(): 198*061da546Spatrick # don't show anything for internal breakpoints 199*061da546Spatrick return 200*061da546Spatrick 201*061da546Spatrick for location in bp: 202*061da546Spatrick # hack! getting the LineEntry via SBBreakpointLocation.GetAddress.GetLineEntry does not work good for 203*061da546Spatrick # inlined frames, so we get the description (which does take 204*061da546Spatrick # into account inlined functions) and parse it. 205*061da546Spatrick desc = lldbutil.get_description( 206*061da546Spatrick location, lldb.eDescriptionLevelFull) 207*061da546Spatrick match = re.search('at\ ([^:]+):([\d]+)', desc) 208*061da546Spatrick try: 209*061da546Spatrick path = match.group(1) 210*061da546Spatrick line = int(match.group(2).strip()) 211*061da546Spatrick except ValueError as e: 212*061da546Spatrick # bp loc unparsable 213*061da546Spatrick continue 214*061da546Spatrick 215*061da546Spatrick if path in locs: 216*061da546Spatrick locs[path].add(line) 217*061da546Spatrick else: 218*061da546Spatrick locs[path] = set([line]) 219*061da546Spatrick return locs 220*061da546Spatrick 221*061da546Spatrick event_type = lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event) 222*061da546Spatrick if event_type == lldb.eBreakpointEventTypeEnabled \ 223*061da546Spatrick or event_type == lldb.eBreakpointEventTypeAdded \ 224*061da546Spatrick or event_type == lldb.eBreakpointEventTypeLocationsResolved \ 225*061da546Spatrick or event_type == lldb.eBreakpointEventTypeLocationsAdded: 226*061da546Spatrick self.addBPLocations(getLocations(event)) 227*061da546Spatrick elif event_type == lldb.eBreakpointEventTypeRemoved \ 228*061da546Spatrick or event_type == lldb.eBreakpointEventTypeLocationsRemoved \ 229*061da546Spatrick or event_type == lldb.eBreakpointEventTypeDisabled: 230*061da546Spatrick self.removeBPLocations(getLocations(event)) 231*061da546Spatrick elif event_type == lldb.eBreakpointEventTypeCommandChanged \ 232*061da546Spatrick or event_type == lldb.eBreakpointEventTypeConditionChanged \ 233*061da546Spatrick or event_type == lldb.eBreakpointEventTypeIgnoreChanged \ 234*061da546Spatrick or event_type == lldb.eBreakpointEventTypeThreadChanged \ 235*061da546Spatrick or event_type == lldb.eBreakpointEventTypeInvalidType: 236*061da546Spatrick # no-op 237*061da546Spatrick pass 238*061da546Spatrick self.refreshSource() 239