1
0
forked from aniani/vim

patch 8.0.1085: terminal debugger can't set breakpoints

Problem:    The terminal debugger can't set breakpoints.
Solution:   Add :Break and :Delete commands.  Also commands for stepping
            through code.
This commit is contained in:
Bram Moolenaar
2017-09-09 22:19:47 +02:00
parent 7be9b50fd7
commit e09ba7bae5
3 changed files with 225 additions and 54 deletions

View File

@@ -1,4 +1,4 @@
*terminal.txt* For Vim version 8.0. Last change: 2017 Aug 29 *terminal.txt* For Vim version 8.0. Last change: 2017 Sep 09
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -30,11 +30,11 @@ This feature is for running a terminal emulator in a Vim window. A job can be
started connected to the terminal emulator. For example, to run a shell: > started connected to the terminal emulator. For example, to run a shell: >
:term bash :term bash
Or to run a debugger: > Or to run build command: >
:term gdb vim :term make myprogram
The job runs asynchronously from Vim, the window will be updated to show The job runs asynchronously from Vim, the window will be updated to show
output from the job, also while editing in any other window. output from the job, also while editing in another window.
Typing ~ Typing ~
@@ -109,7 +109,8 @@ Syntax ~
If [range] is given the specified lines are used as If [range] is given the specified lines are used as
input for the job. It will not be possible to type input for the job. It will not be possible to type
keys in the terminal window. keys in the terminal window. For MS-Windows see the
++eof argument below.
Two comma separated numbers are used as "rows,cols". Two comma separated numbers are used as "rows,cols".
E.g. `:24,80gdb` opens a terminal with 24 rows and 80 E.g. `:24,80gdb` opens a terminal with 24 rows and 80
@@ -133,14 +134,15 @@ Syntax ~
height. height.
++cols={width} Use {width} for the terminal window ++cols={width} Use {width} for the terminal window
width. width.
++eof={text} when using [range], text to send after ++eof={text} when using [range]: text to send after
the last line was written. The default the last line was written. Cannot
is to send CTRL-D. A CR is appended. contain white space. A CR is
appended. For MS-Windows the default
is to send CTRL-D.
E.g. for a shell use "++eof=exit" and E.g. for a shell use "++eof=exit" and
for Python "++eof=exit()". Special for Python "++eof=exit()". Special
codes can be used like with `:map`, codes can be used like with `:map`,
e.g. "<C-Z>" for CTRL-Z. e.g. "<C-Z>" for CTRL-Z.
{only on MS-Windows}
If you want to use more options use the |term_start()| If you want to use more options use the |term_start()|
function. function.
@@ -303,33 +305,90 @@ term_scrape() inspect terminal screen
3. Debugging *terminal-debug* 3. Debugging *terminal-debug*
The Terminal debugging plugin can be used to debug a program with gdb and view The Terminal debugging plugin can be used to debug a program with gdb and view
the source code in a Vim window. the source code in a Vim window. Since this is completely contained inside
Vim this also works remotely over an ssh connection.
Starting ~
Load the plugin with this command: > Load the plugin with this command: >
packadd termdebug packadd termdebug
< *:Termdebug*
To start debugging use `:TermDebug` folowed by the command name, for example: > To start debugging use `:TermDebug` folowed by the command name, for example: >
:TermDebug vim :TermDebug vim
This opens two windows: This opens two windows:
- A terminal window in which "gdb vim" is executed. Here you can directly - A terminal window in which "gdb vim" is executed. Here you can directly
interact with gdb. interact with gdb. The buffer name is "!gdb".
- A terminal window for the executed program. When "run" is used in gdb the - A terminal window for the executed program. When "run" is used in gdb the
program I/O will happen in this window, so that it does not interfere with program I/O will happen in this window, so that it does not interfere with
controlling gdb. controlling gdb. The buffer name is "gdb program".
The current window is used to show the source code. When gdb jumps to a
source file location this window will display the code, if possible. Values The current window is used to show the source code. When gdb pauses the
of variables can be inspected, breakpoints set and cleared, etc. source file location will be displayed, if possible. A sign is used to
highlight the current position (using highlight group debugPC).
If the buffer in the current window is modified, another window will be opened
to display the current gdb position.
Focus the terminal of the executed program to interact with it. This works
the same as any command running in a terminal window.
When the debugger ends the two opened windows are closed. When the debugger ends the two opened windows are closed.
Stepping through code ~
Put focus on the gdb window to type commands there. Some common ones are:
- CTRL-C interrupt the program
- next execute the current line and stop at the next line
- step execute the current line and stop at the next statement, entering
functions
- finish execute until leaving the current function
- where show the stack
- frame N go to the Nth stack frame
- continue continue execution
In the window showing the source code some commands can passed to gdb:
- Break set a breakpoint at the current line; a sign will be displayed
- Delete delete a breakpoint at the current line
- Step execute the gdb "step" command
- NNext execute the gdb "next" command (:Next is a Vim command)
- Finish execute the gdb "finish" command
- Continue execute the gdb "continue" command
Communication ~
There is another, hidden, buffer, which is used for Vim to communicate with
gdb. The buffer name is "gdb communication". Do not delete this buffer, it
will break the debugger.
Customizing ~ Customizing ~
g:debugger The debugger command. Default "gdb". To change the name of the gdb command, set the "termdebugger" variable before
invoking `:Termdebug`: >
let termdebugger = "mygdb"
Only debuggers fully compatible with gdb will work. Vim uses the GDB/MI
interface.
The color of the signs can be adjusted with these highlight groups:
- debugPC the current position
- debugBreakpoint a breakpoint
The defaults are, when 'background' is "light":
hi debugPC term=reverse ctermbg=lightblue guibg=lightblue
hi debugBreakpoint term=reverse ctermbg=red guibg=red
When 'background' is "dark":
hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
hi debugBreakpoint term=reverse ctermbg=red guibg=red
TODO NOT WORKING YET: ~
Values of variables can be inspected, etc.
vim:tw=78:ts=8:ft=help:norl: vim:tw=78:ts=8:ft=help:norl:

View File

@@ -20,18 +20,26 @@
command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>) command -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>)
" Name of the gdb command, defaults to "gdb". " Name of the gdb command, defaults to "gdb".
if !exists('debugger') if !exists('termdebugger')
let debugger = 'gdb' let termdebugger = 'gdb'
endif endif
" Sign used to highlight the line where the program has stopped. " Sign used to highlight the line where the program has stopped.
" There can be only one.
sign define debugPC linehl=debugPC sign define debugPC linehl=debugPC
if &background == 'light'
hi debugPC term=reverse ctermbg=lightblue guibg=lightblue
else
hi debugPC term=reverse ctermbg=darkblue guibg=darkblue
endif
let s:pc_id = 12 let s:pc_id = 12
let s:break_id = 13
" Sign used to indicate a breakpoint.
" Can be used multiple times.
sign define debugBreakpoint text=>> texthl=debugBreakpoint
if &background == 'light'
hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue
else
hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue
endif
hi default debugBreakpoint term=reverse ctermbg=red guibg=red
func s:StartDebug(cmd) func s:StartDebug(cmd)
let s:startwin = win_getid(winnr()) let s:startwin = win_getid(winnr())
@@ -61,7 +69,7 @@ func s:StartDebug(cmd)
let commpty = job_info(term_getjob(s:commbuf))['tty_out'] let commpty = job_info(term_getjob(s:commbuf))['tty_out']
" Open a terminal window to run the debugger. " Open a terminal window to run the debugger.
let cmd = [g:debugger, '-tty', pty, a:cmd] let cmd = [g:termdebugger, '-tty', pty, a:cmd]
echomsg 'executing "' . join(cmd) . '"' echomsg 'executing "' . join(cmd) . '"'
let gdbbuf = term_start(cmd, { let gdbbuf = term_start(cmd, {
\ 'exit_cb': function('s:EndDebug'), \ 'exit_cb': function('s:EndDebug'),
@@ -76,12 +84,24 @@ func s:StartDebug(cmd)
" Connect gdb to the communication pty, using the GDB/MI interface " Connect gdb to the communication pty, using the GDB/MI interface
call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r")
" Install debugger commands.
call s:InstallCommands()
let s:breakpoints = {}
endfunc endfunc
func s:EndDebug(job, status) func s:EndDebug(job, status)
exe 'bwipe! ' . s:ptybuf exe 'bwipe! ' . s:ptybuf
exe 'bwipe! ' . s:commbuf exe 'bwipe! ' . s:commbuf
call setwinvar(s:startwin, '&signcolumn', s:startsigncolumn)
let curwinid = win_getid(winnr())
call win_gotoid(s:startwin)
let &signcolumn = s:startsigncolumn
call s:DeleteCommands()
call win_gotoid(curwinid)
endfunc endfunc
" Handle a message received from gdb on the GDB/MI interface. " Handle a message received from gdb on the GDB/MI interface.
@@ -95,34 +115,124 @@ func s:CommOutput(chan, msg)
endif endif
if msg != '' if msg != ''
if msg =~ '^\*\(stopped\|running\)' if msg =~ '^\*\(stopped\|running\)'
let wid = win_getid(winnr()) call s:HandleCursor(msg)
elseif msg =~ '^\^done,bkpt='
if win_gotoid(s:startwin) call s:HandleNewBreakpoint(msg)
if msg =~ '^\*stopped' elseif msg =~ '^=breakpoint-deleted,'
" TODO: proper parsing call s:HandleBreakpointDelete(msg)
let fname = substitute(msg, '.*fullname="\([^"]*\)".*', '\1', '')
let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
if lnum =~ '^[0-9]*$'
if expand('%:h') != fname
if &modified
" TODO: find existing window
exe 'split ' . fnameescape(fname)
let s:startwin = win_getid(winnr())
else
exe 'edit ' . fnameescape(fname)
endif
endif
exe lnum
exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname)
setlocal signcolumn=yes
endif
else
exe 'sign unplace ' . s:pc_id
endif
call win_gotoid(wid)
endif
endif endif
endif endif
endfor endfor
endfunc endfunc
" Install commands in the current window to control the debugger.
func s:InstallCommands()
command Break call s:SetBreakpoint()
command Delete call s:DeleteBreakpoint()
command Step call s:SendCommand('-exec-step')
command NNext call s:SendCommand('-exec-next')
command Finish call s:SendCommand('-exec-finish')
command Continue call s:SendCommand('-exec-continue')
endfunc
" Delete installed debugger commands in the current window.
func s:DeleteCommands()
delcommand Break
delcommand Delete
delcommand Step
delcommand NNext
delcommand Finish
delcommand Continue
endfunc
" :Break - Set a breakpoint at the cursor position.
func s:SetBreakpoint()
call term_sendkeys(s:commbuf, '-break-insert --source '
\ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r")
endfunc
" :Delete - Delete a breakpoint at the cursor position.
func s:DeleteBreakpoint()
let fname = fnameescape(expand('%:p'))
let lnum = line('.')
for [key, val] in items(s:breakpoints)
if val['fname'] == fname && val['lnum'] == lnum
call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r")
" Assume this always wors, the reply is simply "^done".
exe 'sign unplace ' . (s:break_id + key)
unlet s:breakpoints[key]
break
endif
endfor
endfunc
" :Next, :Continue, etc - send a command to gdb
func s:SendCommand(cmd)
call term_sendkeys(s:commbuf, a:cmd . "\r")
endfunc
" Handle stopping and running message from gdb.
" Will update the sign that shows the current position.
func s:HandleCursor(msg)
let wid = win_getid(winnr())
if win_gotoid(s:startwin)
if a:msg =~ '^\*stopped'
let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
if lnum =~ '^[0-9]*$'
if expand('%:h') != fname
if &modified
" TODO: find existing window
exe 'split ' . fnameescape(fname)
let s:startwin = win_getid(winnr())
else
exe 'edit ' . fnameescape(fname)
endif
endif
exe lnum
exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname)
setlocal signcolumn=yes
endif
else
exe 'sign unplace ' . s:pc_id
endif
call win_gotoid(wid)
endif
endfunc
" Handle setting a breakpoint
" Will update the sign that shows the breakpoint
func s:HandleNewBreakpoint(msg)
let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0
if nr == 0
return
endif
if has_key(s:breakpoints, nr)
let entry = s:breakpoints[nr]
else
let entry = {}
let s:breakpoints[nr] = entry
endif
let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '')
let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
exe 'sign place ' . (s:break_id + nr) . ' line=' . lnum . ' name=debugBreakpoint file=' . fnameescape(fname)
let entry['fname'] = fname
let entry['lnum'] = lnum
endfunc
" Handle deleting a breakpoint
" Will remove the sign that shows the breakpoint
func s:HandleBreakpointDelete(msg)
let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
if nr == 0
return
endif
exe 'sign unplace ' . (s:break_id + nr)
unlet s:breakpoints[nr]
endfunc

View File

@@ -769,6 +769,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1085,
/**/ /**/
1084, 1084,
/**/ /**/