summaryrefslogtreecommitdiffstats
path: root/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py')
-rw-r--r--lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py415
1 files changed, 0 insertions, 415 deletions
diff --git a/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py b/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py
deleted file mode 100644
index 0e59cc5f4b7..00000000000
--- a/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py
+++ /dev/null
@@ -1,415 +0,0 @@
-
-#
-# This file defines the layer that talks to lldb
-#
-
-from __future__ import print_function
-
-import os
-import re
-import sys
-import lldb
-import vim
-from vim_ui import UI
-
-# =================================================
-# Convert some enum value to its string counterpart
-# =================================================
-
-# 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 = [_f for _f in [result.GetStringAtIndex(x)
- for x in range(result.GetSize())] if _f]
- 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))
-
-
-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))
-
-global ctrl
-ctrl = LLDBController()
OpenPOWER on IntegriCloud