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
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
command! -nargs=+ -complete=file VBGattachGDB call s:attachGDB(<f-args>)
|
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 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 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'})
|
command! -nargs=+ -complete=file VBGstartPDB2 call vebugger#pdb#start([<f-args>][0],{'args':[<f-args>][1:],'version':'2'})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user