Added the lldb debugger via the lldb python api
This commit is contained in:
parent
3432d08467
commit
8897e79b75
139
autoload/vebugger/lldb.vim
Normal file
139
autoload/vebugger/lldb.vim
Normal file
@ -0,0 +1,139 @@
|
||||
let s:script_dir_path=expand('<sfile>:p:h')
|
||||
|
||||
function! vebugger#lldb#start(binaryFile,args)
|
||||
let l:debuggerExe=vebugger#util#getToolFullPath('python2',get(a:args,'version'),'python2')
|
||||
let l:debugger=vebugger#std#startDebugger(shellescape(l:debuggerExe)
|
||||
\.' '.s:script_dir_path.'/lldb_wrapper.py '.fnameescape(a:binaryFile))
|
||||
|
||||
let l:debugger.state.lldb={}
|
||||
|
||||
if get(a:args,'pid') "Attach to process
|
||||
call l:debugger.writeLine('process attach --pid '.string(a:args.pid))
|
||||
elseif has_key(a:args,'con') "Attach to lldbserver
|
||||
call l:debugger.writeLine('platform connect connect://'.a:args.con)
|
||||
else
|
||||
call l:debugger.writeLine('settings set target.run-args '.vebugger#util#commandLineArgsForProgram(a:args))
|
||||
if !has('win32')
|
||||
call vebugger#std#openShellBuffer(l:debugger)
|
||||
endif
|
||||
|
||||
" TODO: remove 'and false'; add a temporary breakpoint to lldb
|
||||
if has_key(a:args,'entry') && 0
|
||||
" call l:debugger.writeLine('tbreak '.a:args.entry)
|
||||
" call l:debugger.writeLine('run')
|
||||
else
|
||||
call l:debugger.writeLine('breakpoint set --name main')
|
||||
call l:debugger.writeLine('process launch')
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
call l:debugger.addReadHandler(function('vebugger#lldb#_readProgramOutput'))
|
||||
call l:debugger.addReadHandler(function('vebugger#lldb#_readWhere'))
|
||||
call l:debugger.addReadHandler(function('vebugger#lldb#_readFinish'))
|
||||
call l:debugger.addReadHandler(function('vebugger#lldb#_readEvaluatedExpressions'))
|
||||
|
||||
call l:debugger.setWriteHandler('std','flow',function('vebugger#lldb#_writeFlow'))
|
||||
call l:debugger.setWriteHandler('std','breakpoints',function('vebugger#lldb#_writeBreakpoints'))
|
||||
call l:debugger.setWriteHandler('std','closeDebugger',function('vebugger#lldb#_closeDebugger'))
|
||||
call l:debugger.setWriteHandler('std','evaluateExpressions',function('vebugger#lldb#_requestEvaluateExpression'))
|
||||
call l:debugger.setWriteHandler('std','executeStatements',function('vebugger#lldb#_executeStatements'))
|
||||
|
||||
call l:debugger.generateWriteActionsFromTemplate()
|
||||
|
||||
call l:debugger.std_addAllBreakpointActions(g:vebugger_breakpoints)
|
||||
|
||||
return l:debugger
|
||||
endfunction
|
||||
|
||||
function! vebugger#lldb#_readProgramOutput(pipeName,line,readResult,debugger)
|
||||
if 'out'==a:pipeName
|
||||
\&&(a:line=~'\v^program_stdout:'
|
||||
\||a:line=~'\v^program_stderr:')
|
||||
let a:readResult.std.programOutput={'line':strpart(a:line, 16)}
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vebugger#lldb#_readWhere(pipeName,line,readResult,debugger)
|
||||
if 'out'==a:pipeName
|
||||
\&&a:line=~'\v^debugger_output:'
|
||||
let l:matches=matchlist(a:line,'\v^debugger_output:\s\*\s([^:]+):(\d+)')
|
||||
if 2<len(l:matches)
|
||||
let l:file=l:matches[1]
|
||||
let l:file=fnamemodify(l:file,':~:.')
|
||||
let a:readResult.std.location={
|
||||
\'file':(l:file),
|
||||
\'line':str2nr(l:matches[2])}
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vebugger#lldb#_readFinish(pipeName,line,readResult,debugger)
|
||||
if 'out'==a:pipeName
|
||||
\&&a:line=~'\v^program_state:\sExited'
|
||||
let a:readResult.std.programFinish={'finish':1}
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vebugger#lldb#_writeFlow(writeAction,debugger)
|
||||
if 'stepin'==a:writeAction
|
||||
call a:debugger.writeLine('step')
|
||||
elseif 'stepover'==a:writeAction
|
||||
call a:debugger.writeLine('next')
|
||||
elseif 'stepout'==a:writeAction
|
||||
call a:debugger.writeLine('finish')
|
||||
elseif 'continue'==a:writeAction
|
||||
call a:debugger.writeLine('continue')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vebugger#lldb#_closeDebugger(writeAction,debugger)
|
||||
call a:debugger.writeLine('quit')
|
||||
endfunction
|
||||
|
||||
function! vebugger#lldb#_writeBreakpoints(writeAction,debugger)
|
||||
for l:breakpoint in a:writeAction
|
||||
if 'add'==(l:breakpoint.action)
|
||||
call a:debugger.writeLine('br '.fnameescape(l:breakpoint.file).':'.l:breakpoint.line)
|
||||
elseif 'remove'==l:breakpoint.action
|
||||
call a:debugger.writeLine('clear '.fnameescape(l:breakpoint.file).':'.l:breakpoint.line)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! vebugger#lldb#_requestEvaluateExpression(writeAction,debugger)
|
||||
for l:evalAction in a:writeAction
|
||||
call a:debugger.writeLine('print '.l:evalAction.expression)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! vebugger#lldb#_executeStatements(writeAction,debugger)
|
||||
for l:evalAction in a:writeAction
|
||||
if has_key(l:evalAction,'statement')
|
||||
"Use eval to run the statement - but first we need to remove the ;
|
||||
call a:debugger.writeLine('print '.substitute(l:evalAction.statement,'\v;\s*$','',''))
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! vebugger#lldb#_readEvaluatedExpressions(pipeName,line,readResult,debugger) dict
|
||||
if 'out' == a:pipeName
|
||||
if has_key(self, 'nextExpressionToBePrinted')
|
||||
\&&a:line=~'\v^debugger_output:'
|
||||
let l:matches=matchlist(a:line,'\v^[^\$]*\$(\d+) \= (.*)$')
|
||||
if 2<len(l:matches)
|
||||
let l:expression=l:matches[1]
|
||||
let l:value=l:matches[2]
|
||||
let a:readResult.std.evaluatedExpression={
|
||||
\'expression':self.nextExpressionToBePrinted,
|
||||
\'value':(l:value)}
|
||||
endif
|
||||
call remove(self,'nextExpressionToBePrinted')
|
||||
else
|
||||
let l:matches=matchlist(a:line,'\v^print (.+)$')
|
||||
if 1<len(l:matches)
|
||||
let self.nextExpressionToBePrinted=l:matches[1]
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endfunction
|
165
autoload/vebugger/lldb_wrapper.py
Executable file
165
autoload/vebugger/lldb_wrapper.py
Executable file
@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python2
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import lldb
|
||||
import sys
|
||||
|
||||
|
||||
class NoCustomCommandError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Debugger(object):
|
||||
class BreakpointManager(object):
|
||||
def __init__(self):
|
||||
self._next_breakpoint_id = 1
|
||||
self._location_to_id = {}
|
||||
|
||||
def add_breakpoint(self, filename, linenumber):
|
||||
location = (filename, linenumber)
|
||||
breakpoint_id = self._next_breakpoint_id
|
||||
self._location_to_id[location] = breakpoint_id
|
||||
self._next_breakpoint_id += 1
|
||||
return breakpoint_id
|
||||
|
||||
def remove_breakpoint(self, filename, linenumber):
|
||||
location = (filename, linenumber)
|
||||
breakpoint_id = self._location_to_id[location]
|
||||
del self._location_to_id[location]
|
||||
return breakpoint_id
|
||||
|
||||
def __init__(self, executable):
|
||||
self._executable = executable
|
||||
self._debugger = lldb.SBDebugger.Create()
|
||||
self._debugger.SetAsync(False)
|
||||
self._command_interpreter = self._debugger.GetCommandInterpreter()
|
||||
error = lldb.SBError()
|
||||
self._target = self._debugger.CreateTarget(
|
||||
self._executable, None, None, True, error
|
||||
)
|
||||
self._process = None
|
||||
self._last_debugger_return_obj = None
|
||||
self._state_dict = None
|
||||
self._breakpoint_manager = self.BreakpointManager()
|
||||
self._custom_commands = ['br', 'clear']
|
||||
|
||||
def run_command(self, commandline):
|
||||
if self._is_custom_command(commandline):
|
||||
self._run_custom_command(commandline)
|
||||
else:
|
||||
if isinstance(commandline, unicode):
|
||||
commandline = commandline.encode('utf-8')
|
||||
return_obj = lldb.SBCommandReturnObject()
|
||||
self._command_interpreter.HandleCommand(commandline, return_obj)
|
||||
if self._process is None or self._process.GetState() == lldb.eStateInvalid:
|
||||
self._process = self._command_interpreter.GetProcess()
|
||||
self._last_debugger_return_obj = return_obj
|
||||
|
||||
def _is_custom_command(self, commandline):
|
||||
command = commandline.split()[0]
|
||||
return command in self._custom_commands
|
||||
|
||||
def _run_custom_command(self, commandline):
|
||||
def br(arguments):
|
||||
filename, linenumber = arguments[0].split(':')
|
||||
linenumber = int(linenumber)
|
||||
self._breakpoint_manager.add_breakpoint(filename, linenumber)
|
||||
self.run_command('b {}'.format(*arguments))
|
||||
|
||||
def clear(arguments):
|
||||
filename, linenumber = arguments[0].split(':')
|
||||
linenumber = int(linenumber)
|
||||
breakpoint_id = self._breakpoint_manager.remove_breakpoint(filename,
|
||||
linenumber)
|
||||
self.run_command('breakpoint delete {:d}'.format(breakpoint_id))
|
||||
|
||||
if not self._is_custom_command(commandline):
|
||||
raise NoCustomCommandError
|
||||
parts = commandline.split()
|
||||
print('parts:', parts)
|
||||
command = parts[0]
|
||||
arguments = parts[1:]
|
||||
locals()[command](arguments)
|
||||
|
||||
@property
|
||||
def debugger_output(self):
|
||||
if self._last_debugger_return_obj is not None:
|
||||
return_obj = self._last_debugger_return_obj
|
||||
self._last_debugger_return_obj = None
|
||||
return return_obj.GetOutput()
|
||||
else:
|
||||
return ''
|
||||
|
||||
@property
|
||||
def program_stdout(self):
|
||||
stdout = [self._process.GetSTDOUT(1024)]
|
||||
while stdout[-1]:
|
||||
stdout.append(self._process.GetSTDOUT(1024))
|
||||
return ''.join(stdout)
|
||||
|
||||
@property
|
||||
def program_stderr(self):
|
||||
stderr = [self._process.GetSTDERR(1024)]
|
||||
while stderr[-1]:
|
||||
stderr.append(self._process.GetSTDERR(1024))
|
||||
return ''.join(stderr)
|
||||
|
||||
@property
|
||||
def program_state(self):
|
||||
return self._state_id_to_name(self._process.GetState())
|
||||
|
||||
def _state_id_to_name(self, state_id):
|
||||
if self._state_dict is None:
|
||||
self._state_dict = {}
|
||||
for key, value in lldb.__dict__.iteritems():
|
||||
if key.startswith('eState'):
|
||||
self._state_dict[value] = key[6:]
|
||||
return self._state_dict[state_id]
|
||||
|
||||
|
||||
def prefix_output(output, prefix):
|
||||
lines = output.split('\n')
|
||||
lines = [prefix + line for line in lines]
|
||||
prefixed_output = '\n'.join(lines)
|
||||
return prefixed_output
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
sys.stderr.write('An executable is needed as an argument.\n')
|
||||
sys.exit(1)
|
||||
|
||||
executable = sys.argv[1]
|
||||
debugger = Debugger(executable)
|
||||
|
||||
# set debugger options
|
||||
debugger.run_command('settings set thread-format ${file.fullpath}:${line.number}')
|
||||
debugger.run_command('settings set auto-confirm 1')
|
||||
|
||||
try:
|
||||
while True:
|
||||
line = raw_input()
|
||||
# TODO: find a way to check directly if the debugger was terminated
|
||||
if line in ['exit', 'quit']:
|
||||
raise EOFError
|
||||
debugger.run_command(line)
|
||||
program_stdout = debugger.program_stdout
|
||||
if program_stdout:
|
||||
print(prefix_output(program_stdout, 'program_stdout: '))
|
||||
program_stderr = debugger.program_stderr
|
||||
if program_stderr:
|
||||
print(prefix_output(program_stderr, 'program_stderr: '))
|
||||
print(prefix_output(debugger.debugger_output, 'debugger_output: '))
|
||||
print(prefix_output(debugger.program_state, 'program_state: '))
|
||||
except EOFError:
|
||||
print('Exiting')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -41,6 +41,24 @@ function! s:attachGDB(...)
|
||||
endif
|
||||
endfunction
|
||||
command! -nargs=+ -complete=file VBGattachGDB call s:attachGDB(<f-args>)
|
||||
command! -nargs=+ -complete=file VBGstartLLDB call vebugger#lldb#start([<f-args>][0],{'args':[<f-args>][1:]})
|
||||
function! s:attachLLDB(...)
|
||||
if 1 == a:0
|
||||
let l:processId=vebugger#util#selectProcessOfFile(a:1)
|
||||
if 0 < l:processId
|
||||
call vebugger#lldb#start(a:1, {'pid': l:processId})
|
||||
endif
|
||||
elseif 2 == a:0
|
||||
if a:2 =~ '\v^\d+$'
|
||||
call vebugger#lldb#start(a:1,{'pid': str2nr(a:2)})
|
||||
else
|
||||
call vebugger#lldb#start(a:1, {'con': a:2})
|
||||
endif
|
||||
else
|
||||
throw "Can't call VBGattachLLDB with ".a:0." arguments"
|
||||
endif
|
||||
endfunction
|
||||
command! -nargs=+ -complete=file VBGattachLLDB call s:attachLLDB(<f-args>)
|
||||
command! -nargs=+ -complete=file VBGstartRDebug call vebugger#rdebug#start([<f-args>][0],{'args':[<f-args>][1:]})
|
||||
command! -nargs=+ -complete=file VBGstartPDB call vebugger#pdb#start([<f-args>][0],{'args':[<f-args>][1:]})
|
||||
command! -nargs=+ -complete=file VBGstartPDB2 call vebugger#pdb#start([<f-args>][0],{'args':[<f-args>][1:],'version':'2'})
|
||||
|
Loading…
Reference in New Issue
Block a user