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