251 lines · python
1##===-- sourcewin.py -----------------------------------------*- Python -*-===##2##3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4# See https://llvm.org/LICENSE.txt for license information.5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6##7##===----------------------------------------------------------------------===##8 9import cui10import curses11import lldb12import lldbutil13import re14import os15 16 17class SourceWin(cui.TitledWin):18 def __init__(self, driver, x, y, w, h):19 super(SourceWin, self).__init__(x, y, w, h, "Source")20 self.sourceman = driver.getSourceManager()21 self.sources = {}22 23 self.filename = None24 self.pc_line = None25 self.viewline = 026 27 self.breakpoints = {}28 29 self.win.scrollok(1)30 31 self.markerPC = ":) "32 self.markerBP = "B> "33 self.markerNone = " "34 35 try:36 from pygments.formatters import TerminalFormatter37 38 self.formatter = TerminalFormatter()39 except ImportError:40 # self.win.addstr("\nWarning: no 'pygments' library found. Syntax highlighting is disabled.")41 self.lexer = None42 self.formatter = None43 pass44 45 # FIXME: syntax highlight broken46 self.formatter = None47 self.lexer = None48 49 def handleEvent(self, event):50 if isinstance(event, int):51 self.handleKey(event)52 return53 54 if isinstance(event, lldb.SBEvent):55 if lldb.SBBreakpoint.EventIsBreakpointEvent(event):56 self.handleBPEvent(event)57 58 if lldb.SBProcess.EventIsProcessEvent(59 event60 ) and not lldb.SBProcess.GetRestartedFromEvent(event):61 process = lldb.SBProcess.GetProcessFromEvent(event)62 if not process.IsValid():63 return64 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" % (target, pid, ec)76 )77 78 def pageUp(self):79 if self.viewline > 0:80 self.viewline = self.viewline - 181 self.refreshSource()82 83 def pageDown(self):84 if self.viewline < len(self.content) - self.height + 1:85 self.viewline = self.viewline + 186 self.refreshSource()87 pass88 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 / 297 if self.pc_line < half:98 self.viewline = 099 else:100 self.viewline = self.pc_line - half + 1101 102 if self.viewline < 0:103 raise Exception(104 "negative viewline: pc=%d viewline=%d" % (self.pc_line, self.viewline)105 )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 return118 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 return127 128 if self.formatter is not None:129 from pygments.lexers import get_lexer_for_filename130 131 self.lexer = get_lexer_for_filename(self.filename)132 133 bps = (134 []135 if not self.filename in self.breakpoints136 else self.breakpoints[self.filename]137 )138 self.win.erase()139 if self.content:140 self.formatContent(self.content, self.pc_line, bps)141 142 def getContent(self, path):143 content = []144 if path in self.sources:145 content = self.sources[path]146 else:147 if os.path.exists(path):148 with open(path) as x:149 content = x.readlines()150 self.sources[path] = content151 return content152 153 def formatContent(self, content, pc_line, breakpoints):154 source = ""155 count = 1156 self.win.erase()157 end = min(len(content), self.viewline + self.height)158 for i in range(self.viewline, end):159 line_num = i + 1160 marker = self.markerNone161 attr = curses.A_NORMAL162 if line_num == pc_line:163 attr = curses.A_REVERSE164 if line_num in breakpoints:165 marker = self.markerBP166 line = "%s%3d %s" % (marker, line_num, self.highlight(content[i]))167 if len(line) >= self.width:168 line = line[0 : self.width - 1] + "\n"169 self.win.addstr(line, attr)170 source += line171 count = count + 1172 return source173 174 def highlight(self, source):175 if self.lexer and self.formatter:176 from pygments import highlight177 178 return highlight(source, self.lexer, self.formatter)179 else:180 return source181 182 def addBPLocations(self, locations):183 for path in locations:184 lines = locations[path]185 if path in self.breakpoints:186 self.breakpoints[path].update(lines)187 else:188 self.breakpoints[path] = lines189 190 def removeBPLocations(self, locations):191 for path in locations:192 lines = locations[path]193 if path in self.breakpoints:194 self.breakpoints[path].difference_update(lines)195 else:196 raise "Removing locations that were never added...no good"197 198 def handleBPEvent(self, event):199 def getLocations(event):200 locs = {}201 202 bp = lldb.SBBreakpoint.GetBreakpointFromEvent(event)203 204 if bp.IsInternal():205 # don't show anything for internal breakpoints206 return207 208 for location in bp:209 # hack! getting the LineEntry via SBBreakpointLocation.GetAddress.GetLineEntry does not work good for210 # inlined frames, so we get the description (which does take211 # into account inlined functions) and parse it.212 desc = lldbutil.get_description(location, lldb.eDescriptionLevelFull)213 match = re.search(r"at\ ([^:]+):([\d]+)", desc)214 try:215 path = match.group(1)216 line = int(match.group(2).strip())217 except ValueError as e:218 # bp loc unparsable219 continue220 221 if path in locs:222 locs[path].add(line)223 else:224 locs[path] = set([line])225 return locs226 227 event_type = lldb.SBBreakpoint.GetBreakpointEventTypeFromEvent(event)228 if (229 event_type == lldb.eBreakpointEventTypeEnabled230 or event_type == lldb.eBreakpointEventTypeAdded231 or event_type == lldb.eBreakpointEventTypeLocationsResolved232 or event_type == lldb.eBreakpointEventTypeLocationsAdded233 ):234 self.addBPLocations(getLocations(event))235 elif (236 event_type == lldb.eBreakpointEventTypeRemoved237 or event_type == lldb.eBreakpointEventTypeLocationsRemoved238 or event_type == lldb.eBreakpointEventTypeDisabled239 ):240 self.removeBPLocations(getLocations(event))241 elif (242 event_type == lldb.eBreakpointEventTypeCommandChanged243 or event_type == lldb.eBreakpointEventTypeConditionChanged244 or event_type == lldb.eBreakpointEventTypeIgnoreChanged245 or event_type == lldb.eBreakpointEventTypeThreadChanged246 or event_type == lldb.eBreakpointEventTypeInvalidType247 ):248 # no-op249 pass250 self.refreshSource()251