Merge branch 'develop' into node-inspect
This commit is contained in:
commit
1c87c72d52
@ -10,12 +10,13 @@ supports:
|
||||
* Debugger flow commands - step-in, set-over, set-out and continue
|
||||
* Breakpoints management
|
||||
* Evaluating expressions in the current executed scope
|
||||
* Messing with the program's state(changing values, calling functions)
|
||||
* Messing with the program's state (changing values, calling functions)
|
||||
|
||||
Vebugger is built as a generic framework for building frontends for
|
||||
interactive shell debugger, and comes with implementations for:
|
||||
|
||||
* GDB - doesn't need introdcution...
|
||||
* GDB - doesn't need introduction...
|
||||
* LLDB - debugger based on LLVM for C-family languages
|
||||
* JDB - a Java debugger
|
||||
* Mdbg - a .NET debugger(Windows only)
|
||||
* PDB - a Python module for debugging Python scripts
|
||||
|
@ -1,199 +1,199 @@
|
||||
"Read and return all new lines from a Vebugger pipe object.
|
||||
function! s:readNewLinesFromPipe(pipeObject)
|
||||
"read
|
||||
let l:text=a:pipeObject.pipe.read(1000,0)
|
||||
while 0<len(l:text)
|
||||
let a:pipeObject.buffer.=l:text
|
||||
let l:text=a:pipeObject.pipe.read(1000,0)
|
||||
endwhile
|
||||
|
||||
"parse
|
||||
let l:lastNewline=strridx(a:pipeObject.buffer,"\n")
|
||||
if 0<=l:lastNewline
|
||||
let l:outLines=split(strpart(a:pipeObject.buffer,0,l:lastNewline),'\r\n\|\n\|\r')
|
||||
let a:pipeObject.buffer=strpart(a:pipeObject.buffer,l:lastNewline+1)
|
||||
return l:outLines
|
||||
endif
|
||||
|
||||
return []
|
||||
"Read bytes from pipe to buffer
|
||||
function! s:fillBufferFromPipe(pipeObject)
|
||||
let l:text = a:pipeObject.pipe.read(1024, 0)
|
||||
let l:totalBytesRead = 0
|
||||
while 0 < len(l:text)
|
||||
let l:totalBytesRead += len(l:text)
|
||||
let a:pipeObject.buffer .= l:text
|
||||
let l:text = a:pipeObject.pipe.read(1024, 0)
|
||||
endwhile
|
||||
return l:totalBytesRead
|
||||
endfunction
|
||||
|
||||
let s:f_debugger={}
|
||||
|
||||
"Terminate the debugger
|
||||
function! s:f_debugger.kill() dict
|
||||
if self.shell.is_valid
|
||||
call self.addLineToTerminal('','== DEBUGGER TERMINATED ==')
|
||||
endif
|
||||
let &updatetime=self.prevUpdateTime
|
||||
call self.shell.kill(15)
|
||||
if exists('s:debugger')
|
||||
for l:closeHandler in s:debugger.closeHandlers
|
||||
call l:closeHandler.handle(self)
|
||||
endfor
|
||||
endif
|
||||
if self.shell.is_valid
|
||||
call self.addLineToTerminal('','== DEBUGGER TERMINATED ==')
|
||||
endif
|
||||
if !has('timers')
|
||||
let &updatetime=self.prevUpdateTime
|
||||
endif
|
||||
call self.shell.kill(15)
|
||||
if exists('s:debugger')
|
||||
for l:closeHandler in s:debugger.closeHandlers
|
||||
call l:closeHandler.handle(self)
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Write a line to the debugger's interactive shell
|
||||
function! s:f_debugger.writeLine(line) dict
|
||||
call self.shell.stdin.write(a:line."\n")
|
||||
call self.shell.stdin.write(a:line."\n")
|
||||
endfunction
|
||||
|
||||
"Check for new lines from the debugger's interactive shell and handle them
|
||||
function! s:f_debugger.invokeReading() dict
|
||||
let l:newLines={}
|
||||
for l:k in keys(self.pipes)
|
||||
let l:nl=s:readNewLinesFromPipe(self.pipes[l:k])
|
||||
if 0<len(l:nl)
|
||||
let l:newLines[l:k]=l:nl
|
||||
endif
|
||||
endfor
|
||||
for l:k in keys(l:newLines)
|
||||
for l:line in l:newLines[l:k]
|
||||
call self.handleLine(l:k,l:line)
|
||||
endfor
|
||||
endfor
|
||||
let l:newLines = {}
|
||||
for l:k in keys(self.pipes)
|
||||
let l:pipe = self.pipes[l:k]
|
||||
if 0 < s:fillBufferFromPipe(l:pipe)
|
||||
let l:nl = l:pipe.bufferer()
|
||||
if 0 < len(l:nl)
|
||||
let l:newLines[l:k] = l:nl
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
for l:k in keys(l:newLines)
|
||||
for l:line in l:newLines[l:k]
|
||||
call self.handleLine(l:k, l:line)
|
||||
endfor
|
||||
endfor
|
||||
|
||||
let l:checkpid=self.shell.checkpid()
|
||||
if 'exit'==l:checkpid[0]
|
||||
\|| 'error'==l:checkpid[0]
|
||||
call self.kill()
|
||||
endif
|
||||
call feedkeys("f\e", 'n') " Make sure the CursorHold event is refired even if the user does nothing
|
||||
let l:checkpid=self.shell.checkpid()
|
||||
if 'exit'==l:checkpid[0]
|
||||
\|| 'error'==l:checkpid[0]
|
||||
call self.kill()
|
||||
endif
|
||||
if !has('timers')
|
||||
call feedkeys("f\e", '\n') " Make sure the CursorHold event is refired even if the user does nothing
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Handle a single line from the debugger's interactive shell
|
||||
function! s:f_debugger.handleLine(pipeName,line) dict
|
||||
call self.addLineToTerminal(a:pipeName,a:line)
|
||||
call self.addLineToTerminal(a:pipeName,a:line)
|
||||
|
||||
let l:readResult=deepcopy(self.readResultTemplate,1)
|
||||
let l:readResult=deepcopy(self.readResultTemplate,1)
|
||||
|
||||
for l:readHandler in self.readHandlers
|
||||
call l:readHandler.handle(a:pipeName,a:line,l:readResult,self)
|
||||
endfor
|
||||
for l:readHandler in self.readHandlers
|
||||
call l:readHandler.handle(a:pipeName,a:line,l:readResult,self)
|
||||
endfor
|
||||
|
||||
for l:thinkHandler in self.thinkHandlers
|
||||
call l:thinkHandler.handle(l:readResult,self)
|
||||
endfor
|
||||
for l:thinkHandler in self.thinkHandlers
|
||||
call l:thinkHandler.handle(l:readResult,self)
|
||||
endfor
|
||||
|
||||
call self.performWriteActions()
|
||||
call self.performWriteActions()
|
||||
endfunction
|
||||
|
||||
"Perform all write actions
|
||||
function! s:f_debugger.performWriteActions() dict
|
||||
for l:namespace in keys(self.writeActions)
|
||||
let l:handlers=get(self.writeHandlers,l:namespace)
|
||||
if !empty(l:handlers)
|
||||
for l:writeAction in items(self.writeActions[l:namespace])
|
||||
if !empty(l:writeAction[1])
|
||||
if has_key(l:handlers,l:writeAction[0])
|
||||
call l:handlers[l:writeAction[0]].handle(l:writeAction[1],self)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
call self.generateWriteActionsFromTemplate()
|
||||
for l:namespace in keys(self.writeActions)
|
||||
let l:handlers=get(self.writeHandlers,l:namespace)
|
||||
if !empty(l:handlers)
|
||||
for l:writeAction in items(self.writeActions[l:namespace])
|
||||
if !empty(l:writeAction[1])
|
||||
if has_key(l:handlers,l:writeAction[0])
|
||||
call l:handlers[l:writeAction[0]].handle(l:writeAction[1],self)
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
call self.generateWriteActionsFromTemplate()
|
||||
endfunction
|
||||
|
||||
"Show the terminal buffer that gets it's content from the debugger's
|
||||
"interactive shell
|
||||
function! s:f_debugger.showTerminalBuffer() dict
|
||||
if has_key(self,'terminalBuffer')
|
||||
if -1<bufwinnr(self.terminalBuffer)
|
||||
return
|
||||
endif
|
||||
endif
|
||||
new
|
||||
setlocal buftype=nofile
|
||||
setlocal bufhidden=wipe
|
||||
let self.terminalBuffer=bufnr('')
|
||||
silent file Vebugger:Ternimal
|
||||
wincmd p
|
||||
if has_key(self,'terminalBuffer')
|
||||
if -1<bufwinnr(self.terminalBuffer)
|
||||
return
|
||||
endif
|
||||
endif
|
||||
new
|
||||
setlocal buftype=nofile
|
||||
setlocal bufhidden=wipe
|
||||
setlocal filetype=VebuggerTerminal
|
||||
let self.terminalBuffer=bufnr('')
|
||||
silent file Vebugger:Terminal
|
||||
wincmd p
|
||||
endfunction
|
||||
|
||||
"Close the terminal buffer
|
||||
function! s:f_debugger.closeTerminalBuffer() dict
|
||||
if has_key(self,'terminalBuffer')
|
||||
if -1<bufwinnr(self.terminalBuffer)
|
||||
let l:bufwin=bufwinnr(self.terminalBuffer)
|
||||
exe l:bufwin.'wincmd w'
|
||||
wincmd c
|
||||
wincmd p
|
||||
endif
|
||||
endif
|
||||
if has_key(self,'terminalBuffer')
|
||||
if -1<bufwinnr(self.terminalBuffer)
|
||||
let l:bufwin=bufwinnr(self.terminalBuffer)
|
||||
exe l:bufwin.'wincmd w'
|
||||
wincmd c
|
||||
wincmd p
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Check if the terminal buffer associated with this debugger is currently open
|
||||
function! s:f_debugger.isTerminalBufferOpen() dict
|
||||
if has_key(self,'terminalBuffer')
|
||||
if -1<bufwinnr(self.terminalBuffer)
|
||||
return 1
|
||||
endif
|
||||
endif
|
||||
return 0
|
||||
if has_key(self,'terminalBuffer')
|
||||
if -1<bufwinnr(self.terminalBuffer)
|
||||
return 1
|
||||
endif
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
"Turn on and off the terminal buffer associated with this debugger
|
||||
function! s:f_debugger.toggleTerminalBuffer() dict
|
||||
if self.isTerminalBufferOpen()
|
||||
call self.closeTerminalBuffer()
|
||||
else
|
||||
call self.showTerminalBuffer()
|
||||
endif
|
||||
if self.isTerminalBufferOpen()
|
||||
call self.closeTerminalBuffer()
|
||||
else
|
||||
call self.showTerminalBuffer()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Write a line to the terminal buffer. This function does not process the line
|
||||
function! s:f_debugger.addLineToTerminal(pipeName,line) dict
|
||||
if has_key(self,'terminalBuffer')
|
||||
let l:bufwin=bufwinnr(self.terminalBuffer)
|
||||
if -1<l:bufwin
|
||||
exe l:bufwin.'wincmd w'
|
||||
if has_key(self,'pipes')
|
||||
\&&has_key(self.pipes,a:pipeName)
|
||||
\&&has_key(self.pipes[a:pipeName],'annotation')
|
||||
call append (line('$'),(self.pipes[a:pipeName].annotation).(a:line))
|
||||
else
|
||||
call append (line('$'),a:line)
|
||||
endif
|
||||
normal G
|
||||
wincmd p
|
||||
endif
|
||||
endif
|
||||
if has_key(self,'terminalBuffer')
|
||||
let l:bufwin=bufwinnr(self.terminalBuffer)
|
||||
if -1<l:bufwin
|
||||
exe 'noautocmd '.l:bufwin.'wincmd w'
|
||||
if has_key(self,'pipes')
|
||||
\&&has_key(self.pipes,a:pipeName)
|
||||
\&&has_key(self.pipes[a:pipeName],'annotation')
|
||||
call append (line('$'),(self.pipes[a:pipeName].annotation).(a:line))
|
||||
else
|
||||
call append (line('$'),a:line)
|
||||
endif
|
||||
normal G
|
||||
noautocmd wincmd p
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Add an handler to a handler list
|
||||
function! s:addHandler(list,handler)
|
||||
if type(a:handler) == type({})
|
||||
call add(a:list,a:handler)
|
||||
elseif type(a:handler) == type(function('tr'))
|
||||
call add(a:list,{'handle':a:handler})
|
||||
endif
|
||||
if type(a:handler) == type({})
|
||||
call add(a:list,a:handler)
|
||||
elseif type(a:handler) == type(function('tr'))
|
||||
call add(a:list,{'handle':a:handler})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Set a named handler in a handler dictionary
|
||||
function! s:setHandler(dict,namespace,name,handler)
|
||||
if !has_key(a:dict,a:namespace)
|
||||
let a:dict[a:namespace]={}
|
||||
endif
|
||||
if type(a:handler) == type({})
|
||||
let a:dict[a:namespace][a:name]=a:handler
|
||||
elseif type(a:handler) == type(function('tr'))
|
||||
let a:dict[a:namespace][a:name]={'handle':a:handler}
|
||||
endif
|
||||
if !has_key(a:dict,a:namespace)
|
||||
let a:dict[a:namespace]={}
|
||||
endif
|
||||
if type(a:handler) == type({})
|
||||
let a:dict[a:namespace][a:name]=a:handler
|
||||
elseif type(a:handler) == type(function('tr'))
|
||||
let a:dict[a:namespace][a:name]={'handle':a:handler}
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Add a read handler. Read handlers process output from the debugger's
|
||||
"interactive shell and modify read result objects with structured information
|
||||
"parsed from those lines
|
||||
function! s:f_debugger.addReadHandler(handler) dict
|
||||
call s:addHandler(self.readHandlers,a:handler)
|
||||
call s:addHandler(self.readHandlers,a:handler)
|
||||
endfunction
|
||||
|
||||
"Add a think handler. Think handlers are debugger agnostic - they look at
|
||||
"read result objects and decide what to do with them.
|
||||
function! s:f_debugger.addThinkHandler(handler) dict
|
||||
call s:addHandler(self.thinkHandlers,a:handler)
|
||||
call s:addHandler(self.thinkHandlers,a:handler)
|
||||
endfunction
|
||||
|
||||
"Set a write handler. Write handlers get write action objects and convert them
|
||||
@ -201,147 +201,179 @@ endfunction
|
||||
"of the namespace and name it is registered for, to prevent the same write
|
||||
"action handled by multiple write handlers.
|
||||
function! s:f_debugger.setWriteHandler(namespace,name,handler) dict
|
||||
call s:setHandler(self.writeHandlers,a:namespace,a:name,a:handler)
|
||||
call s:setHandler(self.writeHandlers,a:namespace,a:name,a:handler)
|
||||
endfunction
|
||||
|
||||
"Add a close handler. Close handlers are called when the debugger is closed to
|
||||
"tidy things up.
|
||||
function! s:f_debugger.addCloseHandler(handler) dict
|
||||
call s:addHandler(self.closeHandlers,a:handler)
|
||||
call s:addHandler(self.closeHandlers,a:handler)
|
||||
endfunction
|
||||
|
||||
"Create an empty write action that follows the write actions template. That
|
||||
"action will later be filled by think handlers or from outside.
|
||||
function! s:f_debugger.generateWriteActionsFromTemplate() dict
|
||||
let self.writeActions=deepcopy(self.writeActionsTemplate)
|
||||
let self.writeActions=deepcopy(self.writeActionsTemplate)
|
||||
endfunction
|
||||
|
||||
"Set a write action of a specific namespace and name, for write actions that
|
||||
"do not support a list
|
||||
function! s:f_debugger.setWriteAction(namespace,name,value) dict
|
||||
let self.writeActions[a:namespace][a:name]=a:value
|
||||
let self.writeActions[a:namespace][a:name]=a:value
|
||||
endfunction
|
||||
|
||||
"Set a write action and perform it
|
||||
function! s:f_debugger.setWriteActionAndPerform(namespace, name, value) dict
|
||||
call self.setWriteAction(a:namespace, a:name, a:value)
|
||||
call self.performWriteActions()
|
||||
endfunction
|
||||
|
||||
"Add a write action of a specific namespace and name, for write actions that supports a list
|
||||
function! s:f_debugger.addWriteAction(namespace,name,value) dict
|
||||
call add(self.writeActions[a:namespace][a:name],a:value)
|
||||
call add(self.writeActions[a:namespace][a:name],a:value)
|
||||
endfunction
|
||||
|
||||
"Add a write action and perform it
|
||||
function! s:f_debugger.addWriteActionAndPerform(namespace, name, value) dict
|
||||
call self.addWriteAction(a:namespace, a:name, a:value)
|
||||
call self.performWriteActions()
|
||||
endfunction
|
||||
|
||||
"Create a bare debugger object from a raw shell line
|
||||
function! vebugger#createDebugger(command)
|
||||
|
||||
let l:debugger=deepcopy(s:f_debugger)
|
||||
let l:debugger=deepcopy(s:f_debugger)
|
||||
|
||||
let l:debugger.shell=vimproc#ptyopen(a:command,3)
|
||||
let l:debugger.shell=vimproc#ptyopen(a:command,3)
|
||||
|
||||
let l:debugger.outBuffer=''
|
||||
let l:debugger.errBuffer=''
|
||||
let l:debugger.outBuffer=''
|
||||
let l:debugger.errBuffer=''
|
||||
|
||||
let l:debugger.pipes={
|
||||
\'out':{'pipe':(l:debugger.shell.stdout),'buffer':''},
|
||||
\'err':{'pipe':(l:debugger.shell.stderr),'buffer':'','annotation':"err:\t\t"}}
|
||||
let l:debugger.pipes = {
|
||||
\ 'out': {'pipe':(l:debugger.shell.stdout), 'buffer': ''},
|
||||
\ 'err': {'pipe':(l:debugger.shell.stderr), 'buffer': '', 'annotation': "err:\t\t"}}
|
||||
for l:pipe in values(l:debugger.pipes)
|
||||
"let l:pipe.buffer = ''
|
||||
"let l:pipe.readIntoBuffer = function('vebugger#readIntoBuffer')
|
||||
"let l:pipe.bufferer = function('vebugger#readNewLinesFromPipe')
|
||||
endfor
|
||||
|
||||
let l:debugger.readResultTemplate={}
|
||||
let l:debugger.state={}
|
||||
let l:debugger.writeActionsTemplate={}
|
||||
let l:debugger.readResultTemplate={}
|
||||
let l:debugger.state={}
|
||||
let l:debugger.writeActionsTemplate={}
|
||||
|
||||
let l:debugger.readHandlers=[]
|
||||
let l:debugger.thinkHandlers=[]
|
||||
let l:debugger.writeHandlers={}
|
||||
let l:debugger.closeHandlers=[]
|
||||
let l:debugger.readHandlers=[]
|
||||
let l:debugger.thinkHandlers=[]
|
||||
let l:debugger.writeHandlers={}
|
||||
let l:debugger.closeHandlers=[]
|
||||
|
||||
let l:debugger.prevUpdateTime=&updatetime
|
||||
if !has('timers')
|
||||
let l:debugger.prevUpdateTime=&updatetime
|
||||
set updatetime=500
|
||||
endif
|
||||
|
||||
set updatetime=500
|
||||
return l:debugger
|
||||
return l:debugger
|
||||
endfunction
|
||||
|
||||
if has('timers')
|
||||
function! s:readingTimerCallback(timerId)
|
||||
call s:debugger.invokeReading()
|
||||
endfunction
|
||||
endif
|
||||
|
||||
"Create a debugger and set it as the currently active debugger
|
||||
function! vebugger#startDebugger(command)
|
||||
call vebugger#killDebugger()
|
||||
call vebugger#killDebugger()
|
||||
|
||||
let s:debugger=vebugger#createDebugger(a:command)
|
||||
let s:debugger=vebugger#createDebugger(a:command)
|
||||
|
||||
augroup vebugger_shell
|
||||
autocmd!
|
||||
autocmd CursorHold * call s:debugger.invokeReading()
|
||||
augroup END
|
||||
if has('timers')
|
||||
let s:timerId = timer_start(500, function('s:readingTimerCallback'), {'repeat': -1})
|
||||
else
|
||||
augroup vebugger_shell
|
||||
autocmd!
|
||||
autocmd CursorHold * call s:debugger.invokeReading()
|
||||
augroup END
|
||||
endif
|
||||
|
||||
return s:debugger
|
||||
return s:debugger
|
||||
endfunction
|
||||
|
||||
"Terminate the currently active debugger
|
||||
function! vebugger#killDebugger()
|
||||
augroup vebugger_shell
|
||||
autocmd!
|
||||
augroup END
|
||||
if exists('s:debugger')
|
||||
call s:debugger.closeTerminalBuffer()
|
||||
call s:debugger.kill()
|
||||
unlet s:debugger
|
||||
endif
|
||||
if has('timers')
|
||||
if exists('s:timerId')
|
||||
call timer_stop(s:timerId)
|
||||
unlet s:timerId
|
||||
endif
|
||||
else
|
||||
augroup vebugger_shell
|
||||
autocmd!
|
||||
augroup END
|
||||
endif
|
||||
if exists('s:debugger')
|
||||
call vebugger#std#closeShellBuffer(s:debugger)
|
||||
call s:debugger.closeTerminalBuffer()
|
||||
call s:debugger.kill()
|
||||
unlet s:debugger
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Perform an action on the actvie debugger, and register that action
|
||||
function! vebugger#userAction(action, ...)
|
||||
if exists('s:debugger')
|
||||
if has_key(s:debugger, a:action)
|
||||
let s:debugger.lastUserAction = {
|
||||
\'action': a:action,
|
||||
\'args': a:000}
|
||||
|
||||
try
|
||||
doautocmd User Vebugger_PreUserAction
|
||||
finally
|
||||
call call(s:debugger[a:action], a:000, s:debugger)
|
||||
doautocmd User Vebugger_PostUserAction
|
||||
endtry
|
||||
else
|
||||
throw 'Current debugger does not support action '.a:action
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
augroup vebugger_hooks
|
||||
autocmd!
|
||||
"Make a blank action hook to prevent 'No matching autocommands" warning
|
||||
autocmd User Vebugger_* echo
|
||||
augroup END
|
||||
|
||||
"Repeat the last action performed on the active debugger
|
||||
function! vebugger#repeatLastUserAction()
|
||||
if exists('s:debugger')
|
||||
if has_key(s:debugger, 'lastUserAction')
|
||||
let l:lastUserAction = s:debugger.lastUserAction
|
||||
call call(s:debugger[l:lastUserAction.action], l:lastUserAction.args, s:debugger)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Write a line to the currently active debugger
|
||||
function! vebugger#writeLine(line)
|
||||
if exists('s:debugger')
|
||||
call s:debugger.writeLine(a:line)
|
||||
endif
|
||||
if exists('s:debugger')
|
||||
call s:debugger.writeLine(a:line)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Invoke reading for the currently active debugger
|
||||
function! vebugger#invokeReading()
|
||||
if exists('s:debugger')
|
||||
call s:debugger.invokeReading()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Toggle the terminal buffer for the currently active debugger
|
||||
function! vebugger#toggleTerminalBuffer()
|
||||
if exists('s:debugger')
|
||||
call s:debugger.toggleTerminalBuffer()
|
||||
endif
|
||||
if exists('s:debugger')
|
||||
call s:debugger.invokeReading()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Fetch the currently active debugger object
|
||||
function! vebugger#getActiveDebugger()
|
||||
if exists('s:debugger')
|
||||
return s:debugger
|
||||
else
|
||||
return {}
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Set a write action for the currently active debugger
|
||||
function! vebugger#setWriteAction(namespace,name,value)
|
||||
if exists('s:debugger')
|
||||
call s:debugger.setWriteAction(a:namespace,a:name,a:value)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Add a write action to the currently active debugger
|
||||
function! vebugger#addWriteAction(namespace,name,value)
|
||||
if exists('s:debugger')
|
||||
call s:debugger.addWriteAction(a:namespace,a:name,a:value)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Force performing all the write of the currently active debugger
|
||||
function! vebugger#performWriteActions()
|
||||
if exists('s:debugger')
|
||||
call s:debugger.performWriteActions()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Set a write action for the currently active debugger and perform it
|
||||
function! vebugger#setWriteActionAndPerform(namespace,name,value)
|
||||
call vebugger#setWriteAction(a:namespace,a:name,a:value)
|
||||
call vebugger#performWriteActions()
|
||||
endfunction
|
||||
|
||||
"Add a write action to the currently active debugger and perform it
|
||||
function! vebugger#addWriteActionAndPerform(namespace,name,value)
|
||||
call vebugger#addWriteAction(a:namespace,a:name,a:value)
|
||||
call vebugger#performWriteActions()
|
||||
if exists('s:debugger')
|
||||
return s:debugger
|
||||
else
|
||||
return {}
|
||||
endif
|
||||
endfunction
|
||||
|
@ -1,22 +1,20 @@
|
||||
function! vebugger#gdb#searchAndAttach(binaryFile)
|
||||
let l:processId=vebugger#util#selectProcessOfFile(a:binaryFile)
|
||||
if 0<l:processId
|
||||
call vebugger#gdb#start(a:binaryFile,{'pid':l:processId})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vebugger#gdb#start(binaryFile,args)
|
||||
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('gdb',get(a:args,'version'),'gdb'))
|
||||
\.' -i mi --silent '.fnameescape(a:binaryFile))
|
||||
let l:debugger.pipes.out.bufferer = function('vebugger#gdb#readMILinesFromPipe')
|
||||
let l:debugger.state.gdb={}
|
||||
|
||||
|
||||
let l:debugger.pipes.err.annotation = "err&prg\t\t"
|
||||
call l:debugger.writeLine("set width 0")
|
||||
call l:debugger.writeLine("set print pretty off")
|
||||
call l:debugger.writeLine("set print array off")
|
||||
call l:debugger.writeLine("define hook-stop\nwhere\nend")
|
||||
|
||||
if get(a:args,'pid') "Attach to process
|
||||
call l:debugger.writeLine('attach '.string(a:args.pid))
|
||||
elseif has_key(a:args,'con') "Attach to gdbserver
|
||||
call l:debugger.writeLine('target remote '.a:args.con)
|
||||
else
|
||||
call l:debugger.writeLine('set args '.vebugger#util#commandLineArgsForProgram(a:args).' 1>&2')
|
||||
if !has('win32')
|
||||
@ -62,6 +60,61 @@ function! s:findFolderFromStackTrace(src,nameFromStackTrace)
|
||||
return l:path
|
||||
endfunction
|
||||
|
||||
function! vebugger#gdb#readMILinesFromPipe() dict
|
||||
if !has_key(self, 'tildeBuffer')
|
||||
let self.tildeBuffer = ''
|
||||
endif
|
||||
let l:result = []
|
||||
|
||||
while 1
|
||||
let l:newLinePos = stridx(self.buffer, "\n")
|
||||
if l:newLinePos < 0
|
||||
break
|
||||
endif
|
||||
let l:firstChar = self.buffer[0]
|
||||
if l:firstChar == '~'
|
||||
while self.buffer[0] == '~'
|
||||
let l:newLinePos = stridx(self.buffer, "\n")
|
||||
let l:line = self.buffer[0 : l:newLinePos]
|
||||
let l:line = l:line[2 : strridx(l:line, '"') - 1]
|
||||
let self.buffer = strpart(self.buffer, l:newLinePos + 1)
|
||||
"let self.tildeBuffer .= l:line
|
||||
let l:line = substitute(l:line, '\\n', "\n", "g")
|
||||
let l:line = substitute(l:line, '\\t', "\t", "g")
|
||||
let l:line = substitute(l:line, '\\"', '"', "g")
|
||||
let self.tildeBuffer .= l:line
|
||||
endwhile
|
||||
let l:lastTildeNewLine = strridx(self.tildeBuffer, "\n")
|
||||
if 0 <= l:lastTildeNewLine
|
||||
let l:newTildeLines = split(self.tildeBuffer[: l:lastTildeNewLine - 1], '\n')
|
||||
call map(l:newTildeLines, '"~".v:val')
|
||||
let self.tildeBuffer = strpart(self.tildeBuffer, l:lastTildeNewLine + 1)
|
||||
call extend(l:result, l:newTildeLines)
|
||||
endif
|
||||
continue
|
||||
else
|
||||
if !empty(self.tildeBuffer)
|
||||
call extend(l:result, split(self.tildeBuffer, '\n'))
|
||||
let self.tildeBuffer = ''
|
||||
endif
|
||||
endif
|
||||
let l:line = self.buffer[0 : l:newLinePos]
|
||||
if l:firstChar == '&'
|
||||
let l:line = l:line[2 : strridx(l:line, '"') - 1]
|
||||
let l:line = substitute(l:line, '\\n', "\n", "g")
|
||||
let l:line = substitute(l:line, '\\t', "\t", "g")
|
||||
let l:line = substitute(l:line, '\\"', '"', "g")
|
||||
let l:line = '&'.l:line
|
||||
endif
|
||||
|
||||
let l:line = substitute(l:line, '\v[\n\r]+$', '', "")
|
||||
call add(l:result, l:line)
|
||||
let self.buffer = strpart(self.buffer, l:newLinePos + 1)
|
||||
endwhile
|
||||
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
function! vebugger#gdb#_readProgramOutput(pipeName,line,readResult,debugger)
|
||||
if 'err'==a:pipeName
|
||||
\&&a:line!~'\v^[=~*&^]'
|
||||
@ -72,11 +125,10 @@ endfunction
|
||||
|
||||
function! vebugger#gdb#_readWhere(pipeName,line,readResult,debugger)
|
||||
if 'out'==a:pipeName
|
||||
"let l:matches=matchlist(a:line,'\v^\~"#(\d+)\s+(.+)\s+\(.*\)\s+at\s+([^:]+):(\d+)')
|
||||
let l:matches=matchlist(a:line,'\v^\*stopped.*fullname\=\"([^"]+)\",line\=\"(\d+)"')
|
||||
if 2<len(l:matches)
|
||||
let l:file=l:matches[1]
|
||||
let l:file=fnamemodify(l:file,':~:.')
|
||||
let l:file=fnamemodify(l:file,':p')
|
||||
let a:readResult.std.location={
|
||||
\'file':(l:file),
|
||||
\'line':str2nr(l:matches[2])}
|
||||
@ -85,7 +137,7 @@ function! vebugger#gdb#_readWhere(pipeName,line,readResult,debugger)
|
||||
endfunction
|
||||
|
||||
function! vebugger#gdb#_readFinish(pipeName,line,readResult,debugger)
|
||||
if a:line=~'\c\V\^~"[Inferior \.\*exited normally]'
|
||||
if a:line=~'\c\V*stopped\.\*exited\[ -]normally'
|
||||
let a:readResult.std.programFinish={'finish':1}
|
||||
endif
|
||||
endfunction
|
||||
@ -132,9 +184,9 @@ function! vebugger#gdb#_executeStatements(writeAction,debugger)
|
||||
endfunction
|
||||
|
||||
function! vebugger#gdb#_readEvaluatedExpressions(pipeName,line,readResult,debugger) dict
|
||||
if 'out'==a:pipeName
|
||||
if has_key(self,'nextExpressionToBePrinted')
|
||||
let l:matches=matchlist(a:line,'\v^\~"\$(\d+) \= (.*)"$')
|
||||
if 'out' == a:pipeName
|
||||
if has_key(self, 'nextExpressionToBePrinted')
|
||||
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]
|
||||
@ -144,7 +196,7 @@ function! vebugger#gdb#_readEvaluatedExpressions(pipeName,line,readResult,debugg
|
||||
endif
|
||||
call remove(self,'nextExpressionToBePrinted')
|
||||
else
|
||||
let l:matches=matchlist(a:line,'\v^\&"print (.{-})(\\r)?(\\n)?"$')
|
||||
let l:matches=matchlist(a:line,'\v^\&print (.+)$')
|
||||
if 1<len(l:matches)
|
||||
let self.nextExpressionToBePrinted=s:unescapeString(l:matches[1])
|
||||
endif
|
||||
|
@ -1,6 +1,7 @@
|
||||
function! vebugger#jdb#start(entryClass,args)
|
||||
let l:debugger=vebugger#std#startDebugger(shellescape(vebugger#util#getToolFullPath('jdb',get(a:args,'version'),'jdb'))
|
||||
\.(has_key(a:args,'classpath') ? ' -classpath '.fnameescape(a:args.classpath) : ''))
|
||||
\.(has_key(a:args,'classpath') && !has_key(a:args,'attach') ? ' -classpath '.fnameescape(a:args.classpath) : '')
|
||||
\.(has_key(a:args,'attach') ? ' -attach '.shellescape(a:args.attach) : ''))
|
||||
let l:debugger.state.jdb={}
|
||||
if has_key(a:args,'srcpath')
|
||||
let l:debugger.state.jdb.srcpath=a:args.srcpath
|
||||
@ -9,10 +10,14 @@ function! vebugger#jdb#start(entryClass,args)
|
||||
endif
|
||||
let l:debugger.state.jdb.filesToClassesMap={}
|
||||
|
||||
call l:debugger.writeLine('stop on '.a:entryClass.'.main')
|
||||
call l:debugger.writeLine('run '.a:entryClass.' '.vebugger#util#commandLineArgsForProgram(a:args))
|
||||
if !has_key(a:args,'attach')
|
||||
call l:debugger.writeLine('stop on '.a:entryClass.'.main')
|
||||
call l:debugger.writeLine('run '.a:entryClass.' '.vebugger#util#commandLineArgsForProgram(a:args))
|
||||
else
|
||||
call l:debugger.writeLine('run')
|
||||
endif
|
||||
call l:debugger.writeLine('monitor where')
|
||||
if !has('win32')
|
||||
if !has('win32') && !has_key(a:args,'attach')
|
||||
call vebugger#std#openShellBuffer(l:debugger)
|
||||
endif
|
||||
|
||||
@ -33,6 +38,12 @@ function! vebugger#jdb#start(entryClass,args)
|
||||
return l:debugger
|
||||
endfunction
|
||||
|
||||
function! vebugger#jdb#attach(address, ...)
|
||||
let l:args = a:0 ? a:{1} : {}
|
||||
let l:args.attach = a:address
|
||||
call vebugger#jdb#start('', l:args)
|
||||
endfunction
|
||||
|
||||
function! vebugger#jdb#_readProgramOutput(pipeName,line,readResult,debugger) dict
|
||||
if 'out'==a:pipeName
|
||||
if a:line=~'\v^\> \>'
|
||||
@ -56,15 +67,46 @@ function! vebugger#jdb#_readProgramOutput(pipeName,line,readResult,debugger) dic
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:findFolderFromStackTrace(src,nameFromStackTrace)
|
||||
let l:path=a:src
|
||||
for l:dirname in split(a:nameFromStackTrace,'\.')
|
||||
let l:nextPath=l:path.'/'.fnameescape(l:dirname)
|
||||
if empty(glob(l:nextPath))
|
||||
return l:path
|
||||
endif
|
||||
let l:path=l:nextPath
|
||||
endfor
|
||||
function! s:getTagContainingString(tag, str)
|
||||
let l:tags = taglist(a:tag)
|
||||
if (len(l:tags) > 0)
|
||||
for l:tag in l:tags
|
||||
if (filereadable(l:tag.filename) && match(readfile(l:tag.filename), a:str) >= 0)
|
||||
return l:tag
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
return {}
|
||||
endfunction
|
||||
|
||||
function! s:findFolderFromStackTrace(src,nameFromStackTrace,frameNumber)
|
||||
" Remove method name.
|
||||
let l:canonicalClassName = strpart(a:nameFromStackTrace, 0, strridx(a:nameFromStackTrace, "."))
|
||||
" Remove package name.
|
||||
let l:simpleClassName = strridx(l:canonicalClassName, ".") >= 0 ? strpart(l:canonicalClassName, strridx(l:canonicalClassName, ".") + 1) : l:canonicalClassName
|
||||
" Remove class name.
|
||||
let l:package = strridx(l:canonicalClassName, ".") >= 0 ? strpart(l:canonicalClassName, 0, strridx(l:canonicalClassName, ".")) : ""
|
||||
|
||||
" We don't really use callstack, so we use tags only for the current location.
|
||||
" Otherwise it makes everything too slow.
|
||||
if exists('g:vebugger_use_tags') && g:vebugger_use_tags && a:frameNumber == 1
|
||||
" Now first try to find a tag for the class from the required package.
|
||||
let l:classTag = s:getTagContainingString(l:simpleClassName, l:package)
|
||||
if (has_key(l:classTag, "filename"))
|
||||
return fnamemodify(l:classTag.filename, ":h")
|
||||
endif
|
||||
endif
|
||||
|
||||
" If no such tag was found, try to find it using the src path.
|
||||
for l:path in vebugger#util#listify(a:src)
|
||||
for l:dirname in split(a:nameFromStackTrace,'\.')
|
||||
let l:nextPath=l:path.'/'.fnameescape(l:dirname)
|
||||
if empty(glob(l:nextPath))
|
||||
return l:path
|
||||
endif
|
||||
let l:path=l:nextPath
|
||||
endfor
|
||||
endfor
|
||||
return l:path
|
||||
endfunction
|
||||
|
||||
@ -72,9 +114,9 @@ function! vebugger#jdb#_readWhere(pipeName,line,readResult,debugger)
|
||||
if 'out'==a:pipeName
|
||||
let l:matches=matchlist(a:line,'\v\s*\[(\d+)]\s*(\S+)\s*\(([^:]*):(\d*)\)')
|
||||
if 4<len(l:matches)
|
||||
let l:file=s:findFolderFromStackTrace(a:debugger.state.jdb.srcpath,l:matches[2]).'/'.l:matches[3]
|
||||
let l:file=fnamemodify(l:file,':~:.')
|
||||
let l:frameNumber=str2nr(l:matches[1])
|
||||
let l:file=s:findFolderFromStackTrace(a:debugger.state.jdb.srcpath,l:matches[2],l:frameNumber).'/'.l:matches[3]
|
||||
let l:file=fnamemodify(l:file,':p')
|
||||
if 1==l:frameNumber " first stackframe is the current location
|
||||
let a:readResult.std.location={
|
||||
\'file':(l:file),
|
||||
@ -114,7 +156,8 @@ endfunction
|
||||
function! s:getClassNameFromFile(filename)
|
||||
let l:className=fnamemodify(a:filename,':t:r') " Get only the name of the file, without path or extension
|
||||
for l:line in readfile(a:filename)
|
||||
let l:matches=matchlist(l:line,'\vpackage\s+(%(\w|\.)+)\s*;')
|
||||
" trailing ; is optional to make it work for groovy as well
|
||||
let l:matches=matchlist(l:line,'\vpackage\s+(%(\w|\.)+)\s*;?')
|
||||
if 1<len(l:matches)
|
||||
return l:matches[1].'.'.l:className
|
||||
endif
|
||||
|
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('python','lldb','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^where:'
|
||||
let l:matches=matchlist(a:line,'\v^where:\s([^:]+):(\d+)')
|
||||
if 2<len(l:matches)
|
||||
let l:file=l:matches[1]
|
||||
let l:file=fnamemodify(l:file,':p')
|
||||
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
|
255
autoload/vebugger/lldb_wrapper.py
Executable file
255
autoload/vebugger/lldb_wrapper.py
Executable file
@ -0,0 +1,255 @@
|
||||
#!/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 collections
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
FilePosition = collections.namedtuple('FilePosition', ['filepath', 'linenumber'])
|
||||
|
||||
|
||||
try:
|
||||
# Just try for LLDB in case PYTHONPATH is already correctly setup
|
||||
import lldb
|
||||
except ImportError:
|
||||
lldb_python_dirs = []
|
||||
# lldb is not in the PYTHONPATH, try some defaults for the current platform
|
||||
platform_system = platform.system()
|
||||
if platform_system == 'Darwin':
|
||||
# On Darwin, try the currently selected Xcode directory
|
||||
try:
|
||||
xcode_dir = subprocess.check_output(['xcode-select', '--print-path'])
|
||||
except subprocess.CalledProcessError:
|
||||
xcode_dir = None
|
||||
if xcode_dir:
|
||||
lldb_python_dirs.append(
|
||||
os.path.realpath(
|
||||
os.path.join(xcode_dir, '../SharedFrameworks/LLDB.framework/Versions/A/Resources/Python')
|
||||
)
|
||||
)
|
||||
lldb_python_dirs.append(
|
||||
'/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/Python'
|
||||
)
|
||||
for lldb_python_dir in lldb_python_dirs:
|
||||
if os.path.exists(lldb_python_dir):
|
||||
if lldb_python_dir not in sys.path:
|
||||
sys.path.append(lldb_python_dir)
|
||||
try:
|
||||
import lldb
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
else:
|
||||
sys.stderr.write('Error: could not locate the "lldb" module, please set PYTHONPATH correctly\n')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
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']
|
||||
self._set_options()
|
||||
|
||||
def _set_options(self):
|
||||
# -> first read lldbinit
|
||||
try:
|
||||
with open(os.path.expanduser('~/.lldbinit')) as f:
|
||||
for line in f:
|
||||
self.run_command(line)
|
||||
except IOError:
|
||||
pass
|
||||
self.run_command('settings set frame-format frame #${frame.index}: ${frame.pc}{ ${module.file.basename}`${function.name}{${function.pc-offset}}}{ at:${line.file.fullpath}:${line.number}}\n')
|
||||
self.run_command('settings set auto-confirm 1')
|
||||
|
||||
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 where(self):
|
||||
def extract_where(backtrace):
|
||||
where = None
|
||||
pattern = re.compile('at:([^:]+):(\d+)')
|
||||
backtrace_lines = backtrace.split('\n')
|
||||
for line in backtrace_lines:
|
||||
match_obj = pattern.search(line)
|
||||
if match_obj:
|
||||
filepath = match_obj.group(1)
|
||||
linenumber = int(match_obj.group(2))
|
||||
if os.access(filepath, os.R_OK):
|
||||
where = FilePosition(filepath, linenumber)
|
||||
break
|
||||
return where
|
||||
|
||||
where = None
|
||||
self.run_command('bt')
|
||||
debugger_output = self.debugger_output
|
||||
if debugger_output is not None:
|
||||
where = extract_where(debugger_output)
|
||||
return where
|
||||
|
||||
@property
|
||||
def program_stdout(self):
|
||||
stdout = []
|
||||
has_text = True
|
||||
while has_text:
|
||||
text = self._process.GetSTDOUT(1024)
|
||||
if text:
|
||||
stdout.append(text)
|
||||
else:
|
||||
has_text = False
|
||||
return ''.join(stdout)
|
||||
|
||||
@property
|
||||
def program_stderr(self):
|
||||
stderr = []
|
||||
has_text = True
|
||||
while has_text:
|
||||
text = self._process.GetSTDERR(1024)
|
||||
if text:
|
||||
stderr.append(text)
|
||||
else:
|
||||
has_text = False
|
||||
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):
|
||||
if output is None:
|
||||
output = ''
|
||||
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)
|
||||
|
||||
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: '))
|
||||
where = debugger.where
|
||||
if where:
|
||||
print(prefix_output('{:s}:{:d}'.format(where.filepath, where.linenumber), 'where: '))
|
||||
print(prefix_output(debugger.program_state, 'program_state: '))
|
||||
except EOFError:
|
||||
print('Exiting')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -95,7 +95,7 @@ function! vebugger#mdbg#_readWhere(pipeName,line,readResult,debugger)
|
||||
if 3<len(l:matches)
|
||||
let l:frameNumber=str2nr(l:matches[1])
|
||||
let l:file=s:findFilePath(a:debugger.state.mdbg.srcpath,l:matches[3],l:matches[2])
|
||||
let l:file=fnamemodify(l:file,':~:.')
|
||||
let l:file=fnamemodify(l:file,':p')
|
||||
if 0==l:frameNumber " first stackframe is the current location
|
||||
let a:readResult.std.location={
|
||||
\'file':(l:file),
|
||||
|
@ -56,7 +56,7 @@ endfunction
|
||||
|
||||
function! vebugger#pdb#_readWhere(pipeName,line,readResult,debugger)
|
||||
if 'out'==a:pipeName
|
||||
let l:matches=matchlist(a:line,'\v^\> (.+)\((\d+)\).*\(\)$')
|
||||
let l:matches=matchlist(a:line,'\v^\> (.+)\((\d+)\).*\(\)%(-\>.*)?$')
|
||||
|
||||
if 2<len(l:matches)
|
||||
let l:file=l:matches[1]
|
||||
|
@ -1,75 +1,96 @@
|
||||
let g:vebugger_breakpoints=[]
|
||||
|
||||
"Initialize the default pipe bufferers
|
||||
function! vebugger#std#setStandardBufferers(debugger)
|
||||
for l:pipe in values(a:debugger.pipes)
|
||||
let l:pipe.bufferer = function('vebugger#std#readNewLinesFromPipe')
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
"Initialize the std part of the debugger's state
|
||||
function! vebugger#std#setStandardState(debugger)
|
||||
let a:debugger.state.std={
|
||||
\'config':{
|
||||
\ 'externalFileStop_flowCommand':''},
|
||||
\'location':{},
|
||||
\'callstack':[],
|
||||
\'evaluateExpressions':[]}
|
||||
let a:debugger.state.std={
|
||||
\'config':{
|
||||
\ 'externalFileStop_flowCommand':''},
|
||||
\'location':{},
|
||||
\'callstack':[],
|
||||
\'evaluateExpressions':[]}
|
||||
endfunction
|
||||
|
||||
"Initialize the std part of the debugger's read result template
|
||||
function! vebugger#std#setStandardReadResultTemplate(debugger)
|
||||
let a:debugger.readResultTemplate.std={
|
||||
\'programOutput':{},
|
||||
\'location':{},
|
||||
\'callstack':{},
|
||||
\'evaluatedExpression':{},
|
||||
\'programFinish':{},
|
||||
\'exception':{}}
|
||||
let a:debugger.readResultTemplate.std={
|
||||
\'programOutput':{},
|
||||
\'location':{},
|
||||
\'callstack':{},
|
||||
\'evaluatedExpression':{},
|
||||
\'programFinish':{},
|
||||
\'exception':{}}
|
||||
endfunction
|
||||
|
||||
"Initialize the std part of the debugger's write actions template
|
||||
function! vebugger#std#setStandardWriteactionsTemplate(debugger)
|
||||
let a:debugger.writeActionsTemplate.std={
|
||||
\'flow':'',
|
||||
\'breakpoints':[],
|
||||
\'evaluateExpressions':[],
|
||||
\'executeStatements':[],
|
||||
\'removeAfterDisplayed':[],
|
||||
\'closeDebugger':''}
|
||||
let a:debugger.writeActionsTemplate.std={
|
||||
\'flow':'',
|
||||
\'breakpoints':[],
|
||||
\'evaluateExpressions':[],
|
||||
\'executeStatements':[],
|
||||
\'removeAfterDisplayed':[],
|
||||
\'closeDebugger':''}
|
||||
endfunction
|
||||
|
||||
"Adds the std_ functions to the debugger object
|
||||
function! vebugger#std#addStandardFunctions(debugger)
|
||||
for l:k in keys(s:standardFunctions)
|
||||
let a:debugger['std_'.l:k]=s:standardFunctions[l:k]
|
||||
endfor
|
||||
for l:k in keys(s:standardFunctions)
|
||||
let a:debugger['std_'.l:k]=s:standardFunctions[l:k]
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
"Add the standard think handlers to the debugger
|
||||
function! vebugger#std#addStandardThinkHandlers(debugger)
|
||||
for l:ThinkHandler in values(s:standardThinkHandlers)
|
||||
call a:debugger.addThinkHandler(l:ThinkHandler)
|
||||
endfor
|
||||
for l:ThinkHandler in values(s:standardThinkHandlers)
|
||||
call a:debugger.addThinkHandler(l:ThinkHandler)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
"Add the standard close handlers to the debugger
|
||||
function! vebugger#std#addStandardCloseHandlers(debugger)
|
||||
for l:CloseHandler in values(s:standardCloseHandlers)
|
||||
call a:debugger.addCloseHandler(l:CloseHandler)
|
||||
endfor
|
||||
for l:CloseHandler in values(s:standardCloseHandlers)
|
||||
call a:debugger.addCloseHandler(l:CloseHandler)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
"Performs the standard initialization of the debugger object
|
||||
function! vebugger#std#standardInit(debugger)
|
||||
call vebugger#std#setStandardState(a:debugger)
|
||||
call vebugger#std#setStandardReadResultTemplate(a:debugger)
|
||||
call vebugger#std#setStandardWriteactionsTemplate(a:debugger)
|
||||
call vebugger#std#addStandardFunctions(a:debugger)
|
||||
call vebugger#std#addStandardThinkHandlers(a:debugger)
|
||||
call vebugger#std#addStandardCloseHandlers(a:debugger)
|
||||
call vebugger#std#setStandardBufferers(a:debugger)
|
||||
call vebugger#std#setStandardState(a:debugger)
|
||||
call vebugger#std#setStandardReadResultTemplate(a:debugger)
|
||||
call vebugger#std#setStandardWriteactionsTemplate(a:debugger)
|
||||
call vebugger#std#addStandardFunctions(a:debugger)
|
||||
call vebugger#std#addStandardThinkHandlers(a:debugger)
|
||||
call vebugger#std#addStandardCloseHandlers(a:debugger)
|
||||
endfunction
|
||||
|
||||
"Start a debugger with the std settings
|
||||
function! vebugger#std#startDebugger(command)
|
||||
let l:debugger=vebugger#startDebugger(a:command)
|
||||
let l:debugger=vebugger#startDebugger(a:command)
|
||||
|
||||
call vebugger#std#standardInit(l:debugger)
|
||||
call vebugger#std#standardInit(l:debugger)
|
||||
|
||||
return l:debugger
|
||||
return l:debugger
|
||||
endfunction
|
||||
|
||||
|
||||
"Read and return all new lines from a Vebugger pipe object.
|
||||
function! vebugger#std#readNewLinesFromPipe() dict
|
||||
let l:lastNewline = strridx(self.buffer, "\n")
|
||||
if 0 <= l:lastNewline
|
||||
let l:outLines = split(strpart(self.buffer, 0, l:lastNewline), '\r\n\|\n\|\r')
|
||||
let self.buffer = strpart(self.buffer, l:lastNewline + 1)
|
||||
return l:outLines
|
||||
endif
|
||||
|
||||
return []
|
||||
endfunction
|
||||
|
||||
|
||||
@ -78,69 +99,82 @@ endfunction
|
||||
"Shell buffers should not be used when attaching a debugger to a running
|
||||
"process.
|
||||
function! vebugger#std#openShellBuffer(debugger)
|
||||
if has_key(a:debugger,'shellBuffer')
|
||||
if -1<bufwinnr(a:debugger.shellBuffer)
|
||||
return
|
||||
endif
|
||||
endif
|
||||
let l:oldBuffer=bufnr('Vebugger:Shell')
|
||||
if -1<l:oldBuffer
|
||||
let a:debugger.shellBuffer=l:oldBuffer
|
||||
call a:debugger.std_addLineToShellBuffer('')
|
||||
call a:debugger.std_addLineToShellBuffer('==================')
|
||||
call a:debugger.std_addLineToShellBuffer('')
|
||||
return
|
||||
endif
|
||||
8 new
|
||||
let b:debugger=a:debugger
|
||||
autocmd BufDelete <buffer> if exists('b:debugger') | call b:debugger.kill() | endif
|
||||
setlocal buftype=nofile
|
||||
setlocal bufhidden=wipe
|
||||
let a:debugger.shellBuffer=bufnr('')
|
||||
silent file Vebugger:Shell
|
||||
wincmd p
|
||||
if has_key(a:debugger,'shellBuffer')
|
||||
if -1<bufwinnr(a:debugger.shellBuffer)
|
||||
return
|
||||
endif
|
||||
endif
|
||||
let l:oldBuffer=bufnr('Vebugger:Shell')
|
||||
if -1<l:oldBuffer
|
||||
let a:debugger.shellBuffer=l:oldBuffer
|
||||
call a:debugger.std_addLineToShellBuffer('')
|
||||
call a:debugger.std_addLineToShellBuffer('==================')
|
||||
call a:debugger.std_addLineToShellBuffer('')
|
||||
return
|
||||
endif
|
||||
8 new
|
||||
let b:debugger=a:debugger
|
||||
autocmd BufDelete <buffer> if exists('b:debugger') | call b:debugger.kill() | endif
|
||||
setlocal buftype=nofile
|
||||
setlocal filetype=VebuggerShell
|
||||
setlocal bufhidden=wipe
|
||||
let a:debugger.shellBuffer=bufnr('')
|
||||
silent file Vebugger:Shell
|
||||
wincmd p
|
||||
endfunction
|
||||
|
||||
"Closes the shell buffer
|
||||
function! vebugger#std#closeShellBuffer(debugger)
|
||||
if has_key(a:debugger,'shellBuffer')
|
||||
if -1<bufwinnr(a:debugger.shellBuffer)
|
||||
let l:bufwin=bufwinnr(a:debugger.shellBuffer)
|
||||
exe l:bufwin.'wincmd w'
|
||||
wincmd c
|
||||
wincmd p
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
let s:standardFunctions={}
|
||||
|
||||
"Write a line to the shell buffer
|
||||
function! s:standardFunctions.addLineToShellBuffer(line) dict
|
||||
if has_key(self,'shellBuffer')
|
||||
let l:bufwin=bufwinnr(self.shellBuffer)
|
||||
if -1<l:bufwin
|
||||
exe l:bufwin.'wincmd w'
|
||||
call append (line('$'),a:line)
|
||||
normal G
|
||||
wincmd p
|
||||
endif
|
||||
endif
|
||||
if has_key(self,'shellBuffer')
|
||||
let l:bufwin=bufwinnr(self.shellBuffer)
|
||||
if -1<l:bufwin
|
||||
exe 'noautocmd '.l:bufwin.'wincmd w'
|
||||
call append (line('$'),a:line)
|
||||
normal G
|
||||
noautocmd wincmd p
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Set the write actions to add all breakpoints registered in Vebugger
|
||||
function! s:standardFunctions.addAllBreakpointActions(breakpoints) dict
|
||||
for l:breakpoint in a:breakpoints
|
||||
call self.addWriteAction('std','breakpoints',{
|
||||
\'action':'add',
|
||||
\'file':(l:breakpoint.file),
|
||||
\'line':(l:breakpoint.line)})
|
||||
endfor
|
||||
for l:breakpoint in a:breakpoints
|
||||
call self.addWriteAction('std','breakpoints',{
|
||||
\'action':'add',
|
||||
\'file':(l:breakpoint.file),
|
||||
\'line':(l:breakpoint.line)})
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
"Make the debugger evaluate an expression
|
||||
function! s:standardFunctions.eval(expression) dict
|
||||
if -1==index(self.state.std.evaluateExpressions,a:expression)
|
||||
call add(self.state.std.evaluateExpressions,a:expression)
|
||||
endif
|
||||
call self.addWriteAction('std','evaluateExpressions',{
|
||||
\'expression':(a:expression)})
|
||||
call self.performWriteActions()
|
||||
if -1==index(self.state.std.evaluateExpressions,a:expression)
|
||||
call add(self.state.std.evaluateExpressions,a:expression)
|
||||
endif
|
||||
call self.addWriteAction('std','evaluateExpressions',{
|
||||
\'expression':(a:expression)})
|
||||
call self.performWriteActions()
|
||||
endfunction
|
||||
|
||||
"Execute a statement in the debugged program
|
||||
function! s:standardFunctions.execute(statement) dict
|
||||
call self.addWriteAction('std','executeStatements',{
|
||||
\'statement':(a:statement)})
|
||||
call self.performWriteActions()
|
||||
call self.addWriteAction('std','executeStatements',{
|
||||
\'statement':(a:statement)})
|
||||
call self.performWriteActions()
|
||||
endfunction
|
||||
|
||||
|
||||
@ -148,175 +182,216 @@ let s:standardThinkHandlers={}
|
||||
|
||||
"Update the shell buffer with program output
|
||||
function! s:standardThinkHandlers.addProgramOutputToShell(readResult,debugger) dict
|
||||
let l:programOutput=a:readResult.std.programOutput
|
||||
if !empty(l:programOutput)
|
||||
call a:debugger.std_addLineToShellBuffer(l:programOutput.line)
|
||||
endif
|
||||
let l:programOutput=a:readResult.std.programOutput
|
||||
if !empty(l:programOutput)
|
||||
call a:debugger.std_addLineToShellBuffer(l:programOutput.line)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Make Vim jump to the currently executed line
|
||||
function! s:standardThinkHandlers.moveToCurrentLine(readResult,debugger) dict
|
||||
if !empty(a:readResult.std.location)
|
||||
if !empty(a:debugger.state.std.config.externalFileStop_flowCommand) " Do we need to worry about stopping at external files?
|
||||
if 0!=stridx(tolower(fnamemodify(a:readResult.std.location.file,':p')),tolower(getcwd()))
|
||||
call a:debugger.setWriteAction('std','flow',a:debugger.state.std.config.externalFileStop_flowCommand)
|
||||
return
|
||||
endif
|
||||
endif
|
||||
if a:debugger.state.std.location!=a:readResult.std.location
|
||||
if has_key(a:debugger.state.std.location,'file')
|
||||
exe 'sign unplace 1 file='.fnameescape(fnamemodify(a:debugger.state.std.location.file,':p'))
|
||||
endif
|
||||
let a:debugger.state.std.location=deepcopy(a:readResult.std.location)
|
||||
if !bufexists(a:readResult.std.location.file)
|
||||
exe 'new '.(a:readResult.std.location.file)
|
||||
endif
|
||||
call vebugger#std#updateMarksForFile(a:debugger.state,a:readResult.std.location.file)
|
||||
exe 'sign jump 1 file='.fnameescape(fnamemodify(a:readResult.std.location.file,':p'))
|
||||
endif
|
||||
endif
|
||||
if !empty(a:readResult.std.location)
|
||||
if !empty(a:debugger.state.std.config.externalFileStop_flowCommand) " Do we need to worry about stopping at external files?
|
||||
if 0!=stridx(tolower(fnamemodify(a:readResult.std.location.file,':p')),tolower(getcwd()))
|
||||
call a:debugger.setWriteAction('std','flow',a:debugger.state.std.config.externalFileStop_flowCommand)
|
||||
return
|
||||
endif
|
||||
endif
|
||||
if a:debugger.state.std.location!=a:readResult.std.location
|
||||
if has_key(a:debugger.state.std.location,'file')
|
||||
exe 'sign unplace 1 file='.fnameescape(fnamemodify(a:debugger.state.std.location.file,':p'))
|
||||
endif
|
||||
let a:debugger.state.std.location=deepcopy(a:readResult.std.location)
|
||||
if -1 == bufwinnr(a:readResult.std.location.file)
|
||||
exe get(g:, 'vebugger_view_source_cmd', 'new').' '.(a:readResult.std.location.file)
|
||||
endif
|
||||
call vebugger#std#updateMarksForFile(a:debugger.state,a:readResult.std.location.file)
|
||||
exe 'sign jump 1 file='.fnameescape(fnamemodify(a:readResult.std.location.file,':p'))
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Update the call stack
|
||||
function! s:standardThinkHandlers.updateCallStack(readResult,debugger) dict
|
||||
let l:callstack=a:readResult.std.callstack
|
||||
if !empty(l:callstack)
|
||||
if get(l:callstack,'clearOld')
|
||||
let a:debugger.state.std.callstack=[]
|
||||
endif
|
||||
let l:frame={'file':(l:callstack.file),'line':(l:callstack.line)}
|
||||
if 'after'==get(l:callstack,'add')
|
||||
call add(a:debugger.state.std.callstack,l:frame)
|
||||
elseif 'before'==get(l:callstack,'add')
|
||||
call insert(a:debugger.state.std.callstack,l:frame)
|
||||
endif
|
||||
endif
|
||||
let l:callstack=a:readResult.std.callstack
|
||||
if !empty(l:callstack)
|
||||
if get(l:callstack,'clearOld')
|
||||
let a:debugger.state.std.callstack=[]
|
||||
endif
|
||||
let l:frame={'file':(l:callstack.file),'line':(l:callstack.line)}
|
||||
if 'after'==get(l:callstack,'add')
|
||||
call add(a:debugger.state.std.callstack,l:frame)
|
||||
elseif 'before'==get(l:callstack,'add')
|
||||
call insert(a:debugger.state.std.callstack,l:frame)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Helper print function; when using vim timers feedkeys ensures that messages
|
||||
"can be confirmend by the user.
|
||||
function! s:printWithFeedKeys(evaluatedExpression)
|
||||
if empty(get(a:evaluatedExpression,'expression'))
|
||||
let l:echo = a:evaluatedExpression.value
|
||||
else
|
||||
let l:echo = a:evaluatedExpression.expression.': '.a:evaluatedExpression.value
|
||||
endif
|
||||
let l:echoLines = split(l:echo, '\r\n\|\n\|\r')
|
||||
if len(l:echoLines) == 1
|
||||
call add(l:echoLines, '')
|
||||
endif
|
||||
|
||||
if has('timers')
|
||||
" Convert to an expression we can use with feedkeys.
|
||||
" string(l:echo) won't do because it uses single-quotes which
|
||||
" do not escape newlines
|
||||
let l:echoJoinExpr = 'join('.string(l:echoLines).', "\n")'
|
||||
|
||||
let l:echoKeys = ':echo '.l:echoJoinExpr."\<Cr>"
|
||||
if mode() == 'n'
|
||||
" Call echo normaly
|
||||
call feedkeys(l:echoKeys)
|
||||
elseif mode() == 'i'
|
||||
" Execute command in insert mode
|
||||
call feedkeys("\<C-o>".l:echoKeys)
|
||||
endif
|
||||
" NOTE: Other modes are not supported
|
||||
else
|
||||
" Without timer support, feedkeys won't work and we have
|
||||
" to echo directly
|
||||
echo join(l:echoLines, "\n")
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Print an expression that it's evaluation was previously requested
|
||||
function! s:standardThinkHandlers.printEvaluatedExpression(readResult,debugger) dict
|
||||
let l:evaluatedExpression=a:readResult.std.evaluatedExpression
|
||||
if !empty(l:evaluatedExpression)
|
||||
if empty(get(l:evaluatedExpression,'expression'))
|
||||
echo l:evaluatedExpression.value."\n"
|
||||
else
|
||||
let l:index=index(a:debugger.state.std.evaluateExpressions,l:evaluatedExpression.expression)
|
||||
if 0<=l:index
|
||||
call remove(a:debugger.state.std.evaluateExpressions,l:index)
|
||||
echo l:evaluatedExpression.expression.': '.l:evaluatedExpression.value."\n"
|
||||
let g:echo=l:evaluatedExpression.expression.': '.l:evaluatedExpression.value."\n"
|
||||
endif
|
||||
endif
|
||||
call a:debugger.addWriteAction('std','removeAfterDisplayed',a:readResult)
|
||||
endif
|
||||
let l:evaluatedExpression=a:readResult.std.evaluatedExpression
|
||||
if !empty(l:evaluatedExpression)
|
||||
if !empty(get(l:evaluatedExpression,'expression'))
|
||||
let l:index=index(a:debugger.state.std.evaluateExpressions,l:evaluatedExpression.expression)
|
||||
if 0<=l:index
|
||||
call remove(a:debugger.state.std.evaluateExpressions,l:index)
|
||||
endif
|
||||
endif
|
||||
call s:printWithFeedKeys(l:evaluatedExpression)
|
||||
call a:debugger.addWriteAction('std','removeAfterDisplayed',a:readResult)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Close the debugger when the program is finished but the debugger wasn't
|
||||
"closed automatically
|
||||
function! s:standardThinkHandlers.closeDebuggerWhenProgramFinishes(readResult,debugger) dict
|
||||
if !empty(a:readResult.std.programFinish)
|
||||
call a:debugger.setWriteAction('std','closeDebugger','close')
|
||||
endif
|
||||
if !empty(a:readResult.std.programFinish)
|
||||
call a:debugger.setWriteAction('std','closeDebugger','close')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Print an exception message
|
||||
function! s:standardThinkHandlers.printException(readResult,debugger) dict
|
||||
if !empty(a:readResult.std.exception)
|
||||
echohl WarningMsg
|
||||
echo a:readResult.std.exception.message."\n"
|
||||
echohl None
|
||||
endif
|
||||
if !empty(a:readResult.std.exception)
|
||||
echohl WarningMsg
|
||||
echo a:readResult.std.exception.message."\n"
|
||||
echohl None
|
||||
endif
|
||||
endfunction
|
||||
|
||||
let s:standardCloseHandlers={}
|
||||
"Remove the currently executed line when a debugger is closed
|
||||
function! s:standardCloseHandlers.removeCurrentMarker(debugger) dict
|
||||
let a:debugger.state.std.location={}
|
||||
sign unplace 1
|
||||
let a:debugger.state.std.location={}
|
||||
sign unplace 1
|
||||
endfunction
|
||||
|
||||
sign define vebugger_current text=->
|
||||
sign define vebugger_breakpoint text=** linehl=ColorColumn
|
||||
let s:breakpoint_text = get(g:, 'vebugger_breakpoint_text', '->')
|
||||
let s:currentline_text = get(g:, 'vebugger_currentline_text', '**')
|
||||
|
||||
if hlexists("DebuggedLine")
|
||||
sign define vebugger_current linehl=DebuggedLine
|
||||
else
|
||||
execute 'sign define vebugger_current text=' . s:currentline_text
|
||||
endif
|
||||
|
||||
if hlexists('BreakPoint')
|
||||
execute 'sign define vebugger_breakpoint text=' . s:breakpoint_text . ' linehl=BreakPoint texthl=BreakPoint'
|
||||
else
|
||||
execute 'sign define vebugger_breakpoint text=' . s:breakpoint_text . ' linehl=ColorColumn texthl=ColorColumn'
|
||||
endif
|
||||
|
||||
"Update all the marks(currently executed line and breakpoints) for a file
|
||||
function! vebugger#std#updateMarksForFile(state,filename)
|
||||
let l:filename=fnamemodify(a:filename,":p")
|
||||
if bufexists(l:filename)
|
||||
exe 'sign unplace 1 file='.fnameescape(fnamemodify(l:filename,':p'))
|
||||
exe 'sign unplace 2 file='.fnameescape(fnamemodify(l:filename,':p'))
|
||||
let l:filename=fnamemodify(a:filename,":p")
|
||||
let l:bufnr = bufnr(l:filename)
|
||||
if -1 < l:bufnr
|
||||
exe 'sign unplace 1 file='.fnameescape(fnamemodify(l:filename,':p'))
|
||||
for l:sign in vebugger#util#listSignsInBuffer(l:bufnr)
|
||||
if l:sign.name == 'vebugger_breakpoint'
|
||||
exe 'sign unplace 2 file='.fnameescape(fnamemodify(l:filename,':p'))
|
||||
endif
|
||||
endfor
|
||||
|
||||
for l:breakpoint in g:vebugger_breakpoints
|
||||
if fnamemodify(l:breakpoint.file,':p')==fnamemodify(a:filename,':p')
|
||||
exe 'sign place 2 name=vebugger_breakpoint line='.l:breakpoint.line.' file='.fnameescape(fnamemodify(l:breakpoint.file,':p'))
|
||||
endif
|
||||
endfor
|
||||
for l:breakpoint in g:vebugger_breakpoints
|
||||
if fnamemodify(l:breakpoint.file,':p')==fnamemodify(a:filename,':p')
|
||||
exe 'sign place 2 name=vebugger_breakpoint line='.l:breakpoint.line.' file='.fnameescape(fnamemodify(l:breakpoint.file,':p'))
|
||||
endif
|
||||
endfor
|
||||
|
||||
if !empty(a:state)
|
||||
if !empty(a:state.std.location)
|
||||
if fnamemodify(a:state.std.location.file,':p')==fnamemodify(a:filename,':p')
|
||||
exe 'sign place 1 name=vebugger_current line='.a:state.std.location.line.' file='.fnameescape(fnamemodify(l:filename,':p'))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
if !empty(a:state)
|
||||
if !empty(a:state.std.location)
|
||||
if fnamemodify(a:state.std.location.file,':p')==fnamemodify(a:filename,':p')
|
||||
exe 'sign place 1 name=vebugger_current line='.a:state.std.location.line.' file='.fnameescape(fnamemodify(l:filename,':p'))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Toggle a breakpoint on and off
|
||||
function! vebugger#std#toggleBreakpoint(file,line)
|
||||
let l:debugger=vebugger#getActiveDebugger()
|
||||
let l:debuggerState=empty(l:debugger)
|
||||
\? {}
|
||||
\: l:debugger.state
|
||||
for l:i in range(len(g:vebugger_breakpoints))
|
||||
let l:breakpoint=g:vebugger_breakpoints[l:i]
|
||||
if l:breakpoint.file==a:file && l:breakpoint.line==a:line
|
||||
call remove(g:vebugger_breakpoints,l:i)
|
||||
call vebugger#addWriteActionAndPerform('std','breakpoints',{
|
||||
\'action':'remove',
|
||||
\'file':(a:file),
|
||||
\'line':(a:line)})
|
||||
call vebugger#std#updateMarksForFile(l:debuggerState,a:file)
|
||||
return
|
||||
endif
|
||||
endfor
|
||||
call add(g:vebugger_breakpoints,{'file':(a:file),'line':(a:line)})
|
||||
call vebugger#addWriteActionAndPerform('std','breakpoints',{
|
||||
\'action':'add',
|
||||
\'file':(a:file),
|
||||
\'line':(a:line)})
|
||||
call vebugger#std#updateMarksForFile(l:debuggerState,a:file)
|
||||
let l:debugger=vebugger#getActiveDebugger()
|
||||
let l:debuggerState=empty(l:debugger)
|
||||
\? {}
|
||||
\: l:debugger.state
|
||||
for l:i in range(len(g:vebugger_breakpoints))
|
||||
let l:breakpoint=g:vebugger_breakpoints[l:i]
|
||||
if l:breakpoint.file==a:file && l:breakpoint.line==a:line
|
||||
call remove(g:vebugger_breakpoints,l:i)
|
||||
if !empty(l:debugger)
|
||||
call l:debugger.addWriteActionAndPerform('std','breakpoints',{
|
||||
\'action':'remove',
|
||||
\'file':(a:file),
|
||||
\'line':(a:line)})
|
||||
endif
|
||||
call vebugger#std#updateMarksForFile(l:debuggerState,a:file)
|
||||
return
|
||||
endif
|
||||
endfor
|
||||
call add(g:vebugger_breakpoints,{'file':(a:file),'line':(a:line)})
|
||||
if !empty(l:debugger)
|
||||
call l:debugger.addWriteActionAndPerform('std','breakpoints',{
|
||||
\'action':'add',
|
||||
\'file':(a:file),
|
||||
\'line':(a:line)})
|
||||
endif
|
||||
call vebugger#std#updateMarksForFile(l:debuggerState,a:file)
|
||||
endfunction
|
||||
|
||||
"Clear all breakpoints
|
||||
function! vebugger#std#clearBreakpoints()
|
||||
let l:debugger=vebugger#getActiveDebugger()
|
||||
let l:debuggerState=empty(l:debugger) ? {} : l:debugger.state
|
||||
let l:files=[]
|
||||
for l:breakpoint in g:vebugger_breakpoints
|
||||
if index(l:files,l:breakpoint.file)<0
|
||||
call add(l:files,l:breakpoint.file)
|
||||
endif
|
||||
call vebugger#addWriteAction('std','breakpoints',extend({'action':'remove'},l:breakpoint))
|
||||
endfor
|
||||
call vebugger#performWriteActions()
|
||||
let g:vebugger_breakpoints=[]
|
||||
for l:file in l:files
|
||||
call vebugger#std#updateMarksForFile(l:debuggerState,l:file)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
"Ask the active debugger to evaluate an expression
|
||||
function! vebugger#std#eval(expression)
|
||||
let l:debugger=vebugger#getActiveDebugger()
|
||||
if !empty(l:debugger) && !empty(l:debugger.std_eval)
|
||||
call l:debugger.std_eval(a:expression)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
"Ask the active debugger to execute a statement
|
||||
function! vebugger#std#execute(statement)
|
||||
let l:debugger=vebugger#getActiveDebugger()
|
||||
if !empty(l:debugger) && !empty(l:debugger.std_eval)
|
||||
call l:debugger.std_execute(a:statement)
|
||||
endif
|
||||
let l:debugger=vebugger#getActiveDebugger()
|
||||
let l:debuggerState=empty(l:debugger) ? {} : l:debugger.state
|
||||
let l:files=[]
|
||||
for l:breakpoint in g:vebugger_breakpoints
|
||||
if index(l:files,l:breakpoint.file)<0
|
||||
call add(l:files,l:breakpoint.file)
|
||||
endif
|
||||
if !empty(l:debugger)
|
||||
call l:debugger.addWriteAction('std','breakpoints',extend({'action':'remove'},l:breakpoint))
|
||||
endif
|
||||
endfor
|
||||
if !empty(l:debugger)
|
||||
call l:debugger.performWriteActions()
|
||||
endif
|
||||
let g:vebugger_breakpoints=[]
|
||||
for l:file in l:files
|
||||
call vebugger#std#updateMarksForFile(l:debuggerState,l:file)
|
||||
endfor
|
||||
endfunction
|
||||
|
@ -57,7 +57,8 @@ function! vebugger#util#selectProcessOfFile(ofFile)
|
||||
return str2nr(l:resultLinesParsed[l:chosenId][1])
|
||||
else
|
||||
let l:chosenLine=l:resultLines[l:chosenId]
|
||||
return str2nr(matchlist(l:chosenLine,'\v^\s*\d+\)\s+(\d+)')[1])
|
||||
let g:chosenLine = l:chosenLine
|
||||
return str2nr(matchstr(l:chosenLine,'\v^\s*\zs(\d+)\ze\s*'))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
@ -114,3 +115,34 @@ function! vebugger#util#isPathAbsolute(path)
|
||||
return a:path[0]=~'\v^[/~$]' "Absolute paths in Linux start with ~(home),/(root dir) or $(environment variable)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vebugger#util#listify(stringOrList)
|
||||
if type(a:stringOrList) == type([])
|
||||
return copy(a:stringOrList) " so it could safely be modified by map&filter
|
||||
else
|
||||
return [a:stringOrList]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:listSigns(filter) abort
|
||||
let l:result = []
|
||||
for l:line in split(execute('sign place '.a:filter), '\n')
|
||||
let l:match = matchlist(l:line, '\C\v^\s+line\=(\d+)\s+id\=(\d+)\s+name\=(.+)$')
|
||||
if !empty(l:match)
|
||||
call add(l:result, {
|
||||
\ 'line': str2nr(l:match[1]),
|
||||
\ 'id': str2nr(l:match[2]),
|
||||
\ 'name': l:match[3],
|
||||
\ })
|
||||
endif
|
||||
endfor
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
function! vebugger#util#listSignsInBuffer(bufnr) abort
|
||||
return s:listSigns('buffer='.a:bufnr)
|
||||
endfunction
|
||||
|
||||
function! vebugger#util#listSignsInFile(filename) abort
|
||||
return s:listSigns('file='.a:filename)
|
||||
endfunction
|
||||
|
130
doc/vebugger.txt
130
doc/vebugger.txt
@ -4,7 +4,7 @@
|
||||
Author: Idan Arye <https://github.com/idanarye/>
|
||||
License: Same terms as Vim itself (see |license|)
|
||||
|
||||
Version: 1.2.3
|
||||
Version: 1.2.3+
|
||||
|
||||
INTRODUCTION *vebugger*
|
||||
|
||||
@ -20,6 +20,7 @@ supports:
|
||||
Vebugger is built as a generic framework for building frontends for
|
||||
interactive shell debugger, and comes with implementations for:
|
||||
* GDB - doesn't need introdcution...
|
||||
* LLDB - debugger based on LLVM for C-family languages
|
||||
* JDB - a Java debugger
|
||||
* Mdbg - a .NET debugger(Windows only)
|
||||
* PDB - a Python module for debugging Python scripts
|
||||
@ -77,25 +78,56 @@ Example: >
|
||||
If a debugger is not in the PATH you can set the direct path to it by setting
|
||||
g:vebugger_path_XXX, where XXX is the executable used for the debugger:
|
||||
|
||||
*g:vebugger_path_gdb* defaults to "gdb"
|
||||
*g:vebugger_path_jdb* defaults to "jdb"
|
||||
*g:vebugger_path_mdbg* defaults to "Mdbg.exe"
|
||||
*g:vebugger_path_python* defaults to "python"
|
||||
*g:vebugger_path_ruby* defaults to "ruby"
|
||||
*g:vebugger_path_node* defaults to "node inspect"
|
||||
*g:vebugger_path_gdb* defaults to "gdb"
|
||||
*g:vebugger_path_python_lldb* defaults to "python2"
|
||||
*g:vebugger_path_jdb* defaults to "jdb"
|
||||
*g:vebugger_path_mdbg* defaults to "Mdbg.exe"
|
||||
*g:vebugger_path_python* defaults to "python"
|
||||
*g:vebugger_path_ruby* defaults to "ruby"
|
||||
*g:vebugger_path_node* defaults to "node inspect"
|
||||
|
||||
Notice that for PDB and RDebug you use "python" and "ruby", since the debugger
|
||||
is actually a module bundled in the interpreter.
|
||||
Notice that for LLDB, PDB and RDebug you use "python_lldb", "python" and
|
||||
"ruby", since the debugger is actually a module bundled in the interpreter
|
||||
(LLDB is called by a python wrapper because the LLDB executable has no
|
||||
machine interface like GDB to interact with).
|
||||
|
||||
You can set multiple versions for each debugger, by appending the version name
|
||||
to the debugger name with "_". These versions will be used when the "version"
|
||||
argument is supplied when running the debugger:
|
||||
You can set multiple versions for each debugger (except LLDB), by appending
|
||||
the version name to the debugger name with "_". These versions will be used
|
||||
when the "version" argument is supplied when running the debugger:
|
||||
|
||||
*g:vebugger_path_python_2* defaults to "python2"
|
||||
*g:vebugger_path_python_3* defaults to "python3"
|
||||
*g:vebugger_path_mdbg_32* No default - use it for the 32bit version of Mdbg
|
||||
*g:vebugger_path_mdbg_64* No default - use it for the 64bit version of Mdbg
|
||||
|
||||
For LLDB a special python variable is needed ("g:vebugger_path_python_lldb")
|
||||
since the lldb python api is only python 2 compatible. So, it is possible to
|
||||
use a python 2 interpreter for LLDB and another python version for PDB at
|
||||
the same time.
|
||||
|
||||
When stepping into functions in unopened files, they are opened with :new by
|
||||
default. This can be configured by setting *g:vebugger_view_source_cmd*
|
||||
|
||||
Example: >
|
||||
let g:vebugger_view_source_cmd='edit'
|
||||
<
|
||||
|
||||
If you want to change the sign text of current line and breakpoint, use
|
||||
*g:vebugger_breakpoint_text* and *g:vebugger_currentline_text*
|
||||
|
||||
Example: >
|
||||
let g:vebugger_breakpoint_text='->'
|
||||
let g:vebugger_currentline_text='**'
|
||||
<
|
||||
|
||||
Some debuggers (currently jdb only) may use vim tags to find required source
|
||||
files. This is disabled by default, to enable this set *g:vebugger_use_tags*
|
||||
option:
|
||||
|
||||
Example: >
|
||||
let g:vebugger_use_tags=1
|
||||
<
|
||||
|
||||
LAUNCHING DEBUGGERS *vebugger-launching*
|
||||
|
||||
A debugger's implementation is responsible for starting it. The standard is to
|
||||
@ -119,6 +151,7 @@ GDB can be launched with *vebugger#gdb#start*
|
||||
The supported extra arguments are:
|
||||
* "args": Command line arguments for the debugged program
|
||||
* "pid": Process id to attach to
|
||||
* "con": URL of GDB-server to connect to
|
||||
* "entry": The entry point for starting the debugging(default "main")
|
||||
* "version": The version of the debugger to run
|
||||
You can't specify both ("args" and/or "entry") and "pid".
|
||||
@ -132,9 +165,40 @@ attach to, and attaches to them:
|
||||
>
|
||||
VBGattachGDB a.out
|
||||
<
|
||||
|
||||
VBGattachGDB accepts as a second argument the process ID of the process to
|
||||
attach to or the URL for a GDB-server to connect to.
|
||||
|
||||
The *VBGstartGDBForD* command is the same as VBGstartGDB but for Dlang
|
||||
programs.
|
||||
|
||||
|
||||
LAUNCHING LLDB *vebugger-lldb*
|
||||
|
||||
LLDB can be launched with *vebugger#lldb#start*
|
||||
>
|
||||
call vebugger#lldb#start('a.out',{'args':['hello','world']})
|
||||
<
|
||||
|
||||
The supported extra arguments are:
|
||||
* "args": Command line arguments for the debugged program
|
||||
* "pid": Process id to attach to
|
||||
You can't specify both "args" and "pid".
|
||||
|
||||
LLDB can also be launched with the *VBGstartLLDB* command:
|
||||
>
|
||||
VBGstartLLDB a.out hello world
|
||||
<
|
||||
The *VBGattachLLDB* command searches for processes launched from the EXE to
|
||||
attach to, and attaches to them:
|
||||
>
|
||||
VBGattachLLDB a.out
|
||||
<
|
||||
|
||||
VBGattachLLDB accepts as a second argument the process ID of the process to
|
||||
attach to.
|
||||
|
||||
|
||||
LAUNCHING JDB *vebugger-jdb*
|
||||
|
||||
JDB is launched with *vebugger#jdb#start*
|
||||
@ -148,14 +212,27 @@ Unlike in the other debuggers, the first argument here is not the name of a
|
||||
file - it's the name of the class to run. The supported extra arguments are:
|
||||
* "args": Command line arguments for the debugged program
|
||||
* "classpath": Where to look for class files
|
||||
* "srcpath": Where to look for source files
|
||||
* "srcpath": Where to look for source files. You can have multiple srcpaths, passed as a list
|
||||
* "version": The version of the debugger to run
|
||||
|
||||
Alternatively, to attach to a running process you can use *vebugger#jdb#attach*
|
||||
>
|
||||
call vebugger#jdb#attach('8000',{
|
||||
\'srcpath':'src'})
|
||||
<
|
||||
The first argument here is the the address to attach to, e.g. 127.0.0.1:8000
|
||||
or just 8000. It does not support the "args" or "classpath" arguments.
|
||||
|
||||
If you don't supply "classpath" and "srcpath", Vebugger will assume you are
|
||||
using the current directory for source files and class files.
|
||||
|
||||
JDB does not have a command for starting it, since you usually want to supply
|
||||
"classpath" and "srcpath".
|
||||
|
||||
If you need Vebugger to search for the source files in multiple locations, you can
|
||||
specify `srcpath` as a list. Vebugger will search on all these folders
|
||||
and the first match will be returned as the Source file to be shown.
|
||||
|
||||
|
||||
LAUNCHING RDEBUG *vebugger-rdebug*
|
||||
|
||||
@ -258,7 +335,7 @@ MANAGE BREAKPOINTS *vebugger-breakpoints*
|
||||
|
||||
*:VBGtoggleBreakpoint* Toggle a breakpoint. The file and line should be supplied as arguments.
|
||||
*:VBGtoggleBreakpointThisLine* Toggle a breakpoint for the current line.
|
||||
*:VBGclearBreakpints* Clear all breakpoints.
|
||||
*:VBGclearBreakpoints* Clear all breakpoints.
|
||||
|
||||
EVALUATE EXPRESSIONS *vebugger-evalutate*
|
||||
|
||||
@ -299,7 +376,7 @@ O |:VBGstepOut|
|
||||
c |:VBGcontinue|
|
||||
|
||||
b |:VBGtoggleBreakpointThisLine|
|
||||
B |:VBGclearBreakpints|
|
||||
B |:VBGclearBreakpoints|
|
||||
|
||||
e |:VBGevalWordUnderCursor| in normal mode
|
||||
|:VBGevalSelectedText| in select mode
|
||||
@ -490,3 +567,26 @@ defines the "std" namespace, and plugins built on top of Vebugger can define a
|
||||
namespaced named after the plugin to contain their own read results. A
|
||||
debugger frontend implementation can also define it's own namespace for
|
||||
storing debugger-specific data and configuration.
|
||||
|
||||
COMPILER OUTPUT BUFFERERS *vebugger-architecture-bufferers*
|
||||
|
||||
Read handlers receive lines created by the debugger output bufferer. There is
|
||||
only one for each debugger, and if you just want to get the debugger output
|
||||
line-by-line "as is" you can just use the default one and ignore this section.
|
||||
|
||||
The debugger bufferer is a dict function(|:func-dict|) that operates on pipe
|
||||
objects. It will be called when the pipe object's "self.buffer", which
|
||||
represents everything read from a debugger's output stream, has new data.
|
||||
|
||||
The bufferer function should:
|
||||
* return a list of new lines that will be send to the read handlers, or an
|
||||
empty list if there are no new lines.
|
||||
* "advance" the buffer - delete from it's head everything that was handled.
|
||||
This can easily be done with |strpart()|.
|
||||
|
||||
Bufferers should be set individually for each pipe, usually in the debugger
|
||||
creation function:
|
||||
|
||||
Example: >
|
||||
let l:debugger.pipes.out.bufferer = function('g:myCustomBufferer')
|
||||
<
|
||||
|
@ -1,28 +1,64 @@
|
||||
|
||||
|
||||
command! -nargs=1 VBGrawWrite call vebugger#writeLine(<q-args>)
|
||||
command! -nargs=0 VBGrepeat call vebugger#repeatLastUserAction()
|
||||
|
||||
command! -nargs=1 VBGrawWrite call vebugger#userAction('writeLine', <q-args>)
|
||||
command! -nargs=0 VBGkill call vebugger#killDebugger()
|
||||
|
||||
command! -nargs=0 VBGstepIn call vebugger#setWriteActionAndPerform('std','flow','stepin')
|
||||
command! -nargs=0 VBGstepOver call vebugger#setWriteActionAndPerform('std','flow','stepover')
|
||||
command! -nargs=0 VBGstepOut call vebugger#setWriteActionAndPerform('std','flow','stepout')
|
||||
command! -nargs=0 VBGcontinue call vebugger#setWriteActionAndPerform('std','flow','continue')
|
||||
command! -nargs=0 VBGstepIn call vebugger#userAction('setWriteActionAndPerform', 'std', 'flow', 'stepin')
|
||||
command! -nargs=0 VBGstepOver call vebugger#userAction('setWriteActionAndPerform', 'std', 'flow', 'stepover')
|
||||
command! -nargs=0 VBGstepOut call vebugger#userAction('setWriteActionAndPerform', 'std', 'flow', 'stepout')
|
||||
command! -nargs=0 VBGcontinue call vebugger#userAction('setWriteActionAndPerform', 'std', 'flow', 'continue')
|
||||
|
||||
command! -nargs=0 VBGtoggleTerminalBuffer call vebugger#toggleTerminalBuffer()
|
||||
command! -nargs=0 VBGtoggleTerminalBuffer call vebugger#userAction('toggleTerminalBuffer')
|
||||
command! -nargs=+ -complete=file VBGtoggleBreakpoint call vebugger#std#toggleBreakpoint(<f-args>)
|
||||
command! -nargs=0 VBGtoggleBreakpointThisLine call vebugger#std#toggleBreakpoint(expand('%:~:.'),line('.'))
|
||||
command! -nargs=0 VBGclearBreakpints call vebugger#std#clearBreakpoints()
|
||||
command! -nargs=0 VBGtoggleBreakpointThisLine call vebugger#std#toggleBreakpoint(expand('%:p:.'),line('.'))
|
||||
command! -nargs=0 VBGclearBreakpoints call vebugger#std#clearBreakpoints()
|
||||
|
||||
command! -nargs=1 VBGeval call vebugger#std#eval(<q-args>)
|
||||
command! -nargs=0 VBGevalWordUnderCursor call vebugger#std#eval(expand('<cword>'))
|
||||
command! -nargs=1 VBGexecute call vebugger#std#execute(<q-args>)
|
||||
command! -nargs=1 VBGeval call vebugger#userAction('std_eval', <q-args>)
|
||||
command! -nargs=0 VBGevalWordUnderCursor call vebugger#userAction('std_eval', expand('<cword>'))
|
||||
command! -nargs=1 VBGexecute call vebugger#userAction('std_execute', <q-args>)
|
||||
|
||||
command! -range -nargs=0 VBGevalSelectedText call vebugger#std#eval(vebugger#util#get_visual_selection())
|
||||
command! -range -nargs=0 VBGexecuteSelectedText call vebugger#std#execute(vebugger#util#get_visual_selection())
|
||||
command! -range -nargs=0 VBGrawWriteSelectedText call vebugger#writeLine(vebugger#util#get_visual_selection())
|
||||
command! -range -nargs=0 VBGevalSelectedText call vebugger#userAction('std_eval', vebugger#util#get_visual_selection())
|
||||
command! -range -nargs=0 VBGexecuteSelectedText call vebugger#userAction('std_execute', vebugger#util#get_visual_selection())
|
||||
command! -range -nargs=0 VBGrawWriteSelectedText call vebugger#userAction('writeLine', vebugger#util#get_visual_selection())
|
||||
|
||||
command! -nargs=+ -complete=file VBGstartGDB call vebugger#gdb#start([<f-args>][0],{'args':[<f-args>][1:]})
|
||||
command! -nargs=1 -complete=file VBGattachGDB call vebugger#gdb#searchAndAttach(<q-args>)
|
||||
function! s:attachGDB(...)
|
||||
if 1 == a:0
|
||||
let l:processId=vebugger#util#selectProcessOfFile(a:1)
|
||||
if 0 < l:processId
|
||||
call vebugger#gdb#start(a:1, {'pid': l:processId})
|
||||
endif
|
||||
elseif 2 == a:0
|
||||
if a:2 =~ '\v^\d+$'
|
||||
call vebugger#gdb#start(a:1,{'pid': str2nr(a:2)})
|
||||
else
|
||||
call vebugger#gdb#start(a:1, {'con': a:2})
|
||||
endif
|
||||
else
|
||||
throw "Can't call VBGattachGDB with ".a:0." arguments"
|
||||
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 VBGstartNInspect call vebugger#ninspect#start([<f-args>][0],{'args':[<f-args>][1:]})
|
||||
command! -nargs=+ -complete=file VBGstartPDB call vebugger#pdb#start([<f-args>][0],{'args':[<f-args>][1:]})
|
||||
@ -39,7 +75,7 @@ if exists('g:vebugger_leader')
|
||||
\'c':'VBGcontinue',
|
||||
\'t':'VBGtoggleTerminalBuffer',
|
||||
\'b':'VBGtoggleBreakpointThisLine',
|
||||
\'B':'VBGclearBreakpints',
|
||||
\'B':'VBGclearBreakpoints',
|
||||
\'e':'VBGevalWordUnderCursor',
|
||||
\'E':'exe "VBGeval ".input("VBG-Eval> ")',
|
||||
\'x':'exe "VBGexecute ".getline(".")',
|
||||
|
Loading…
Reference in New Issue
Block a user