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 __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.commandInterpreter.HandleCommand("settings set target.load-script-from-symbol-file false", lldb.SBCommandReturnObject()) self.ui = UI()
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() # during step/continue do not return from function until process stops # async is enabled by default, but overridden in vimrc g:lldb_enable_async vimrc_lldb_async = vim.eval('s:lldb_async') if (vimrc_lldb_async == 0): self.dbg.SetAsync(False) else: self.dbg.SetAsync(True) self.commandInterpreter = self.dbg.GetCommandInterpreter() self.ui = UI()
def __init__(self, vifx): """ Creates the LLDB SBDebugger object and initializes the UI class. vifx represents the parent LLInterface object. """ self.target = None self.process = None self.load_dependent_modules = True self.dbg = lldb.SBDebugger.Create() self.command_interpreter = self.dbg.GetCommandInterpreter() self.vifx = vifx self.ui = UI(vifx)
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.commandInterpreter.HandleCommand("settings set target.load-script-from-symbol-file false", lldb.SBCommandReturnObject()) 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 doAttachById(self, process_id): """ Handle process attach. """ error = lldb.SBError() self.processListener = lldb.SBListener("process_event_listener") self.target = self.dbg.CreateTarget('') self.process = self.target.AttachToProcessWithID(self.processListener, int(process_id), 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_id, self.pid) 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)
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)
class LLController(object): """ Handles LLDB events and commands. """ # Timeout (sec) for waiting on new events. Due to the current single threaded design, # we should wait a few seconds for any new event, and update vim buffers. After which, # the user will have to manually request an update (using :LLrefresh). eventDelay = 2 # FIXME see processPendingEvents() def __init__(self, vifx): """ Creates the LLDB SBDebugger object and initializes the UI class. vifx represents the parent LLInterface object. """ self.target = None self.process = None self.load_dependent_modules = True self.dbg = lldb.SBDebugger.Create() self.command_interpreter = self.dbg.GetCommandInterpreter() self.vifx = vifx self.ui = UI(vifx) def complete_command(self, arg, line, pos): """ Returns a list of viable completions for line, and cursor at pos. """ result = lldb.SBStringList() num = self.command_interpreter.HandleCompletion(line, pos, 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 update_ui(self, status='', goto_file=False, buf=None): """ Update lldb buffers and signs placed in source files. @param status The message to be printed on success on the vim status line. @param goto_file Whether or not to move the cursor to the program counter (PC). @param buf If None, all buffers and signs excepts breakpoints would be updated. If '!all', all buffers incl. breakpoints would be updated. Otherwise, update only the specified buffer. """ excl = ['breakpoints'] commander = self.get_command_result if buf is None: self.ui.update(self.target, commander, status, goto_file, excl) elif buf == '!all': self.ui.update(self.target, commander, status, goto_file) else: self.ui.update_buffer(buf, self.target, commander) def do_frame(self, args): """ Handle 'frame' command. """ self.exec_command("frame", args) if args.startswith('s'): # select self.update_ui(goto_file=True) def do_thread(self, args): """ Handle 'thread' command. """ self.exec_command("thread", args) if args.startswith('se'): # select self.update_ui(goto_file=True) elif args[0] not in list('bil'): # not in backtrace, info, list self.processPendingEvents(self.eventDelay) def do_process(self, args): """ Handle 'process' command. """ # FIXME use do_attach/do_detach to handle attach/detach subcommands. if args.startswith("la"): # launch if self.process is not None and self.process.IsValid(): pid = self.process.GetProcessID() self.process.Destroy() (success, result) = self.get_command_result("process", args) self.process = self.target.process if not success: self.vifx.log("Error during launch: " + str(result)) 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) self.vifx.log("%s" % result, 0) elif args.startswith("i"): # interrupt if not self.process or not self.process.IsValid(): self.vifx.log("No valid process to interrupt.") return self.process.SendAsyncInterrupt() elif args.startswith("k"): # kill if not self.process or not self.process.IsValid(): self.vifx.log("No valid process to kill.") return if not self.process.Destroy().Success(): self.vifx.log("Error during kill: " + str(error)) else: self.vifx.log("Killed process (pid=%d)" % self.pid) else: self.exec_command("process", args) self.processPendingEvents(self.eventDelay) def do_attach(self, process_name): """ Handle process attach. """ (success, result) = self.get_command_result("attach", args) self.target = self.dbg.GetSelectedTarget() if not success: self.vifx.log("Error during attach: " + str(result)) return # attach succeeded, initialize variables, listeners self.process = self.target.process self.pid = self.process.GetProcessID() self.processListener = lldb.SBListener("process_event_listener") self.process.GetBroadcaster().AddListener(self.processListener, lldb.SBProcess.eBroadcastBitStateChanged) self.vifx.log(str(result), 0) def do_detach(self): """ Handle process detach. """ if self.process is not None and self.process.IsValid(): pid = self.process.GetProcessID() self.process.Detach() self.processPendingEvents(self.eventDelay) def do_target(self, args): """ Handle 'target' command. """ (success, result) = self.get_command_result("target", args) if not success: self.vifx.log(str(result)) elif args.startswith('c'): # create self.target = self.dbg.GetSelectedTarget() self.vifx.log(str(result), 0) self.processPendingEvents(self.eventDelay) def do_command(self, args): """ Handle 'command' command. """ self.ctrl.exec_command("command", args) if args.startswith('so'): # source self.processPendingEvents(self.eventDelay) self.update_ui(buf='breakpoints') def do_breakswitch(self, bufnr, line): """ Switch breakpoint at the specified line in the buffer. """ key = (bufnr, line) if self.ui.bp_list.has_key(key): bps = self.ui.bp_list[key] args = "delete %s" % " ".join([str(b.GetID()) for b in bps]) else: path = self.vifx.get_buffer_from_nr(bufnr).name args = "set -f %s -l %d" % (path, line) self.do_breakpoint(args) def do_breakpoint(self, args): """ Handle breakpoint command with command interpreter. """ self.exec_command("breakpoint", args) self.update_ui(buf="breakpoints") def do_refresh(self): """ Process pending events and update UI on request. """ status = self.processPendingEvents() def do_exit(self): """ Destroy the debugger instance. """ self.dbg.Terminate() self.dbg = None def get_command_result(self, command, args=""): """ Run command in the command interpreter and returns (success, output) """ result = lldb.SBCommandReturnObject() cmd = "%s %s" % (command, args) self.command_interpreter.HandleCommand(cmd, result) return (result.Succeeded(), result.GetOutput() if result.Succeeded() else result.GetError()) def exec_command(self, command, args, update_level=0, goto_file=False): """ Run command in the interpreter and: + Print result on the vim status line (update_level >= 0) + Update UI (update_level >= 1) + Check for and process any lldb events in queue (update_level >= 2) """ (success, output) = self.get_command_result(command, args) if success: if update_level == 0 and len(output) > 0: self.vifx.log(output, 0) if update_level == 1: self.update_ui(output, goto_file) elif update_level > 1: self.processPendingEvents(self.eventDelay, goto_file) else: self.vifx.log(output) def processPendingEvents(self, wait_seconds=0, goto_file=True): # FIXME replace this with a separate thread """ 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. """ 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 self.update_ui(goto_file=goto_file, buf='!all')