summaryrefslogtreecommitdiffstats
path: root/lldb/utils/vim-lldb/python-vim-lldb
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/utils/vim-lldb/python-vim-lldb')
-rw-r--r--lldb/utils/vim-lldb/python-vim-lldb/import_lldb.py112
-rw-r--r--lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py741
-rw-r--r--lldb/utils/vim-lldb/python-vim-lldb/plugin.py16
-rw-r--r--lldb/utils/vim-lldb/python-vim-lldb/vim_panes.py987
-rw-r--r--lldb/utils/vim-lldb/python-vim-lldb/vim_signs.py134
-rw-r--r--lldb/utils/vim-lldb/python-vim-lldb/vim_ui.py462
6 files changed, 1286 insertions, 1166 deletions
diff --git a/lldb/utils/vim-lldb/python-vim-lldb/import_lldb.py b/lldb/utils/vim-lldb/python-vim-lldb/import_lldb.py
index a2145d50466..41cb42b1bb3 100644
--- a/lldb/utils/vim-lldb/python-vim-lldb/import_lldb.py
+++ b/lldb/utils/vim-lldb/python-vim-lldb/import_lldb.py
@@ -1,61 +1,71 @@
# Locate and load the lldb python module
-import os, sys
+import os
+import sys
+
def import_lldb():
- """ Find and import the lldb modules. This function tries to find the lldb module by:
- 1. Simply by doing "import lldb" in case the system python installation is aware of lldb. If that fails,
- 2. Executes the lldb executable pointed to by the LLDB environment variable (or if unset, the first lldb
- on PATH") with the -P flag to determine the PYTHONPATH to set. If the lldb executable returns a valid
- path, it is added to sys.path and the import is attempted again. If that fails, 3. On Mac OS X the
- default Xcode 4.5 installation path.
- """
-
- # Try simple 'import lldb', in case of a system-wide install or a pre-configured PYTHONPATH
- try:
- import lldb
- return True
- except ImportError:
- pass
-
- # Allow overriding default path to lldb executable with the LLDB environment variable
- lldb_executable = 'lldb'
- if 'LLDB' in os.environ and os.path.exists(os.environ['LLDB']):
- lldb_executable = os.environ['LLDB']
-
- # Try using builtin module location support ('lldb -P')
- from subprocess import check_output, CalledProcessError
- try:
- with open(os.devnull, 'w') as fnull:
- lldb_minus_p_path = check_output("%s -P" % lldb_executable, shell=True, stderr=fnull).strip()
- if not os.path.exists(lldb_minus_p_path):
- #lldb -P returned invalid path, probably too old
- pass
- else:
- sys.path.append(lldb_minus_p_path)
- import lldb
- return True
- except CalledProcessError:
- # Cannot run 'lldb -P' to determine location of lldb python module
- pass
- except ImportError:
- # Unable to import lldb module from path returned by `lldb -P`
- pass
-
- # On Mac OS X, use the try the default path to XCode lldb module
- if "darwin" in sys.platform:
- xcode_python_path = "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/Current/Resources/Python/"
- sys.path.append(xcode_python_path)
+ """ Find and import the lldb modules. This function tries to find the lldb module by:
+ 1. Simply by doing "import lldb" in case the system python installation is aware of lldb. If that fails,
+ 2. Executes the lldb executable pointed to by the LLDB environment variable (or if unset, the first lldb
+ on PATH") with the -P flag to determine the PYTHONPATH to set. If the lldb executable returns a valid
+ path, it is added to sys.path and the import is attempted again. If that fails, 3. On Mac OS X the
+ default Xcode 4.5 installation path.
+ """
+
+ # Try simple 'import lldb', in case of a system-wide install or a
+ # pre-configured PYTHONPATH
try:
- import lldb
- return True
+ import lldb
+ return True
except ImportError:
- # Unable to import lldb module from default Xcode python path
- pass
+ pass
+
+ # Allow overriding default path to lldb executable with the LLDB
+ # environment variable
+ lldb_executable = 'lldb'
+ if 'LLDB' in os.environ and os.path.exists(os.environ['LLDB']):
+ lldb_executable = os.environ['LLDB']
+
+ # Try using builtin module location support ('lldb -P')
+ from subprocess import check_output, CalledProcessError
+ try:
+ with open(os.devnull, 'w') as fnull:
+ lldb_minus_p_path = check_output(
+ "%s -P" %
+ lldb_executable,
+ shell=True,
+ stderr=fnull).strip()
+ if not os.path.exists(lldb_minus_p_path):
+ # lldb -P returned invalid path, probably too old
+ pass
+ else:
+ sys.path.append(lldb_minus_p_path)
+ import lldb
+ return True
+ except CalledProcessError:
+ # Cannot run 'lldb -P' to determine location of lldb python module
+ pass
+ except ImportError:
+ # Unable to import lldb module from path returned by `lldb -P`
+ pass
+
+ # On Mac OS X, use the try the default path to XCode lldb module
+ if "darwin" in sys.platform:
+ xcode_python_path = "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/Current/Resources/Python/"
+ sys.path.append(xcode_python_path)
+ try:
+ import lldb
+ return True
+ except ImportError:
+ # Unable to import lldb module from default Xcode python path
+ pass
- return False
+ return False
if not import_lldb():
- import vim
- vim.command('redraw | echo "%s"' % " Error loading lldb module; vim-lldb will be disabled. Check LLDB installation or set LLDB environment variable.")
+ import vim
+ vim.command(
+ 'redraw | echo "%s"' %
+ " Error loading lldb module; vim-lldb will be disabled. Check LLDB installation or set LLDB environment variable.")
diff --git a/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py b/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py
index 923e771e6af..ed54633a3ea 100644
--- a/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py
+++ b/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py
@@ -3,7 +3,9 @@
# This file defines the layer that talks to lldb
#
-import os, re, sys
+import os
+import re
+import sys
import lldb
import vim
from vim_ui import UI
@@ -13,372 +15,399 @@ from vim_ui import UI
# =================================================
# Shamelessly copy/pasted from lldbutil.py in the test suite
-def state_type_to_str(enum):
- """Returns the stateType string given an enum."""
- if enum == lldb.eStateInvalid:
- return "invalid"
- elif enum == lldb.eStateUnloaded:
- return "unloaded"
- elif enum == lldb.eStateConnected:
- return "connected"
- elif enum == lldb.eStateAttaching:
- return "attaching"
- elif enum == lldb.eStateLaunching:
- return "launching"
- elif enum == lldb.eStateStopped:
- return "stopped"
- elif enum == lldb.eStateRunning:
- return "running"
- elif enum == lldb.eStateStepping:
- return "stepping"
- elif enum == lldb.eStateCrashed:
- return "crashed"
- elif enum == lldb.eStateDetached:
- return "detached"
- elif enum == lldb.eStateExited:
- return "exited"
- elif enum == lldb.eStateSuspended:
- return "suspended"
- else:
- raise Exception("Unknown StateType enum")
-class StepType:
- INSTRUCTION = 1
- INSTRUCTION_OVER = 2
- INTO = 3
- OVER = 4
- OUT = 5
-class LLDBController(object):
- """ Handles Vim and LLDB events such as commands and lldb events. """
-
- # Timeouts (sec) for waiting on new events. Because vim is not multi-threaded, we are restricted to
- # servicing LLDB events from the main UI thread. Usually, we only process events that are already
- # sitting on the queue. But in some situations (when we are expecting an event as a result of some
- # user interaction) we want to wait for it. The constants below set these wait period in which the
- # Vim UI is "blocked". Lower numbers will make Vim more responsive, but LLDB will be delayed and higher
- # numbers will mean that LLDB events are processed faster, but the Vim UI may appear less responsive at
- # times.
- eventDelayStep = 2
- eventDelayLaunch = 1
- eventDelayContinue = 1
-
- def __init__(self):
- """ Creates the LLDB SBDebugger object and initializes the UI class. """
- self.target = None
- self.process = None
- self.load_dependent_modules = True
-
- self.dbg = lldb.SBDebugger.Create()
- self.commandInterpreter = self.dbg.GetCommandInterpreter()
-
- self.ui = UI()
-
- def completeCommand(self, a, l, p):
- """ Returns a list of viable completions for command a with length l and cursor at p """
-
- assert l[0] == 'L'
- # Remove first 'L' character that all commands start with
- l = l[1:]
-
- # Adjust length as string has 1 less character
- p = int(p) - 1
-
- result = lldb.SBStringList()
- num = self.commandInterpreter.HandleCompletion(l, p, 1, -1, result)
-
- if num == -1:
- # FIXME: insert completion character... what's a completion character?
- pass
- elif num == -2:
- # FIXME: replace line with result.GetStringAtIndex(0)
- pass
-
- if result.GetSize() > 0:
- results = filter(None, [result.GetStringAtIndex(x) for x in range(result.GetSize())])
- return results
- else:
- return []
-
- def doStep(self, stepType):
- """ Perform a step command and block the UI for eventDelayStep seconds in order to process
- events on lldb's event queue.
- FIXME: if the step does not complete in eventDelayStep seconds, we relinquish control to
- the main thread to avoid the appearance of a "hang". If this happens, the UI will
- update whenever; usually when the user moves the cursor. This is somewhat annoying.
- """
- if not self.process:
- sys.stderr.write("No process to step")
- return
-
- t = self.process.GetSelectedThread()
- if stepType == StepType.INSTRUCTION:
- t.StepInstruction(False)
- if stepType == StepType.INSTRUCTION_OVER:
- t.StepInstruction(True)
- elif stepType == StepType.INTO:
- t.StepInto()
- elif stepType == StepType.OVER:
- t.StepOver()
- elif stepType == StepType.OUT:
- t.StepOut()
-
- self.processPendingEvents(self.eventDelayStep, True)
-
- def doSelect(self, command, args):
- """ Like doCommand, but suppress output when "select" is the first argument."""
- a = args.split(' ')
- return self.doCommand(command, args, "select" != a[0], True)
-
- def doProcess(self, args):
- """ Handle 'process' command. If 'launch' is requested, use doLaunch() instead
- of the command interpreter to start the inferior process.
- """
- a = args.split(' ')
- if len(args) == 0 or (len(a) > 0 and a[0] != 'launch'):
- self.doCommand("process", args)
- #self.ui.update(self.target, "", self)
- else:
- self.doLaunch('-s' not in args, "")
-
- def doAttach(self, process_name):
- """ Handle process attach. """
- error = lldb.SBError()
-
- self.processListener = lldb.SBListener("process_event_listener")
- self.target = self.dbg.CreateTarget('')
- self.process = self.target.AttachToProcessWithName(self.processListener, process_name, False, error)
- if not error.Success():
- sys.stderr.write("Error during attach: " + str(error))
- return
-
- self.ui.activate()
- self.pid = self.process.GetProcessID()
-
- print "Attached to %s (pid=%d)" % (process_name, self.pid)
-
- def doDetach(self):
- if self.process is not None and self.process.IsValid():
- pid = self.process.GetProcessID()
- state = state_type_to_str(self.process.GetState())
- self.process.Detach()
- self.processPendingEvents(self.eventDelayLaunch)
-
- def doLaunch(self, stop_at_entry, args):
- """ Handle process launch. """
- error = lldb.SBError()
-
- fs = self.target.GetExecutable()
- exe = os.path.join(fs.GetDirectory(), fs.GetFilename())
- if self.process is not None and self.process.IsValid():
- pid = self.process.GetProcessID()
- state = state_type_to_str(self.process.GetState())
- self.process.Destroy()
-
- launchInfo = lldb.SBLaunchInfo(args.split(' '))
- self.process = self.target.Launch(launchInfo, error)
- if not error.Success():
- sys.stderr.write("Error during launch: " + str(error))
- return
-
- # launch succeeded, store pid and add some event listeners
- self.pid = self.process.GetProcessID()
- self.processListener = lldb.SBListener("process_event_listener")
- self.process.GetBroadcaster().AddListener(self.processListener, lldb.SBProcess.eBroadcastBitStateChanged)
-
- print "Launched %s %s (pid=%d)" % (exe, args, self.pid)
-
- if not stop_at_entry:
- self.doContinue()
+def state_type_to_str(enum):
+ """Returns the stateType string given an enum."""
+ if enum == lldb.eStateInvalid:
+ return "invalid"
+ elif enum == lldb.eStateUnloaded:
+ return "unloaded"
+ elif enum == lldb.eStateConnected:
+ return "connected"
+ elif enum == lldb.eStateAttaching:
+ return "attaching"
+ elif enum == lldb.eStateLaunching:
+ return "launching"
+ elif enum == lldb.eStateStopped:
+ return "stopped"
+ elif enum == lldb.eStateRunning:
+ return "running"
+ elif enum == lldb.eStateStepping:
+ return "stepping"
+ elif enum == lldb.eStateCrashed:
+ return "crashed"
+ elif enum == lldb.eStateDetached:
+ return "detached"
+ elif enum == lldb.eStateExited:
+ return "exited"
+ elif enum == lldb.eStateSuspended:
+ return "suspended"
else:
- self.processPendingEvents(self.eventDelayLaunch)
-
- def doTarget(self, args):
- """ Pass target command to interpreter, except if argument is not one of the valid options, or
- is create, in which case try to create a target with the argument as the executable. For example:
- target list ==> handled by interpreter
- target create blah ==> custom creation of target 'blah'
- target blah ==> also creates target blah
- """
- target_args = [#"create",
- "delete",
- "list",
- "modules",
- "select",
- "stop-hook",
- "symbols",
- "variable"]
-
- a = args.split(' ')
- if len(args) == 0 or (len(a) > 0 and a[0] in target_args):
- self.doCommand("target", args)
- return
- elif len(a) > 1 and a[0] == "create":
- exe = a[1]
- elif len(a) == 1 and a[0] not in target_args:
- exe = a[0]
-
- err = lldb.SBError()
- self.target = self.dbg.CreateTarget(exe, None, None, self.load_dependent_modules, err)
- if not self.target:
- sys.stderr.write("Error creating target %s. %s" % (str(exe), str(err)))
- return
-
- self.ui.activate()
- self.ui.update(self.target, "created target %s" % str(exe), self)
-
- def doContinue(self):
- """ Handle 'contiue' command.
- FIXME: switch to doCommand("continue", ...) to handle -i ignore-count param.
- """
- if not self.process or not self.process.IsValid():
- sys.stderr.write("No process to continue")
- return
+ raise Exception("Unknown StateType enum")
- self.process.Continue()
- self.processPendingEvents(self.eventDelayContinue)
- def doBreakpoint(self, args):
- """ Handle breakpoint command with command interpreter, except if the user calls
- "breakpoint" with no other args, in which case add a breakpoint at the line
- under the cursor.
- """
- a = args.split(' ')
- if len(args) == 0:
- show_output = False
-
- # User called us with no args, so toggle the bp under cursor
- cw = vim.current.window
- cb = vim.current.buffer
- name = cb.name
- line = cw.cursor[0]
-
- # Since the UI is responsbile for placing signs at bp locations, we have to
- # ask it if there already is one or more breakpoints at (file, line)...
- if self.ui.haveBreakpoint(name, line):
- bps = self.ui.getBreakpoints(name, line)
- args = "delete %s" % " ".join([str(b.GetID()) for b in bps])
- self.ui.deleteBreakpoints(name, line)
- else:
- args = "set -f %s -l %d" % (name, line)
- else:
- show_output = True
-
- self.doCommand("breakpoint", args, show_output)
- return
-
- def doRefresh(self):
- """ process pending events and update UI on request """
- status = self.processPendingEvents()
-
- def doShow(self, name):
- """ handle :Lshow <name> """
- if not name:
- self.ui.activate()
- return
-
- if self.ui.showWindow(name):
- self.ui.update(self.target, "", self)
-
- def doHide(self, name):
- """ handle :Lhide <name> """
- if self.ui.hideWindow(name):
- self.ui.update(self.target, "", self)
-
- def doExit(self):
- self.dbg.Terminate()
- self.dbg = None
-
- def getCommandResult(self, command, command_args):
- """ Run cmd in the command interpreter and returns (success, output) """
- result = lldb.SBCommandReturnObject()
- cmd = "%s %s" % (command, command_args)
-
- self.commandInterpreter.HandleCommand(cmd, result)
- return (result.Succeeded(), result.GetOutput() if result.Succeeded() else result.GetError())
-
- def doCommand(self, command, command_args, print_on_success = True, goto_file=False):
- """ Run cmd in interpreter and print result (success or failure) on the vim status line. """
- (success, output) = self.getCommandResult(command, command_args)
- if success:
- self.ui.update(self.target, "", self, goto_file)
- if len(output) > 0 and print_on_success:
- print output
- else:
- sys.stderr.write(output)
-
- def getCommandOutput(self, command, command_args=""):
- """ runs cmd in the command interpreter andreturns (status, result) """
- result = lldb.SBCommandReturnObject()
- cmd = "%s %s" % (command, command_args)
- self.commandInterpreter.HandleCommand(cmd, result)
- return (result.Succeeded(), result.GetOutput() if result.Succeeded() else result.GetError())
-
- def processPendingEvents(self, wait_seconds=0, goto_file=True):
- """ Handle any events that are queued from the inferior.
- Blocks for at most wait_seconds, or if wait_seconds == 0,
- process only events that are already queued.
- """
+class StepType:
+ INSTRUCTION = 1
+ INSTRUCTION_OVER = 2
+ INTO = 3
+ OVER = 4
+ OUT = 5
- status = None
- num_events_handled = 0
-
- if self.process is not None:
- event = lldb.SBEvent()
- old_state = self.process.GetState()
- new_state = None
- done = False
- if old_state == lldb.eStateInvalid or old_state == lldb.eStateExited:
- # Early-exit if we are in 'boring' states
- pass
- else:
- while not done and self.processListener is not None:
- if not self.processListener.PeekAtNextEvent(event):
- if wait_seconds > 0:
- # No events on the queue, but we are allowed to wait for wait_seconds
- # for any events to show up.
- self.processListener.WaitForEvent(wait_seconds, event)
- new_state = lldb.SBProcess.GetStateFromEvent(event)
-
- num_events_handled += 1
-
- done = not self.processListener.PeekAtNextEvent(event)
- else:
- # An event is on the queue, process it here.
- self.processListener.GetNextEvent(event)
- new_state = lldb.SBProcess.GetStateFromEvent(event)
-
- # continue if stopped after attaching
- if old_state == lldb.eStateAttaching and new_state == lldb.eStateStopped:
- self.process.Continue()
-
- # If needed, perform any event-specific behaviour here
- num_events_handled += 1
-
- if num_events_handled == 0:
- pass
- else:
- if old_state == new_state:
- status = ""
- self.ui.update(self.target, status, self, goto_file)
+
+class LLDBController(object):
+ """ Handles Vim and LLDB events such as commands and lldb events. """
+
+ # Timeouts (sec) for waiting on new events. Because vim is not multi-threaded, we are restricted to
+ # servicing LLDB events from the main UI thread. Usually, we only process events that are already
+ # sitting on the queue. But in some situations (when we are expecting an event as a result of some
+ # user interaction) we want to wait for it. The constants below set these wait period in which the
+ # Vim UI is "blocked". Lower numbers will make Vim more responsive, but LLDB will be delayed and higher
+ # numbers will mean that LLDB events are processed faster, but the Vim UI may appear less responsive at
+ # times.
+ eventDelayStep = 2
+ eventDelayLaunch = 1
+ eventDelayContinue = 1
+
+ def __init__(self):
+ """ Creates the LLDB SBDebugger object and initializes the UI class. """
+ self.target = None
+ self.process = None
+ self.load_dependent_modules = True
+
+ self.dbg = lldb.SBDebugger.Create()
+ self.commandInterpreter = self.dbg.GetCommandInterpreter()
+
+ self.ui = UI()
+
+ def completeCommand(self, a, l, p):
+ """ Returns a list of viable completions for command a with length l and cursor at p """
+
+ assert l[0] == 'L'
+ # Remove first 'L' character that all commands start with
+ l = l[1:]
+
+ # Adjust length as string has 1 less character
+ p = int(p) - 1
+
+ result = lldb.SBStringList()
+ num = self.commandInterpreter.HandleCompletion(l, p, 1, -1, result)
+
+ if num == -1:
+ # FIXME: insert completion character... what's a completion
+ # character?
+ pass
+ elif num == -2:
+ # FIXME: replace line with result.GetStringAtIndex(0)
+ pass
+
+ if result.GetSize() > 0:
+ results = filter(None, [result.GetStringAtIndex(x)
+ for x in range(result.GetSize())])
+ return results
+ else:
+ return []
+
+ def doStep(self, stepType):
+ """ Perform a step command and block the UI for eventDelayStep seconds in order to process
+ events on lldb's event queue.
+ FIXME: if the step does not complete in eventDelayStep seconds, we relinquish control to
+ the main thread to avoid the appearance of a "hang". If this happens, the UI will
+ update whenever; usually when the user moves the cursor. This is somewhat annoying.
+ """
+ if not self.process:
+ sys.stderr.write("No process to step")
+ return
+
+ t = self.process.GetSelectedThread()
+ if stepType == StepType.INSTRUCTION:
+ t.StepInstruction(False)
+ if stepType == StepType.INSTRUCTION_OVER:
+ t.StepInstruction(True)
+ elif stepType == StepType.INTO:
+ t.StepInto()
+ elif stepType == StepType.OVER:
+ t.StepOver()
+ elif stepType == StepType.OUT:
+ t.StepOut()
+
+ self.processPendingEvents(self.eventDelayStep, True)
+
+ def doSelect(self, command, args):
+ """ Like doCommand, but suppress output when "select" is the first argument."""
+ a = args.split(' ')
+ return self.doCommand(command, args, "select" != a[0], True)
+
+ def doProcess(self, args):
+ """ Handle 'process' command. If 'launch' is requested, use doLaunch() instead
+ of the command interpreter to start the inferior process.
+ """
+ a = args.split(' ')
+ if len(args) == 0 or (len(a) > 0 and a[0] != 'launch'):
+ self.doCommand("process", args)
+ #self.ui.update(self.target, "", self)
+ else:
+ self.doLaunch('-s' not in args, "")
+
+ def doAttach(self, process_name):
+ """ Handle process attach. """
+ error = lldb.SBError()
+
+ self.processListener = lldb.SBListener("process_event_listener")
+ self.target = self.dbg.CreateTarget('')
+ self.process = self.target.AttachToProcessWithName(
+ self.processListener, process_name, False, error)
+ if not error.Success():
+ sys.stderr.write("Error during attach: " + str(error))
+ return
+
+ self.ui.activate()
+ self.pid = self.process.GetProcessID()
+
+ print "Attached to %s (pid=%d)" % (process_name, self.pid)
+
+ def doDetach(self):
+ if self.process is not None and self.process.IsValid():
+ pid = self.process.GetProcessID()
+ state = state_type_to_str(self.process.GetState())
+ self.process.Detach()
+ self.processPendingEvents(self.eventDelayLaunch)
+
+ def doLaunch(self, stop_at_entry, args):
+ """ Handle process launch. """
+ error = lldb.SBError()
+
+ fs = self.target.GetExecutable()
+ exe = os.path.join(fs.GetDirectory(), fs.GetFilename())
+ if self.process is not None and self.process.IsValid():
+ pid = self.process.GetProcessID()
+ state = state_type_to_str(self.process.GetState())
+ self.process.Destroy()
+
+ launchInfo = lldb.SBLaunchInfo(args.split(' '))
+ self.process = self.target.Launch(launchInfo, error)
+ if not error.Success():
+ sys.stderr.write("Error during launch: " + str(error))
+ return
+
+ # launch succeeded, store pid and add some event listeners
+ self.pid = self.process.GetProcessID()
+ self.processListener = lldb.SBListener("process_event_listener")
+ self.process.GetBroadcaster().AddListener(
+ self.processListener, lldb.SBProcess.eBroadcastBitStateChanged)
+
+ print "Launched %s %s (pid=%d)" % (exe, args, self.pid)
+
+ if not stop_at_entry:
+ self.doContinue()
+ else:
+ self.processPendingEvents(self.eventDelayLaunch)
+
+ def doTarget(self, args):
+ """ Pass target command to interpreter, except if argument is not one of the valid options, or
+ is create, in which case try to create a target with the argument as the executable. For example:
+ target list ==> handled by interpreter
+ target create blah ==> custom creation of target 'blah'
+ target blah ==> also creates target blah
+ """
+ target_args = [ # "create",
+ "delete",
+ "list",
+ "modules",
+ "select",
+ "stop-hook",
+ "symbols",
+ "variable"]
+
+ a = args.split(' ')
+ if len(args) == 0 or (len(a) > 0 and a[0] in target_args):
+ self.doCommand("target", args)
+ return
+ elif len(a) > 1 and a[0] == "create":
+ exe = a[1]
+ elif len(a) == 1 and a[0] not in target_args:
+ exe = a[0]
+
+ err = lldb.SBError()
+ self.target = self.dbg.CreateTarget(
+ exe, None, None, self.load_dependent_modules, err)
+ if not self.target:
+ sys.stderr.write(
+ "Error creating target %s. %s" %
+ (str(exe), str(err)))
+ return
+
+ self.ui.activate()
+ self.ui.update(self.target, "created target %s" % str(exe), self)
+
+ def doContinue(self):
+ """ Handle 'contiue' command.
+ FIXME: switch to doCommand("continue", ...) to handle -i ignore-count param.
+ """
+ if not self.process or not self.process.IsValid():
+ sys.stderr.write("No process to continue")
+ return
+
+ self.process.Continue()
+ self.processPendingEvents(self.eventDelayContinue)
+
+ def doBreakpoint(self, args):
+ """ Handle breakpoint command with command interpreter, except if the user calls
+ "breakpoint" with no other args, in which case add a breakpoint at the line
+ under the cursor.
+ """
+ a = args.split(' ')
+ if len(args) == 0:
+ show_output = False
+
+ # User called us with no args, so toggle the bp under cursor
+ cw = vim.current.window
+ cb = vim.current.buffer
+ name = cb.name
+ line = cw.cursor[0]
+
+ # Since the UI is responsbile for placing signs at bp locations, we have to
+ # ask it if there already is one or more breakpoints at (file,
+ # line)...
+ if self.ui.haveBreakpoint(name, line):
+ bps = self.ui.getBreakpoints(name, line)
+ args = "delete %s" % " ".join([str(b.GetID()) for b in bps])
+ self.ui.deleteBreakpoints(name, line)
+ else:
+ args = "set -f %s -l %d" % (name, line)
+ else:
+ show_output = True
+
+ self.doCommand("breakpoint", args, show_output)
+ return
+
+ def doRefresh(self):
+ """ process pending events and update UI on request """
+ status = self.processPendingEvents()
+
+ def doShow(self, name):
+ """ handle :Lshow <name> """
+ if not name:
+ self.ui.activate()
+ return
+
+ if self.ui.showWindow(name):
+ self.ui.update(self.target, "", self)
+
+ def doHide(self, name):
+ """ handle :Lhide <name> """
+ if self.ui.hideWindow(name):
+ self.ui.update(self.target, "", self)
+
+ def doExit(self):
+ self.dbg.Terminate()
+ self.dbg = None
+
+ def getCommandResult(self, command, command_args):
+ """ Run cmd in the command interpreter and returns (success, output) """
+ result = lldb.SBCommandReturnObject()
+ cmd = "%s %s" % (command, command_args)
+
+ self.commandInterpreter.HandleCommand(cmd, result)
+ return (result.Succeeded(), result.GetOutput()
+ if result.Succeeded() else result.GetError())
+
+ def doCommand(
+ self,
+ command,
+ command_args,
+ print_on_success=True,
+ goto_file=False):
+ """ Run cmd in interpreter and print result (success or failure) on the vim status line. """
+ (success, output) = self.getCommandResult(command, command_args)
+ if success:
+ self.ui.update(self.target, "", self, goto_file)
+ if len(output) > 0 and print_on_success:
+ print output
+ else:
+ sys.stderr.write(output)
+
+ def getCommandOutput(self, command, command_args=""):
+ """ runs cmd in the command interpreter andreturns (status, result) """
+ result = lldb.SBCommandReturnObject()
+ cmd = "%s %s" % (command, command_args)
+ self.commandInterpreter.HandleCommand(cmd, result)
+ return (result.Succeeded(), result.GetOutput()
+ if result.Succeeded() else result.GetError())
+
+ def processPendingEvents(self, wait_seconds=0, goto_file=True):
+ """ Handle any events that are queued from the inferior.
+ Blocks for at most wait_seconds, or if wait_seconds == 0,
+ process only events that are already queued.
+ """
+
+ status = None
+ num_events_handled = 0
+
+ if self.process is not None:
+ event = lldb.SBEvent()
+ old_state = self.process.GetState()
+ new_state = None
+ done = False
+ if old_state == lldb.eStateInvalid or old_state == lldb.eStateExited:
+ # Early-exit if we are in 'boring' states
+ pass
+ else:
+ while not done and self.processListener is not None:
+ if not self.processListener.PeekAtNextEvent(event):
+ if wait_seconds > 0:
+ # No events on the queue, but we are allowed to wait for wait_seconds
+ # for any events to show up.
+ self.processListener.WaitForEvent(
+ wait_seconds, event)
+ new_state = lldb.SBProcess.GetStateFromEvent(event)
+
+ num_events_handled += 1
+
+ done = not self.processListener.PeekAtNextEvent(event)
+ else:
+ # An event is on the queue, process it here.
+ self.processListener.GetNextEvent(event)
+ new_state = lldb.SBProcess.GetStateFromEvent(event)
+
+ # continue if stopped after attaching
+ if old_state == lldb.eStateAttaching and new_state == lldb.eStateStopped:
+ self.process.Continue()
+
+ # If needed, perform any event-specific behaviour here
+ num_events_handled += 1
+
+ if num_events_handled == 0:
+ pass
+ else:
+ if old_state == new_state:
+ status = ""
+ self.ui.update(self.target, status, self, goto_file)
def returnCompleteCommand(a, l, p):
- """ Returns a "\n"-separated string with possible completion results
- for command a with length l and cursor at p.
- """
- separator = "\n"
- results = ctrl.completeCommand(a, l, p)
- vim.command('return "%s%s"' % (separator.join(results), separator))
+ """ Returns a "\n"-separated string with possible completion results
+ for command a with length l and cursor at p.
+ """
+ separator = "\n"
+ results = ctrl.completeCommand(a, l, p)
+ vim.command('return "%s%s"' % (separator.join(results), separator))
+
def returnCompleteWindow(a, l, p):
- """ Returns a "\n"-separated string with possible completion results
- for commands that expect a window name parameter (like hide/show).
- FIXME: connect to ctrl.ui instead of hardcoding the list here
- """
- separator = "\n"
- results = ['breakpoints', 'backtrace', 'disassembly', 'locals', 'threads', 'registers']
- vim.command('return "%s%s"' % (separator.join(results), separator))
+ """ Returns a "\n"-separated string with possible completion results
+ for commands that expect a window name parameter (like hide/show).
+ FIXME: connect to ctrl.ui instead of hardcoding the list here
+ """
+ separator = "\n"
+ results = [
+ 'breakpoints',
+ 'backtrace',
+ 'disassembly',
+ 'locals',
+ 'threads',
+ 'registers']
+ vim.command('return "%s%s"' % (separator.join(results), separator))
global ctrl
ctrl = LLDBController()
diff --git a/lldb/utils/vim-lldb/python-vim-lldb/plugin.py b/lldb/utils/vim-lldb/python-vim-lldb/plugin.py
index 694783a95b0..c59546c745c 100644
--- a/lldb/utils/vim-lldb/python-vim-lldb/plugin.py
+++ b/lldb/utils/vim-lldb/python-vim-lldb/plugin.py
@@ -1,14 +1,16 @@
-# Try to import all dependencies, catch and handle the error gracefully if it fails.
+# Try to import all dependencies, catch and handle the error gracefully if
+# it fails.
import import_lldb
try:
- import lldb
- import vim
+ import lldb
+ import vim
except ImportError:
- sys.stderr.write("Unable to load vim/lldb module. Check lldb is on the path is available (or LLDB is set) and that script is invoked inside Vim with :pyfile")
- pass
+ sys.stderr.write(
+ "Unable to load vim/lldb module. Check lldb is on the path is available (or LLDB is set) and that script is invoked inside Vim with :pyfile")
+ pass
else:
- # Everthing went well, so use import to start the plugin controller
- from lldb_controller import *
+ # Everthing went well, so use import to start the plugin controller
+ from lldb_controller import *
diff --git a/lldb/utils/vim-lldb/python-vim-lldb/vim_panes.py b/lldb/utils/vim-lldb/python-vim-lldb/vim_panes.py
index ec537199922..b0c804d1962 100644
--- a/lldb/utils/vim-lldb/python-vim-lldb/vim_panes.py
+++ b/lldb/utils/vim-lldb/python-vim-lldb/vim_panes.py
@@ -6,8 +6,8 @@
# - get_content() - returns a string with the pane contents
#
# Optionally, to highlight text, implement:
-# - get_highlights() - returns a map
-#
+# - get_highlights() - returns a map
+#
# And call:
# - define_highlight(unique_name, colour)
# at some point in the constructor.
@@ -16,7 +16,7 @@
# If the pane shows some key-value data that is in the context of a
# single frame, inherit from FrameKeyValuePane and implement:
# - get_frame_content(self, SBFrame frame)
-#
+#
#
# If the pane presents some information that can be retrieved with
# a simple LLDB command while the subprocess is stopped, inherit
@@ -27,12 +27,12 @@
# Optionally, you can implement:
# - get_selected_line()
# to highlight a selected line and place the cursor there.
-#
+#
#
# FIXME: implement WatchlistPane to displayed watched expressions
-# FIXME: define interface for interactive panes, like catching enter
+# FIXME: define interface for interactive panes, like catching enter
# presses to change selected frame/thread...
-#
+#
import lldb
import vim
@@ -44,6 +44,8 @@ import sys
# ==============================================================
# Shamelessly copy/pasted from lldbutil.py in the test suite
+
+
def get_description(obj, option=None):
"""Calls lldb_obj.GetDescription() and returns a string, or None.
@@ -70,512 +72,552 @@ def get_description(obj, option=None):
if not success:
return None
return stream.GetData()
-
+
+
def get_selected_thread(target):
- """ Returns a tuple with (thread, error) where thread == None if error occurs """
- process = target.GetProcess()
- if process is None or not process.IsValid():
- return (None, VimPane.MSG_NO_PROCESS)
+ """ Returns a tuple with (thread, error) where thread == None if error occurs """
+ process = target.GetProcess()
+ if process is None or not process.IsValid():
+ return (None, VimPane.MSG_NO_PROCESS)
+
+ thread = process.GetSelectedThread()
+ if thread is None or not thread.IsValid():
+ return (None, VimPane.MSG_NO_THREADS)
+ return (thread, "")
- thread = process.GetSelectedThread()
- if thread is None or not thread.IsValid():
- return (None, VimPane.MSG_NO_THREADS)
- return (thread, "")
def get_selected_frame(target):
- """ Returns a tuple with (frame, error) where frame == None if error occurs """
- (thread, error) = get_selected_thread(target)
- if thread is None:
- return (None, error)
+ """ Returns a tuple with (frame, error) where frame == None if error occurs """
+ (thread, error) = get_selected_thread(target)
+ if thread is None:
+ return (None, error)
+
+ frame = thread.GetSelectedFrame()
+ if frame is None or not frame.IsValid():
+ return (None, VimPane.MSG_NO_FRAME)
+ return (frame, "")
- frame = thread.GetSelectedFrame()
- if frame is None or not frame.IsValid():
- return (None, VimPane.MSG_NO_FRAME)
- return (frame, "")
def _cmd(cmd):
- vim.command("call confirm('%s')" % cmd)
- vim.command(cmd)
+ vim.command("call confirm('%s')" % cmd)
+ vim.command(cmd)
+
def move_cursor(line, col=0):
- """ moves cursor to specified line and col """
- cw = vim.current.window
- if cw.cursor[0] != line:
- vim.command("execute \"normal %dgg\"" % line)
+ """ moves cursor to specified line and col """
+ cw = vim.current.window
+ if cw.cursor[0] != line:
+ vim.command("execute \"normal %dgg\"" % line)
+
def winnr():
- """ Returns currently selected window number """
- return int(vim.eval("winnr()"))
+ """ Returns currently selected window number """
+ return int(vim.eval("winnr()"))
+
def bufwinnr(name):
- """ Returns window number corresponding with buffer name """
- return int(vim.eval("bufwinnr('%s')" % name))
+ """ Returns window number corresponding with buffer name """
+ return int(vim.eval("bufwinnr('%s')" % name))
+
def goto_window(nr):
- """ go to window number nr"""
- if nr != winnr():
- vim.command(str(nr) + ' wincmd w')
+ """ go to window number nr"""
+ if nr != winnr():
+ vim.command(str(nr) + ' wincmd w')
+
def goto_next_window():
- """ go to next window. """
- vim.command('wincmd w')
- return (winnr(), vim.current.buffer.name)
+ """ go to next window. """
+ vim.command('wincmd w')
+ return (winnr(), vim.current.buffer.name)
+
def goto_previous_window():
- """ go to previously selected window """
- vim.command("execute \"normal \\<c-w>p\"")
+ """ go to previously selected window """
+ vim.command("execute \"normal \\<c-w>p\"")
+
def have_gui():
- """ Returns True if vim is in a gui (Gvim/MacVim), False otherwise. """
- return int(vim.eval("has('gui_running')")) == 1
+ """ Returns True if vim is in a gui (Gvim/MacVim), False otherwise. """
+ return int(vim.eval("has('gui_running')")) == 1
+
class PaneLayout(object):
- """ A container for a (vertical) group layout of VimPanes """
+ """ A container for a (vertical) group layout of VimPanes """
+
+ def __init__(self):
+ self.panes = {}
+
+ def havePane(self, name):
+ """ Returns true if name is a registered pane, False otherwise """
+ return name in self.panes
+
+ def prepare(self, panes=[]):
+ """ Draw panes on screen. If empty list is provided, show all. """
+
+ # If we can't select a window contained in the layout, we are doing a
+ # first draw
+ first_draw = not self.selectWindow(True)
+ did_first_draw = False
+
+ # Prepare each registered pane
+ for name in self.panes:
+ if name in panes or len(panes) == 0:
+ if first_draw:
+ # First window in layout will be created with :vsp, and
+ # closed later
+ vim.command(":vsp")
+ first_draw = False
+ did_first_draw = True
+ self.panes[name].prepare()
+
+ if did_first_draw:
+ # Close the split window
+ vim.command(":q")
+
+ self.selectWindow(False)
+
+ def contains(self, bufferName=None):
+ """ Returns True if window with name bufferName is contained in the layout, False otherwise.
+ If bufferName is None, the currently selected window is checked.
+ """
+ if not bufferName:
+ bufferName = vim.current.buffer.name
+
+ for p in self.panes:
+ if bufferName is not None and bufferName.endswith(p):
+ return True
+ return False
+
+ def selectWindow(self, select_contained=True):
+ """ Selects a window contained in the layout (if select_contained = True) and returns True.
+ If select_contained = False, a window that is not contained is selected. Returns False
+ if no group windows can be selected.
+ """
+ if select_contained == self.contains():
+ # Simple case: we are already selected
+ return True
+
+ # Otherwise, switch to next window until we find a contained window, or
+ # reach the first window again.
+ first = winnr()
+ (curnum, curname) = goto_next_window()
+
+ while not select_contained == self.contains(
+ curname) and curnum != first:
+ (curnum, curname) = goto_next_window()
+
+ return self.contains(curname) == select_contained
+
+ def hide(self, panes=[]):
+ """ Hide panes specified. If empty list provided, hide all. """
+ for name in self.panes:
+ if name in panes or len(panes) == 0:
+ self.panes[name].destroy()
+
+ def registerForUpdates(self, p):
+ self.panes[p.name] = p
+
+ def update(self, target, controller):
+ for name in self.panes:
+ self.panes[name].update(target, controller)
- def __init__(self):
- self.panes = {}
- def havePane(self, name):
- """ Returns true if name is a registered pane, False otherwise """
- return name in self.panes
+class VimPane(object):
+ """ A generic base class for a pane that displays stuff """
+ CHANGED_VALUE_HIGHLIGHT_NAME_GUI = 'ColorColumn'
+ CHANGED_VALUE_HIGHLIGHT_NAME_TERM = 'lldb_changed'
+ CHANGED_VALUE_HIGHLIGHT_COLOUR_TERM = 'darkred'
+
+ SELECTED_HIGHLIGHT_NAME_GUI = 'Cursor'
+ SELECTED_HIGHLIGHT_NAME_TERM = 'lldb_selected'
+ SELECTED_HIGHLIGHT_COLOUR_TERM = 'darkblue'
+
+ MSG_NO_TARGET = "Target does not exist."
+ MSG_NO_PROCESS = "Process does not exist."
+ MSG_NO_THREADS = "No valid threads."
+ MSG_NO_FRAME = "No valid frame."
+
+ # list of defined highlights, so we avoid re-defining them
+ highlightTypes = []
+
+ def __init__(self, owner, name, open_below=False, height=3):
+ self.owner = owner
+ self.name = name
+ self.buffer = None
+ self.maxHeight = 20
+ self.openBelow = open_below
+ self.height = height
+ self.owner.registerForUpdates(self)
+
+ def isPrepared(self):
+ """ check window is OK """
+ if self.buffer is None or len(
+ dir(self.buffer)) == 0 or bufwinnr(self.name) == -1:
+ return False
+ return True
- def prepare(self, panes = []):
- """ Draw panes on screen. If empty list is provided, show all. """
+ def prepare(self, method='new'):
+ """ check window is OK, if not then create """
+ if not self.isPrepared():
+ self.create(method)
+
+ def on_create(self):
+ pass
+
+ def destroy(self):
+ """ destroy window """
+ if self.buffer is None or len(dir(self.buffer)) == 0:
+ return
+ vim.command('bdelete ' + self.name)
+
+ def create(self, method):
+ """ create window """
+
+ if method != 'edit':
+ belowcmd = "below" if self.openBelow else ""
+ vim.command('silent %s %s %s' % (belowcmd, method, self.name))
+ else:
+ vim.command('silent %s %s' % (method, self.name))
+
+ self.window = vim.current.window
+
+ # Set LLDB pane options
+ vim.command("setlocal buftype=nofile") # Don't try to open a file
+ vim.command("setlocal noswapfile") # Don't use a swap file
+ vim.command("set nonumber") # Don't display line numbers
+ # vim.command("set nowrap") # Don't wrap text
+
+ # Save some parameters and reference to buffer
+ self.buffer = vim.current.buffer
+ self.width = int(vim.eval("winwidth(0)"))
+ self.height = int(vim.eval("winheight(0)"))
+
+ self.on_create()
+ goto_previous_window()
+
+ def update(self, target, controller):
+ """ updates buffer contents """
+ self.target = target
+ if not self.isPrepared():
+ # Window is hidden, or otherwise not ready for an update
+ return
+
+ original_cursor = self.window.cursor
+
+ # Select pane
+ goto_window(bufwinnr(self.name))
+
+ # Clean and update content, and apply any highlights.
+ self.clean()
+
+ if self.write(self.get_content(target, controller)):
+ self.apply_highlights()
+
+ cursor = self.get_selected_line()
+ if cursor is None:
+ # Place the cursor at its original position in the window
+ cursor_line = min(original_cursor[0], len(self.buffer))
+ cursor_col = min(
+ original_cursor[1], len(
+ self.buffer[
+ cursor_line - 1]))
+ else:
+ # Place the cursor at the location requested by a VimPane
+ # implementation
+ cursor_line = min(cursor, len(self.buffer))
+ cursor_col = self.window.cursor[1]
+
+ self.window.cursor = (cursor_line, cursor_col)
+
+ goto_previous_window()
+
+ def get_selected_line(self):
+ """ Returns the line number to move the cursor to, or None to leave
+ it where the user last left it.
+ Subclasses implement this to define custom behaviour.
+ """
+ return None
- # If we can't select a window contained in the layout, we are doing a first draw
- first_draw = not self.selectWindow(True)
- did_first_draw = False
+ def apply_highlights(self):
+ """ Highlights each set of lines in each highlight group """
+ highlights = self.get_highlights()
+ for highlightType in highlights:
+ lines = highlights[highlightType]
+ if len(lines) == 0:
+ continue
+
+ cmd = 'match %s /' % highlightType
+ lines = ['\%' + '%d' % line + 'l' for line in lines]
+ cmd += '\\|'.join(lines)
+ cmd += '/'
+ vim.command(cmd)
+
+ def define_highlight(self, name, colour):
+ """ Defines highlihght """
+ if name in VimPane.highlightTypes:
+ # highlight already defined
+ return
+
+ vim.command(
+ "highlight %s ctermbg=%s guibg=%s" %
+ (name, colour, colour))
+ VimPane.highlightTypes.append(name)
+
+ def write(self, msg):
+ """ replace buffer with msg"""
+ self.prepare()
+
+ msg = str(msg.encode("utf-8", "replace")).split('\n')
+ try:
+ self.buffer.append(msg)
+ vim.command("execute \"normal ggdd\"")
+ except vim.error:
+ # cannot update window; happens when vim is exiting.
+ return False
+
+ move_cursor(1, 0)
+ return True
- # Prepare each registered pane
- for name in self.panes:
- if name in panes or len(panes) == 0:
- if first_draw:
- # First window in layout will be created with :vsp, and closed later
- vim.command(":vsp")
- first_draw = False
- did_first_draw = True
- self.panes[name].prepare()
+ def clean(self):
+ """ clean all datas in buffer """
+ self.prepare()
+ vim.command(':%d')
+ #self.buffer[:] = None
- if did_first_draw:
- # Close the split window
- vim.command(":q")
+ def get_content(self, target, controller):
+ """ subclasses implement this to provide pane content """
+ assert(0 and "pane subclass must implement this")
+ pass
- self.selectWindow(False)
+ def get_highlights(self):
+ """ Subclasses implement this to provide pane highlights.
+ This function is expected to return a map of:
+ { highlight_name ==> [line_number, ...], ... }
+ """
+ return {}
- def contains(self, bufferName = None):
- """ Returns True if window with name bufferName is contained in the layout, False otherwise.
- If bufferName is None, the currently selected window is checked.
- """
- if not bufferName:
- bufferName = vim.current.buffer.name
- for p in self.panes:
- if bufferName is not None and bufferName.endswith(p):
- return True
- return False
+class FrameKeyValuePane(VimPane):
- def selectWindow(self, select_contained = True):
- """ Selects a window contained in the layout (if select_contained = True) and returns True.
- If select_contained = False, a window that is not contained is selected. Returns False
- if no group windows can be selected.
- """
- if select_contained == self.contains():
- # Simple case: we are already selected
- return True
+ def __init__(self, owner, name, open_below):
+ """ Initialize parent, define member variables, choose which highlight
+ to use based on whether or not we have a gui (MacVim/Gvim).
+ """
+
+ VimPane.__init__(self, owner, name, open_below)
+
+ # Map-of-maps key/value history { frame --> { variable_name,
+ # variable_value } }
+ self.frameValues = {}
+
+ if have_gui():
+ self.changedHighlight = VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_GUI
+ else:
+ self.changedHighlight = VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_TERM
+ self.define_highlight(VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_TERM,
+ VimPane.CHANGED_VALUE_HIGHLIGHT_COLOUR_TERM)
+
+ def format_pair(self, key, value, changed=False):
+ """ Formats a key/value pair. Appends a '*' if changed == True """
+ marker = '*' if changed else ' '
+ return "%s %s = %s\n" % (marker, key, value)
+
+ def get_content(self, target, controller):
+ """ Get content for a frame-aware pane. Also builds the list of lines that
+ need highlighting (i.e. changed values.)
+ """
+ if target is None or not target.IsValid():
+ return VimPane.MSG_NO_TARGET
+
+ self.changedLines = []
+
+ (frame, err) = get_selected_frame(target)
+ if frame is None:
+ return err
+
+ output = get_description(frame)
+ lineNum = 1
+
+ # Retrieve the last values displayed for this frame
+ frameId = get_description(frame.GetBlock())
+ if frameId in self.frameValues:
+ frameOldValues = self.frameValues[frameId]
+ else:
+ frameOldValues = {}
+
+ # Read the frame variables
+ vals = self.get_frame_content(frame)
+ for (key, value) in vals:
+ lineNum += 1
+ if len(frameOldValues) == 0 or (
+ key in frameOldValues and frameOldValues[key] == value):
+ output += self.format_pair(key, value)
+ else:
+ output += self.format_pair(key, value, True)
+ self.changedLines.append(lineNum)
+
+ # Save values as oldValues
+ newValues = {}
+ for (key, value) in vals:
+ newValues[key] = value
+ self.frameValues[frameId] = newValues
+
+ return output
+
+ def get_highlights(self):
+ ret = {}
+ ret[self.changedHighlight] = self.changedLines
+ return ret
- # Otherwise, switch to next window until we find a contained window, or reach the first window again.
- first = winnr()
- (curnum, curname) = goto_next_window()
- while not select_contained == self.contains(curname) and curnum != first:
- (curnum, curname) = goto_next_window()
+class LocalsPane(FrameKeyValuePane):
+ """ Pane that displays local variables """
- return self.contains(curname) == select_contained
+ def __init__(self, owner, name='locals'):
+ FrameKeyValuePane.__init__(self, owner, name, open_below=True)
- def hide(self, panes = []):
- """ Hide panes specified. If empty list provided, hide all. """
- for name in self.panes:
- if name in panes or len(panes) == 0:
- self.panes[name].destroy()
+ # FIXME: allow users to customize display of args/locals/statics/scope
+ self.arguments = True
+ self.show_locals = True
+ self.show_statics = True
+ self.show_in_scope_only = True
- def registerForUpdates(self, p):
- self.panes[p.name] = p
+ def format_variable(self, var):
+ """ Returns a Tuple of strings "(Type) Name", "Value" for SBValue var """
+ val = var.GetValue()
+ if val is None:
+ # If the value is too big, SBValue.GetValue() returns None; replace
+ # with ...
+ val = "..."
- def update(self, target, controller):
- for name in self.panes:
- self.panes[name].update(target, controller)
+ return ("(%s) %s" % (var.GetTypeName(), var.GetName()), "%s" % val)
+ def get_frame_content(self, frame):
+ """ Returns list of key-value pairs of local variables in frame """
+ vals = frame.GetVariables(self.arguments,
+ self.show_locals,
+ self.show_statics,
+ self.show_in_scope_only)
+ return [self.format_variable(x) for x in vals]
-class VimPane(object):
- """ A generic base class for a pane that displays stuff """
- CHANGED_VALUE_HIGHLIGHT_NAME_GUI = 'ColorColumn'
- CHANGED_VALUE_HIGHLIGHT_NAME_TERM = 'lldb_changed'
- CHANGED_VALUE_HIGHLIGHT_COLOUR_TERM = 'darkred'
-
- SELECTED_HIGHLIGHT_NAME_GUI = 'Cursor'
- SELECTED_HIGHLIGHT_NAME_TERM = 'lldb_selected'
- SELECTED_HIGHLIGHT_COLOUR_TERM = 'darkblue'
-
- MSG_NO_TARGET = "Target does not exist."
- MSG_NO_PROCESS = "Process does not exist."
- MSG_NO_THREADS = "No valid threads."
- MSG_NO_FRAME = "No valid frame."
-
- # list of defined highlights, so we avoid re-defining them
- highlightTypes = []
-
- def __init__(self, owner, name, open_below=False, height=3):
- self.owner = owner
- self.name = name
- self.buffer = None
- self.maxHeight = 20
- self.openBelow = open_below
- self.height = height
- self.owner.registerForUpdates(self)
-
- def isPrepared(self):
- """ check window is OK """
- if self.buffer == None or len(dir(self.buffer)) == 0 or bufwinnr(self.name) == -1:
- return False
- return True
-
- def prepare(self, method = 'new'):
- """ check window is OK, if not then create """
- if not self.isPrepared():
- self.create(method)
-
- def on_create(self):
- pass
-
- def destroy(self):
- """ destroy window """
- if self.buffer == None or len(dir(self.buffer)) == 0:
- return
- vim.command('bdelete ' + self.name)
-
- def create(self, method):
- """ create window """
-
- if method != 'edit':
- belowcmd = "below" if self.openBelow else ""
- vim.command('silent %s %s %s' % (belowcmd, method, self.name))
- else:
- vim.command('silent %s %s' % (method, self.name))
-
- self.window = vim.current.window
-
- # Set LLDB pane options
- vim.command("setlocal buftype=nofile") # Don't try to open a file
- vim.command("setlocal noswapfile") # Don't use a swap file
- vim.command("set nonumber") # Don't display line numbers
- #vim.command("set nowrap") # Don't wrap text
-
- # Save some parameters and reference to buffer
- self.buffer = vim.current.buffer
- self.width = int( vim.eval("winwidth(0)") )
- self.height = int( vim.eval("winheight(0)") )
-
- self.on_create()
- goto_previous_window()
-
- def update(self, target, controller):
- """ updates buffer contents """
- self.target = target
- if not self.isPrepared():
- # Window is hidden, or otherwise not ready for an update
- return
-
- original_cursor = self.window.cursor
-
- # Select pane
- goto_window(bufwinnr(self.name))
-
- # Clean and update content, and apply any highlights.
- self.clean()
-
- if self.write(self.get_content(target, controller)):
- self.apply_highlights()
-
- cursor = self.get_selected_line()
- if cursor is None:
- # Place the cursor at its original position in the window
- cursor_line = min(original_cursor[0], len(self.buffer))
- cursor_col = min(original_cursor[1], len(self.buffer[cursor_line - 1]))
- else:
- # Place the cursor at the location requested by a VimPane implementation
- cursor_line = min(cursor, len(self.buffer))
- cursor_col = self.window.cursor[1]
-
- self.window.cursor = (cursor_line, cursor_col)
-
- goto_previous_window()
-
- def get_selected_line(self):
- """ Returns the line number to move the cursor to, or None to leave
- it where the user last left it.
- Subclasses implement this to define custom behaviour.
- """
- return None
-
- def apply_highlights(self):
- """ Highlights each set of lines in each highlight group """
- highlights = self.get_highlights()
- for highlightType in highlights:
- lines = highlights[highlightType]
- if len(lines) == 0:
- continue
-
- cmd = 'match %s /' % highlightType
- lines = ['\%' + '%d' % line + 'l' for line in lines]
- cmd += '\\|'.join(lines)
- cmd += '/'
- vim.command(cmd)
-
- def define_highlight(self, name, colour):
- """ Defines highlihght """
- if name in VimPane.highlightTypes:
- # highlight already defined
- return
-
- vim.command("highlight %s ctermbg=%s guibg=%s" % (name, colour, colour))
- VimPane.highlightTypes.append(name)
-
- def write(self, msg):
- """ replace buffer with msg"""
- self.prepare()
-
- msg = str(msg.encode("utf-8", "replace")).split('\n')
- try:
- self.buffer.append(msg)
- vim.command("execute \"normal ggdd\"")
- except vim.error:
- # cannot update window; happens when vim is exiting.
- return False
-
- move_cursor(1, 0)
- return True
-
- def clean(self):
- """ clean all datas in buffer """
- self.prepare()
- vim.command(':%d')
- #self.buffer[:] = None
-
- def get_content(self, target, controller):
- """ subclasses implement this to provide pane content """
- assert(0 and "pane subclass must implement this")
- pass
-
- def get_highlights(self):
- """ Subclasses implement this to provide pane highlights.
- This function is expected to return a map of:
- { highlight_name ==> [line_number, ...], ... }
- """
- return {}
+class RegistersPane(FrameKeyValuePane):
+ """ Pane that displays the contents of registers """
-class FrameKeyValuePane(VimPane):
- def __init__(self, owner, name, open_below):
- """ Initialize parent, define member variables, choose which highlight
- to use based on whether or not we have a gui (MacVim/Gvim).
- """
+ def __init__(self, owner, name='registers'):
+ FrameKeyValuePane.__init__(self, owner, name, open_below=True)
- VimPane.__init__(self, owner, name, open_below)
+ def format_register(self, reg):
+ """ Returns a tuple of strings ("name", "value") for SBRegister reg. """
+ name = reg.GetName()
+ val = reg.GetValue()
+ if val is None:
+ val = "..."
+ return (name, val.strip())
- # Map-of-maps key/value history { frame --> { variable_name, variable_value } }
- self.frameValues = {}
+ def get_frame_content(self, frame):
+ """ Returns a list of key-value pairs ("name", "value") of registers in frame """
- if have_gui():
- self.changedHighlight = VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_GUI
- else:
- self.changedHighlight = VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_TERM
- self.define_highlight(VimPane.CHANGED_VALUE_HIGHLIGHT_NAME_TERM,
- VimPane.CHANGED_VALUE_HIGHLIGHT_COLOUR_TERM)
-
- def format_pair(self, key, value, changed = False):
- """ Formats a key/value pair. Appends a '*' if changed == True """
- marker = '*' if changed else ' '
- return "%s %s = %s\n" % (marker, key, value)
-
- def get_content(self, target, controller):
- """ Get content for a frame-aware pane. Also builds the list of lines that
- need highlighting (i.e. changed values.)
- """
- if target is None or not target.IsValid():
- return VimPane.MSG_NO_TARGET
+ result = []
+ for register_sets in frame.GetRegisters():
+ # hack the register group name into the list of registers...
+ result.append((" = = %s =" % register_sets.GetName(), ""))
- self.changedLines = []
+ for reg in register_sets:
+ result.append(self.format_register(reg))
+ return result
- (frame, err) = get_selected_frame(target)
- if frame is None:
- return err
- output = get_description(frame)
- lineNum = 1
+class CommandPane(VimPane):
+ """ Pane that displays the output of an LLDB command """
- # Retrieve the last values displayed for this frame
- frameId = get_description(frame.GetBlock())
- if frameId in self.frameValues:
- frameOldValues = self.frameValues[frameId]
- else:
- frameOldValues = {}
-
- # Read the frame variables
- vals = self.get_frame_content(frame)
- for (key, value) in vals:
- lineNum += 1
- if len(frameOldValues) == 0 or (key in frameOldValues and frameOldValues[key] == value):
- output += self.format_pair(key, value)
- else:
- output += self.format_pair(key, value, True)
- self.changedLines.append(lineNum)
-
- # Save values as oldValues
- newValues = {}
- for (key, value) in vals:
- newValues[key] = value
- self.frameValues[frameId] = newValues
-
- return output
-
- def get_highlights(self):
- ret = {}
- ret[self.changedHighlight] = self.changedLines
- return ret
+ def __init__(self, owner, name, open_below, process_required=True):
+ VimPane.__init__(self, owner, name, open_below)
+ self.process_required = process_required
-class LocalsPane(FrameKeyValuePane):
- """ Pane that displays local variables """
- def __init__(self, owner, name = 'locals'):
- FrameKeyValuePane.__init__(self, owner, name, open_below=True)
-
- # FIXME: allow users to customize display of args/locals/statics/scope
- self.arguments = True
- self.show_locals = True
- self.show_statics = True
- self.show_in_scope_only = True
-
- def format_variable(self, var):
- """ Returns a Tuple of strings "(Type) Name", "Value" for SBValue var """
- val = var.GetValue()
- if val is None:
- # If the value is too big, SBValue.GetValue() returns None; replace with ...
- val = "..."
-
- return ("(%s) %s" % (var.GetTypeName(), var.GetName()), "%s" % val)
-
- def get_frame_content(self, frame):
- """ Returns list of key-value pairs of local variables in frame """
- vals = frame.GetVariables(self.arguments,
- self.show_locals,
- self.show_statics,
- self.show_in_scope_only)
- return [self.format_variable(x) for x in vals]
+ def setCommand(self, command, args=""):
+ self.command = command
+ self.args = args
-class RegistersPane(FrameKeyValuePane):
- """ Pane that displays the contents of registers """
- def __init__(self, owner, name = 'registers'):
- FrameKeyValuePane.__init__(self, owner, name, open_below=True)
-
- def format_register(self, reg):
- """ Returns a tuple of strings ("name", "value") for SBRegister reg. """
- name = reg.GetName()
- val = reg.GetValue()
- if val is None:
- val = "..."
- return (name, val.strip())
-
- def get_frame_content(self, frame):
- """ Returns a list of key-value pairs ("name", "value") of registers in frame """
-
- result = []
- for register_sets in frame.GetRegisters():
- # hack the register group name into the list of registers...
- result.append((" = = %s =" % register_sets.GetName(), ""))
-
- for reg in register_sets:
- result.append(self.format_register(reg))
- return result
+ def get_content(self, target, controller):
+ output = ""
+ if not target:
+ output = VimPane.MSG_NO_TARGET
+ elif self.process_required and not target.GetProcess():
+ output = VimPane.MSG_NO_PROCESS
+ else:
+ (success, output) = controller.getCommandOutput(
+ self.command, self.args)
+ return output
-class CommandPane(VimPane):
- """ Pane that displays the output of an LLDB command """
- def __init__(self, owner, name, open_below, process_required=True):
- VimPane.__init__(self, owner, name, open_below)
- self.process_required = process_required
-
- def setCommand(self, command, args = ""):
- self.command = command
- self.args = args
-
- def get_content(self, target, controller):
- output = ""
- if not target:
- output = VimPane.MSG_NO_TARGET
- elif self.process_required and not target.GetProcess():
- output = VimPane.MSG_NO_PROCESS
- else:
- (success, output) = controller.getCommandOutput(self.command, self.args)
- return output
class StoppedCommandPane(CommandPane):
- """ Pane that displays the output of an LLDB command when the process is
- stopped; otherwise displays process status. This class also implements
- highlighting for a single line (to show a single-line selected entity.)
- """
- def __init__(self, owner, name, open_below):
- """ Initialize parent and define highlight to use for selected line. """
- CommandPane.__init__(self, owner, name, open_below)
- if have_gui():
- self.selectedHighlight = VimPane.SELECTED_HIGHLIGHT_NAME_GUI
- else:
- self.selectedHighlight = VimPane.SELECTED_HIGHLIGHT_NAME_TERM
- self.define_highlight(VimPane.SELECTED_HIGHLIGHT_NAME_TERM,
- VimPane.SELECTED_HIGHLIGHT_COLOUR_TERM)
-
- def get_content(self, target, controller):
- """ Returns the output of a command that relies on the process being stopped.
- If the process is not in 'stopped' state, the process status is returned.
+ """ Pane that displays the output of an LLDB command when the process is
+ stopped; otherwise displays process status. This class also implements
+ highlighting for a single line (to show a single-line selected entity.)
"""
- output = ""
- if not target or not target.IsValid():
- output = VimPane.MSG_NO_TARGET
- elif not target.GetProcess() or not target.GetProcess().IsValid():
- output = VimPane.MSG_NO_PROCESS
- elif target.GetProcess().GetState() == lldb.eStateStopped:
- (success, output) = controller.getCommandOutput(self.command, self.args)
- else:
- (success, output) = controller.getCommandOutput("process", "status")
- return output
- def get_highlights(self):
- """ Highlight the line under the cursor. Users moving the cursor has
- no effect on the selected line.
- """
- ret = {}
- line = self.get_selected_line()
- if line is not None:
- ret[self.selectedHighlight] = [line]
- return ret
- return ret
-
- def get_selected_line(self):
- """ Subclasses implement this to control where the cursor (and selected highlight)
- is placed.
- """
- return None
+ def __init__(self, owner, name, open_below):
+ """ Initialize parent and define highlight to use for selected line. """
+ CommandPane.__init__(self, owner, name, open_below)
+ if have_gui():
+ self.selectedHighlight = VimPane.SELECTED_HIGHLIGHT_NAME_GUI
+ else:
+ self.selectedHighlight = VimPane.SELECTED_HIGHLIGHT_NAME_TERM
+ self.define_highlight(VimPane.SELECTED_HIGHLIGHT_NAME_TERM,
+ VimPane.SELECTED_HIGHLIGHT_COLOUR_TERM)
+
+ def get_content(self, target, controller):
+ """ Returns the output of a command that relies on the process being stopped.
+ If the process is not in 'stopped' state, the process status is returned.
+ """
+ output = ""
+ if not target or not target.IsValid():
+ output = VimPane.MSG_NO_TARGET
+ elif not target.GetProcess() or not target.GetProcess().IsValid():
+ output = VimPane.MSG_NO_PROCESS
+ elif target.GetProcess().GetState() == lldb.eStateStopped:
+ (success, output) = controller.getCommandOutput(
+ self.command, self.args)
+ else:
+ (success, output) = controller.getCommandOutput("process", "status")
+ return output
+
+ def get_highlights(self):
+ """ Highlight the line under the cursor. Users moving the cursor has
+ no effect on the selected line.
+ """
+ ret = {}
+ line = self.get_selected_line()
+ if line is not None:
+ ret[self.selectedHighlight] = [line]
+ return ret
+ return ret
+
+ def get_selected_line(self):
+ """ Subclasses implement this to control where the cursor (and selected highlight)
+ is placed.
+ """
+ return None
+
class DisassemblyPane(CommandPane):
- """ Pane that displays disassembly around PC """
- def __init__(self, owner, name = 'disassembly'):
- CommandPane.__init__(self, owner, name, open_below=True)
+ """ Pane that displays disassembly around PC """
+
+ def __init__(self, owner, name='disassembly'):
+ CommandPane.__init__(self, owner, name, open_below=True)
+
+ # FIXME: let users customize the number of instructions to disassemble
+ self.setCommand("disassemble", "-c %d -p" % self.maxHeight)
- # FIXME: let users customize the number of instructions to disassemble
- self.setCommand("disassemble", "-c %d -p" % self.maxHeight)
class ThreadPane(StoppedCommandPane):
- """ Pane that displays threads list """
- def __init__(self, owner, name = 'threads'):
- StoppedCommandPane.__init__(self, owner, name, open_below=False)
- self.setCommand("thread", "list")
+ """ Pane that displays threads list """
+
+ def __init__(self, owner, name='threads'):
+ StoppedCommandPane.__init__(self, owner, name, open_below=False)
+ self.setCommand("thread", "list")
# FIXME: the function below assumes threads are listed in sequential order,
# which turns out to not be the case. Highlighting of selected thread
@@ -592,27 +634,36 @@ class ThreadPane(StoppedCommandPane):
# else:
# return thread.GetIndexID() + 1
+
class BacktracePane(StoppedCommandPane):
- """ Pane that displays backtrace """
- def __init__(self, owner, name = 'backtrace'):
- StoppedCommandPane.__init__(self, owner, name, open_below=False)
- self.setCommand("bt", "")
+ """ Pane that displays backtrace """
+ def __init__(self, owner, name='backtrace'):
+ StoppedCommandPane.__init__(self, owner, name, open_below=False)
+ self.setCommand("bt", "")
- def get_selected_line(self):
- """ Returns the line number in the buffer with the selected frame.
- Formula: selected_line = selected_frame_id + 2
- FIXME: the above formula hack does not work when the function return
- value is printed in the bt window; the wrong line is highlighted.
- """
+ def get_selected_line(self):
+ """ Returns the line number in the buffer with the selected frame.
+ Formula: selected_line = selected_frame_id + 2
+ FIXME: the above formula hack does not work when the function return
+ value is printed in the bt window; the wrong line is highlighted.
+ """
+
+ (frame, err) = get_selected_frame(self.target)
+ if frame is None:
+ return None
+ else:
+ return frame.GetFrameID() + 2
- (frame, err) = get_selected_frame(self.target)
- if frame is None:
- return None
- else:
- return frame.GetFrameID() + 2
class BreakpointsPane(CommandPane):
- def __init__(self, owner, name = 'breakpoints'):
- super(BreakpointsPane, self).__init__(owner, name, open_below=False, process_required=False)
- self.setCommand("breakpoint", "list")
+
+ def __init__(self, owner, name='breakpoints'):
+ super(
+ BreakpointsPane,
+ self).__init__(
+ owner,
+ name,
+ open_below=False,
+ process_required=False)
+ self.setCommand("breakpoint", "list")
diff --git a/lldb/utils/vim-lldb/python-vim-lldb/vim_signs.py b/lldb/utils/vim-lldb/python-vim-lldb/vim_signs.py
index 926cc29a7fc..94f79ecebe8 100644
--- a/lldb/utils/vim-lldb/python-vim-lldb/vim_signs.py
+++ b/lldb/utils/vim-lldb/python-vim-lldb/vim_signs.py
@@ -3,71 +3,79 @@
import vim
+
class VimSign(object):
- SIGN_TEXT_BREAKPOINT_RESOLVED = "B>"
- SIGN_TEXT_BREAKPOINT_UNRESOLVED = "b>"
- SIGN_TEXT_PC = "->"
- SIGN_HIGHLIGHT_COLOUR_PC = 'darkblue'
-
- # unique sign id (for ':[sign/highlight] define)
- sign_id = 1
-
- # unique name id (for ':sign place')
- name_id = 1
-
- # Map of {(sign_text, highlight_colour) --> sign_name}
- defined_signs = {}
-
- def __init__(self, sign_text, buffer, line_number, highlight_colour=None):
- """ Define the sign and highlight (if applicable) and show the sign. """
-
- # Get the sign name, either by defining it, or looking it up in the map of defined signs
- key = (sign_text, highlight_colour)
- if not key in VimSign.defined_signs:
- name = self.define(sign_text, highlight_colour)
- else:
- name = VimSign.defined_signs[key]
-
- self.show(name, buffer.number, line_number)
- pass
-
- def define(self, sign_text, highlight_colour):
- """ Defines sign and highlight (if highlight_colour is not None). """
- sign_name = "sign%d" % VimSign.name_id
- if highlight_colour is None:
- vim.command("sign define %s text=%s" % (sign_name, sign_text))
- else:
- self.highlight_name = "highlight%d" % VimSign.name_id
- vim.command("highlight %s ctermbg=%s guibg=%s" % (self.highlight_name,
- highlight_colour,
- highlight_colour))
- vim.command("sign define %s text=%s linehl=%s texthl=%s" % (sign_name,
- sign_text,
- self.highlight_name,
- self.highlight_name))
- VimSign.defined_signs[(sign_text, highlight_colour)] = sign_name
- VimSign.name_id += 1
- return sign_name
-
-
- def show(self, name, buffer_number, line_number):
- self.id = VimSign.sign_id
- VimSign.sign_id += 1
- vim.command("sign place %d name=%s line=%d buffer=%s" % (self.id, name, line_number, buffer_number))
- pass
-
- def hide(self):
- vim.command("sign unplace %d" % self.id)
- pass
+ SIGN_TEXT_BREAKPOINT_RESOLVED = "B>"
+ SIGN_TEXT_BREAKPOINT_UNRESOLVED = "b>"
+ SIGN_TEXT_PC = "->"
+ SIGN_HIGHLIGHT_COLOUR_PC = 'darkblue'
+
+ # unique sign id (for ':[sign/highlight] define)
+ sign_id = 1
+
+ # unique name id (for ':sign place')
+ name_id = 1
+
+ # Map of {(sign_text, highlight_colour) --> sign_name}
+ defined_signs = {}
+
+ def __init__(self, sign_text, buffer, line_number, highlight_colour=None):
+ """ Define the sign and highlight (if applicable) and show the sign. """
+
+ # Get the sign name, either by defining it, or looking it up in the map
+ # of defined signs
+ key = (sign_text, highlight_colour)
+ if key not in VimSign.defined_signs:
+ name = self.define(sign_text, highlight_colour)
+ else:
+ name = VimSign.defined_signs[key]
+
+ self.show(name, buffer.number, line_number)
+ pass
+
+ def define(self, sign_text, highlight_colour):
+ """ Defines sign and highlight (if highlight_colour is not None). """
+ sign_name = "sign%d" % VimSign.name_id
+ if highlight_colour is None:
+ vim.command("sign define %s text=%s" % (sign_name, sign_text))
+ else:
+ self.highlight_name = "highlight%d" % VimSign.name_id
+ vim.command(
+ "highlight %s ctermbg=%s guibg=%s" %
+ (self.highlight_name, highlight_colour, highlight_colour))
+ vim.command(
+ "sign define %s text=%s linehl=%s texthl=%s" %
+ (sign_name, sign_text, self.highlight_name, self.highlight_name))
+ VimSign.defined_signs[(sign_text, highlight_colour)] = sign_name
+ VimSign.name_id += 1
+ return sign_name
+
+ def show(self, name, buffer_number, line_number):
+ self.id = VimSign.sign_id
+ VimSign.sign_id += 1
+ vim.command("sign place %d name=%s line=%d buffer=%s" %
+ (self.id, name, line_number, buffer_number))
+ pass
+
+ def hide(self):
+ vim.command("sign unplace %d" % self.id)
+ pass
+
class BreakpointSign(VimSign):
- def __init__(self, buffer, line_number, is_resolved):
- txt = VimSign.SIGN_TEXT_BREAKPOINT_RESOLVED if is_resolved else VimSign.SIGN_TEXT_BREAKPOINT_UNRESOLVED
- super(BreakpointSign, self).__init__(txt, buffer, line_number)
+
+ def __init__(self, buffer, line_number, is_resolved):
+ txt = VimSign.SIGN_TEXT_BREAKPOINT_RESOLVED if is_resolved else VimSign.SIGN_TEXT_BREAKPOINT_UNRESOLVED
+ super(BreakpointSign, self).__init__(txt, buffer, line_number)
+
class PCSign(VimSign):
- def __init__(self, buffer, line_number, is_selected_thread):
- super(PCSign, self).__init__(VimSign.SIGN_TEXT_PC,
- buffer,
- line_number,
- VimSign.SIGN_HIGHLIGHT_COLOUR_PC if is_selected_thread else None)
+
+ def __init__(self, buffer, line_number, is_selected_thread):
+ super(
+ PCSign,
+ self).__init__(
+ VimSign.SIGN_TEXT_PC,
+ buffer,
+ line_number,
+ VimSign.SIGN_HIGHLIGHT_COLOUR_PC if is_selected_thread else None)
diff --git a/lldb/utils/vim-lldb/python-vim-lldb/vim_ui.py b/lldb/utils/vim-lldb/python-vim-lldb/vim_ui.py
index 4be346b96f0..33eb6650466 100644
--- a/lldb/utils/vim-lldb/python-vim-lldb/vim_ui.py
+++ b/lldb/utils/vim-lldb/python-vim-lldb/vim_ui.py
@@ -1,235 +1,255 @@
# LLDB UI state in the Vim user interface.
-import os, re, sys
+import os
+import re
+import sys
import lldb
import vim
from vim_panes import *
from vim_signs import *
+
def is_same_file(a, b):
- """ returns true if paths a and b are the same file """
- a = os.path.realpath(a)
- b = os.path.realpath(b)
- return a in b or b in a
+ """ returns true if paths a and b are the same file """
+ a = os.path.realpath(a)
+ b = os.path.realpath(b)
+ return a in b or b in a
+
class UI:
- def __init__(self):
- """ Declare UI state variables """
-
- # Default panes to display
- self.defaultPanes = ['breakpoints', 'backtrace', 'locals', 'threads', 'registers', 'disassembly']
-
- # map of tuples (filename, line) --> SBBreakpoint
- self.markedBreakpoints = {}
-
- # Currently shown signs
- self.breakpointSigns = {}
- self.pcSigns = []
-
- # Container for panes
- self.paneCol = PaneLayout()
-
- # All possible LLDB panes
- self.backtracePane = BacktracePane(self.paneCol)
- self.threadPane = ThreadPane(self.paneCol)
- self.disassemblyPane = DisassemblyPane(self.paneCol)
- self.localsPane = LocalsPane(self.paneCol)
- self.registersPane = RegistersPane(self.paneCol)
- self.breakPane = BreakpointsPane(self.paneCol)
-
- def activate(self):
- """ Activate UI: display default set of panes """
- self.paneCol.prepare(self.defaultPanes)
-
- def get_user_buffers(self, filter_name=None):
- """ Returns a list of buffers that are not a part of the LLDB UI. That is, they
- are not contained in the PaneLayout object self.paneCol.
- """
- ret = []
- for w in vim.windows:
- b = w.buffer
- if not self.paneCol.contains(b.name):
- if filter_name is None or filter_name in b.name:
- ret.append(b)
- return ret
-
- def update_pc(self, process, buffers, goto_file):
- """ Place the PC sign on the PC location of each thread's selected frame """
-
- def GetPCSourceLocation(thread):
- """ Returns a tuple (thread_index, file, line, column) that represents where
- the PC sign should be placed for a thread.
- """
-
- frame = thread.GetSelectedFrame()
- frame_num = frame.GetFrameID()
- le = frame.GetLineEntry()
- while not le.IsValid() and frame_num < thread.GetNumFrames():
- frame_num += 1
- le = thread.GetFrameAtIndex(frame_num).GetLineEntry()
-
- if le.IsValid():
- path = os.path.join(le.GetFileSpec().GetDirectory(), le.GetFileSpec().GetFilename())
- return (thread.GetIndexID(), path, le.GetLine(), le.GetColumn())
- return None
-
-
- # Clear all existing PC signs
- del_list = []
- for sign in self.pcSigns:
- sign.hide()
- del_list.append(sign)
- for sign in del_list:
- self.pcSigns.remove(sign)
- del sign
-
- # Select a user (non-lldb) window
- if not self.paneCol.selectWindow(False):
- # No user window found; avoid clobbering by splitting
- vim.command(":vsp")
-
- # Show a PC marker for each thread
- for thread in process:
- loc = GetPCSourceLocation(thread)
- if not loc:
- # no valid source locations for PCs. hide all existing PC markers
- continue
-
- buf = None
- (tid, fname, line, col) = loc
- buffers = self.get_user_buffers(fname)
- is_selected = thread.GetIndexID() == process.GetSelectedThread().GetIndexID()
- if len(buffers) == 1:
- buf = buffers[0]
- if buf != vim.current.buffer:
- # Vim has an open buffer to the required file: select it
- vim.command('execute ":%db"' % buf.number)
- elif is_selected and vim.current.buffer.name not in fname and os.path.exists(fname) and goto_file:
- # FIXME: If current buffer is modified, vim will complain when we try to switch away.
- # Find a way to detect if the current buffer is modified, and...warn instead?
- vim.command('execute ":e %s"' % fname)
- buf = vim.current.buffer
- elif len(buffers) > 1 and goto_file:
- #FIXME: multiple open buffers match PC location
- continue
- else:
- continue
-
- self.pcSigns.append(PCSign(buf, line, is_selected))
-
- if is_selected and goto_file:
- # if the selected file has a PC marker, move the cursor there too
- curname = vim.current.buffer.name
- if curname is not None and is_same_file(curname, fname):
- move_cursor(line, 0)
- elif move_cursor:
- print "FIXME: not sure where to move cursor because %s != %s " % (vim.current.buffer.name, fname)
-
- def update_breakpoints(self, target, buffers):
- """ Decorates buffer with signs corresponding to breakpoints in target. """
-
- def GetBreakpointLocations(bp):
- """ Returns a list of tuples (resolved, filename, line) where a breakpoint was resolved. """
- if not bp.IsValid():
- sys.stderr.write("breakpoint is invalid, no locations")
- return []
-
- ret = []
- numLocs = bp.GetNumLocations()
- for i in range(numLocs):
- loc = bp.GetLocationAtIndex(i)
- desc = get_description(loc, lldb.eDescriptionLevelFull)
- match = re.search('at\ ([^:]+):([\d]+)', desc)
- try:
- lineNum = int(match.group(2).strip())
- ret.append((loc.IsResolved(), match.group(1), lineNum))
- except ValueError as e:
- sys.stderr.write("unable to parse breakpoint location line number: '%s'" % match.group(2))
- sys.stderr.write(str(e))
-
- return ret
-
-
- if target is None or not target.IsValid():
- return
-
- needed_bps = {}
- for bp_index in range(target.GetNumBreakpoints()):
- bp = target.GetBreakpointAtIndex(bp_index)
- locations = GetBreakpointLocations(bp)
- for (is_resolved, file, line) in GetBreakpointLocations(bp):
- for buf in buffers:
- if file in buf.name:
- needed_bps[(buf, line, is_resolved)] = bp
-
- # Hide any signs that correspond with disabled breakpoints
- del_list = []
- for (b, l, r) in self.breakpointSigns:
- if (b, l, r) not in needed_bps:
- self.breakpointSigns[(b, l, r)].hide()
- del_list.append((b, l, r))
- for d in del_list:
- del self.breakpointSigns[d]
-
- # Show any signs for new breakpoints
- for (b, l, r) in needed_bps:
- bp = needed_bps[(b, l, r)]
- if self.haveBreakpoint(b.name, l):
- self.markedBreakpoints[(b.name, l)].append(bp)
- else:
- self.markedBreakpoints[(b.name, l)] = [bp]
-
- if (b, l, r) not in self.breakpointSigns:
- s = BreakpointSign(b, l, r)
- self.breakpointSigns[(b, l, r)] = s
-
- def update(self, target, status, controller, goto_file=False):
- """ Updates debugger info panels and breakpoint/pc marks and prints
- status to the vim status line. If goto_file is True, the user's
- cursor is moved to the source PC location in the selected frame.
- """
-
- self.paneCol.update(target, controller)
- self.update_breakpoints(target, self.get_user_buffers())
-
- if target is not None and target.IsValid():
- process = target.GetProcess()
- if process is not None and process.IsValid():
- self.update_pc(process, self.get_user_buffers, goto_file)
-
- if status is not None and len(status) > 0:
- print status
-
- def haveBreakpoint(self, file, line):
- """ Returns True if we have a breakpoint at file:line, False otherwise """
- return (file, line) in self.markedBreakpoints
-
- def getBreakpoints(self, fname, line):
- """ Returns the LLDB SBBreakpoint object at fname:line """
- if self.haveBreakpoint(fname, line):
- return self.markedBreakpoints[(fname, line)]
- else:
- return None
-
- def deleteBreakpoints(self, name, line):
- del self.markedBreakpoints[(name, line)]
-
- def showWindow(self, name):
- """ Shows (un-hides) window pane specified by name """
- if not self.paneCol.havePane(name):
- sys.stderr.write("unknown window: %s" % name)
- return False
- self.paneCol.prepare([name])
- return True
-
- def hideWindow(self, name):
- """ Hides window pane specified by name """
- if not self.paneCol.havePane(name):
- sys.stderr.write("unknown window: %s" % name)
- return False
- self.paneCol.hide([name])
- return True
+
+ def __init__(self):
+ """ Declare UI state variables """
+
+ # Default panes to display
+ self.defaultPanes = [
+ 'breakpoints',
+ 'backtrace',
+ 'locals',
+ 'threads',
+ 'registers',
+ 'disassembly']
+
+ # map of tuples (filename, line) --> SBBreakpoint
+ self.markedBreakpoints = {}
+
+ # Currently shown signs
+ self.breakpointSigns = {}
+ self.pcSigns = []
+
+ # Container for panes
+ self.paneCol = PaneLayout()
+
+ # All possible LLDB panes
+ self.backtracePane = BacktracePane(self.paneCol)
+ self.threadPane = ThreadPane(self.paneCol)
+ self.disassemblyPane = DisassemblyPane(self.paneCol)
+ self.localsPane = LocalsPane(self.paneCol)
+ self.registersPane = RegistersPane(self.paneCol)
+ self.breakPane = BreakpointsPane(self.paneCol)
+
+ def activate(self):
+ """ Activate UI: display default set of panes """
+ self.paneCol.prepare(self.defaultPanes)
+
+ def get_user_buffers(self, filter_name=None):
+ """ Returns a list of buffers that are not a part of the LLDB UI. That is, they
+ are not contained in the PaneLayout object self.paneCol.
+ """
+ ret = []
+ for w in vim.windows:
+ b = w.buffer
+ if not self.paneCol.contains(b.name):
+ if filter_name is None or filter_name in b.name:
+ ret.append(b)
+ return ret
+
+ def update_pc(self, process, buffers, goto_file):
+ """ Place the PC sign on the PC location of each thread's selected frame """
+
+ def GetPCSourceLocation(thread):
+ """ Returns a tuple (thread_index, file, line, column) that represents where
+ the PC sign should be placed for a thread.
+ """
+
+ frame = thread.GetSelectedFrame()
+ frame_num = frame.GetFrameID()
+ le = frame.GetLineEntry()
+ while not le.IsValid() and frame_num < thread.GetNumFrames():
+ frame_num += 1
+ le = thread.GetFrameAtIndex(frame_num).GetLineEntry()
+
+ if le.IsValid():
+ path = os.path.join(
+ le.GetFileSpec().GetDirectory(),
+ le.GetFileSpec().GetFilename())
+ return (
+ thread.GetIndexID(),
+ path,
+ le.GetLine(),
+ le.GetColumn())
+ return None
+
+ # Clear all existing PC signs
+ del_list = []
+ for sign in self.pcSigns:
+ sign.hide()
+ del_list.append(sign)
+ for sign in del_list:
+ self.pcSigns.remove(sign)
+ del sign
+
+ # Select a user (non-lldb) window
+ if not self.paneCol.selectWindow(False):
+ # No user window found; avoid clobbering by splitting
+ vim.command(":vsp")
+
+ # Show a PC marker for each thread
+ for thread in process:
+ loc = GetPCSourceLocation(thread)
+ if not loc:
+ # no valid source locations for PCs. hide all existing PC
+ # markers
+ continue
+
+ buf = None
+ (tid, fname, line, col) = loc
+ buffers = self.get_user_buffers(fname)
+ is_selected = thread.GetIndexID() == process.GetSelectedThread().GetIndexID()
+ if len(buffers) == 1:
+ buf = buffers[0]
+ if buf != vim.current.buffer:
+ # Vim has an open buffer to the required file: select it
+ vim.command('execute ":%db"' % buf.number)
+ elif is_selected and vim.current.buffer.name not in fname and os.path.exists(fname) and goto_file:
+ # FIXME: If current buffer is modified, vim will complain when we try to switch away.
+ # Find a way to detect if the current buffer is modified,
+ # and...warn instead?
+ vim.command('execute ":e %s"' % fname)
+ buf = vim.current.buffer
+ elif len(buffers) > 1 and goto_file:
+ # FIXME: multiple open buffers match PC location
+ continue
+ else:
+ continue
+
+ self.pcSigns.append(PCSign(buf, line, is_selected))
+
+ if is_selected and goto_file:
+ # if the selected file has a PC marker, move the cursor there
+ # too
+ curname = vim.current.buffer.name
+ if curname is not None and is_same_file(curname, fname):
+ move_cursor(line, 0)
+ elif move_cursor:
+ print "FIXME: not sure where to move cursor because %s != %s " % (vim.current.buffer.name, fname)
+
+ def update_breakpoints(self, target, buffers):
+ """ Decorates buffer with signs corresponding to breakpoints in target. """
+
+ def GetBreakpointLocations(bp):
+ """ Returns a list of tuples (resolved, filename, line) where a breakpoint was resolved. """
+ if not bp.IsValid():
+ sys.stderr.write("breakpoint is invalid, no locations")
+ return []
+
+ ret = []
+ numLocs = bp.GetNumLocations()
+ for i in range(numLocs):
+ loc = bp.GetLocationAtIndex(i)
+ desc = get_description(loc, lldb.eDescriptionLevelFull)
+ match = re.search('at\ ([^:]+):([\d]+)', desc)
+ try:
+ lineNum = int(match.group(2).strip())
+ ret.append((loc.IsResolved(), match.group(1), lineNum))
+ except ValueError as e:
+ sys.stderr.write(
+ "unable to parse breakpoint location line number: '%s'" %
+ match.group(2))
+ sys.stderr.write(str(e))
+
+ return ret
+
+ if target is None or not target.IsValid():
+ return
+
+ needed_bps = {}
+ for bp_index in range(target.GetNumBreakpoints()):
+ bp = target.GetBreakpointAtIndex(bp_index)
+ locations = GetBreakpointLocations(bp)
+ for (is_resolved, file, line) in GetBreakpointLocations(bp):
+ for buf in buffers:
+ if file in buf.name:
+ needed_bps[(buf, line, is_resolved)] = bp
+
+ # Hide any signs that correspond with disabled breakpoints
+ del_list = []
+ for (b, l, r) in self.breakpointSigns:
+ if (b, l, r) not in needed_bps:
+ self.breakpointSigns[(b, l, r)].hide()
+ del_list.append((b, l, r))
+ for d in del_list:
+ del self.breakpointSigns[d]
+
+ # Show any signs for new breakpoints
+ for (b, l, r) in needed_bps:
+ bp = needed_bps[(b, l, r)]
+ if self.haveBreakpoint(b.name, l):
+ self.markedBreakpoints[(b.name, l)].append(bp)
+ else:
+ self.markedBreakpoints[(b.name, l)] = [bp]
+
+ if (b, l, r) not in self.breakpointSigns:
+ s = BreakpointSign(b, l, r)
+ self.breakpointSigns[(b, l, r)] = s
+
+ def update(self, target, status, controller, goto_file=False):
+ """ Updates debugger info panels and breakpoint/pc marks and prints
+ status to the vim status line. If goto_file is True, the user's
+ cursor is moved to the source PC location in the selected frame.
+ """
+
+ self.paneCol.update(target, controller)
+ self.update_breakpoints(target, self.get_user_buffers())
+
+ if target is not None and target.IsValid():
+ process = target.GetProcess()
+ if process is not None and process.IsValid():
+ self.update_pc(process, self.get_user_buffers, goto_file)
+
+ if status is not None and len(status) > 0:
+ print status
+
+ def haveBreakpoint(self, file, line):
+ """ Returns True if we have a breakpoint at file:line, False otherwise """
+ return (file, line) in self.markedBreakpoints
+
+ def getBreakpoints(self, fname, line):
+ """ Returns the LLDB SBBreakpoint object at fname:line """
+ if self.haveBreakpoint(fname, line):
+ return self.markedBreakpoints[(fname, line)]
+ else:
+ return None
+
+ def deleteBreakpoints(self, name, line):
+ del self.markedBreakpoints[(name, line)]
+
+ def showWindow(self, name):
+ """ Shows (un-hides) window pane specified by name """
+ if not self.paneCol.havePane(name):
+ sys.stderr.write("unknown window: %s" % name)
+ return False
+ self.paneCol.prepare([name])
+ return True
+
+ def hideWindow(self, name):
+ """ Hides window pane specified by name """
+ if not self.paneCol.havePane(name):
+ sys.stderr.write("unknown window: %s" % name)
+ return False
+ self.paneCol.hide([name])
+ return True
global ui
ui = UI()
OpenPOWER on IntegriCloud