1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3c572da5fSBram Moolenaar" WORK IN PROGRESS - much doesn't work yet 4c572da5fSBram Moolenaar" 5fe386641SBram Moolenaar" Open two visible terminal windows: 6c572da5fSBram Moolenaar" 1. run a pty, as with ":term NONE" 7c572da5fSBram Moolenaar" 2. run gdb, passing the pty 8fe386641SBram Moolenaar" The current window is used to view source code and follows gdb. 9fe386641SBram Moolenaar" 10fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb. 11fe386641SBram Moolenaar" 12fe386641SBram Moolenaar" The communication with gdb uses GDB/MI. See: 13fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html 14c572da5fSBram Moolenaar" 15c572da5fSBram Moolenaar" Author: Bram Moolenaar 16fe386641SBram Moolenaar" Copyright: Vim license applies, see ":help license" 17c572da5fSBram Moolenaar 18fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 19fe386641SBram Moolenaar" To end type "quit" in the gdb window. 20c572da5fSBram Moolenaarcommand -nargs=* -complete=file Termdebug call s:StartDebug(<q-args>) 21c572da5fSBram Moolenaar 22fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 23*e09ba7baSBram Moolenaarif !exists('termdebugger') 24*e09ba7baSBram Moolenaar let termdebugger = 'gdb' 25c572da5fSBram Moolenaarendif 26c572da5fSBram Moolenaar 27fe386641SBram Moolenaar" Sign used to highlight the line where the program has stopped. 28*e09ba7baSBram Moolenaar" There can be only one. 29fe386641SBram Moolenaarsign define debugPC linehl=debugPC 30fe386641SBram Moolenaarlet s:pc_id = 12 31*e09ba7baSBram Moolenaarlet s:break_id = 13 32*e09ba7baSBram Moolenaar 33*e09ba7baSBram Moolenaar" Sign used to indicate a breakpoint. 34*e09ba7baSBram Moolenaar" Can be used multiple times. 35*e09ba7baSBram Moolenaarsign define debugBreakpoint text=>> texthl=debugBreakpoint 36*e09ba7baSBram Moolenaar 37*e09ba7baSBram Moolenaarif &background == 'light' 38*e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue 39*e09ba7baSBram Moolenaarelse 40*e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue 41*e09ba7baSBram Moolenaarendif 42*e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 43fe386641SBram Moolenaar 44c572da5fSBram Moolenaarfunc s:StartDebug(cmd) 45fe386641SBram Moolenaar let s:startwin = win_getid(winnr()) 46fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 47fe386641SBram Moolenaar 48c572da5fSBram Moolenaar " Open a terminal window without a job, to run the debugged program 49fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 50fe386641SBram Moolenaar \ 'term_name': 'gdb program', 51fe386641SBram Moolenaar \ }) 52fe386641SBram Moolenaar if s:ptybuf == 0 53fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 54fe386641SBram Moolenaar return 55fe386641SBram Moolenaar endif 56fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 57fe386641SBram Moolenaar 58fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 59fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 60fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 61fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 62fe386641SBram Moolenaar \ 'hidden': 1, 63fe386641SBram Moolenaar \ }) 64fe386641SBram Moolenaar if s:commbuf == 0 65fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 66fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 67fe386641SBram Moolenaar return 68fe386641SBram Moolenaar endif 69fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 70c572da5fSBram Moolenaar 71c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 72*e09ba7baSBram Moolenaar let cmd = [g:termdebugger, '-tty', pty, a:cmd] 73c572da5fSBram Moolenaar echomsg 'executing "' . join(cmd) . '"' 74c572da5fSBram Moolenaar let gdbbuf = term_start(cmd, { 75c572da5fSBram Moolenaar \ 'exit_cb': function('s:EndDebug'), 76fe386641SBram Moolenaar \ 'term_finish': 'close', 77c572da5fSBram Moolenaar \ }) 78fe386641SBram Moolenaar if gdbbuf == 0 79fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 80fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 81fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 82fe386641SBram Moolenaar return 83fe386641SBram Moolenaar endif 84fe386641SBram Moolenaar 85fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 86fe386641SBram Moolenaar call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") 87*e09ba7baSBram Moolenaar 88*e09ba7baSBram Moolenaar " Install debugger commands. 89*e09ba7baSBram Moolenaar call s:InstallCommands() 90*e09ba7baSBram Moolenaar 91*e09ba7baSBram Moolenaar let s:breakpoints = {} 92c572da5fSBram Moolenaarendfunc 93c572da5fSBram Moolenaar 94c572da5fSBram Moolenaarfunc s:EndDebug(job, status) 95c572da5fSBram Moolenaar exe 'bwipe! ' . s:ptybuf 96fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 97*e09ba7baSBram Moolenaar 98*e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 99*e09ba7baSBram Moolenaar 100*e09ba7baSBram Moolenaar call win_gotoid(s:startwin) 101*e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 102*e09ba7baSBram Moolenaar call s:DeleteCommands() 103*e09ba7baSBram Moolenaar 104*e09ba7baSBram Moolenaar call win_gotoid(curwinid) 105fe386641SBram Moolenaarendfunc 106fe386641SBram Moolenaar 107fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 108fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 109fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 110fe386641SBram Moolenaar 111fe386641SBram Moolenaar for msg in msgs 112fe386641SBram Moolenaar " remove prefixed NL 113fe386641SBram Moolenaar if msg[0] == "\n" 114fe386641SBram Moolenaar let msg = msg[1:] 115fe386641SBram Moolenaar endif 116fe386641SBram Moolenaar if msg != '' 117fe386641SBram Moolenaar if msg =~ '^\*\(stopped\|running\)' 118*e09ba7baSBram Moolenaar call s:HandleCursor(msg) 119*e09ba7baSBram Moolenaar elseif msg =~ '^\^done,bkpt=' 120*e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 121*e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 122*e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 123*e09ba7baSBram Moolenaar endif 124*e09ba7baSBram Moolenaar endif 125*e09ba7baSBram Moolenaar endfor 126*e09ba7baSBram Moolenaarendfunc 127*e09ba7baSBram Moolenaar 128*e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 129*e09ba7baSBram Moolenaarfunc s:InstallCommands() 130*e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 131*e09ba7baSBram Moolenaar command Delete call s:DeleteBreakpoint() 132*e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 133*e09ba7baSBram Moolenaar command NNext call s:SendCommand('-exec-next') 134*e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 135*e09ba7baSBram Moolenaar command Continue call s:SendCommand('-exec-continue') 136*e09ba7baSBram Moolenaarendfunc 137*e09ba7baSBram Moolenaar 138*e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 139*e09ba7baSBram Moolenaarfunc s:DeleteCommands() 140*e09ba7baSBram Moolenaar delcommand Break 141*e09ba7baSBram Moolenaar delcommand Delete 142*e09ba7baSBram Moolenaar delcommand Step 143*e09ba7baSBram Moolenaar delcommand NNext 144*e09ba7baSBram Moolenaar delcommand Finish 145*e09ba7baSBram Moolenaar delcommand Continue 146*e09ba7baSBram Moolenaarendfunc 147*e09ba7baSBram Moolenaar 148*e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 149*e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 150*e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, '-break-insert --source ' 151*e09ba7baSBram Moolenaar \ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r") 152*e09ba7baSBram Moolenaarendfunc 153*e09ba7baSBram Moolenaar 154*e09ba7baSBram Moolenaar" :Delete - Delete a breakpoint at the cursor position. 155*e09ba7baSBram Moolenaarfunc s:DeleteBreakpoint() 156*e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 157*e09ba7baSBram Moolenaar let lnum = line('.') 158*e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 159*e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 160*e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r") 161*e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 162*e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 163*e09ba7baSBram Moolenaar unlet s:breakpoints[key] 164*e09ba7baSBram Moolenaar break 165*e09ba7baSBram Moolenaar endif 166*e09ba7baSBram Moolenaar endfor 167*e09ba7baSBram Moolenaarendfunc 168*e09ba7baSBram Moolenaar 169*e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb 170*e09ba7baSBram Moolenaarfunc s:SendCommand(cmd) 171*e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 172*e09ba7baSBram Moolenaarendfunc 173*e09ba7baSBram Moolenaar 174*e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 175*e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 176*e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 177fe386641SBram Moolenaar let wid = win_getid(winnr()) 178fe386641SBram Moolenaar 179fe386641SBram Moolenaar if win_gotoid(s:startwin) 180*e09ba7baSBram Moolenaar if a:msg =~ '^\*stopped' 181*e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 182*e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 183fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 184fe386641SBram Moolenaar if expand('%:h') != fname 185fe386641SBram Moolenaar if &modified 186fe386641SBram Moolenaar " TODO: find existing window 187fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 188fe386641SBram Moolenaar let s:startwin = win_getid(winnr()) 189fe386641SBram Moolenaar else 190fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 191fe386641SBram Moolenaar endif 192fe386641SBram Moolenaar endif 193fe386641SBram Moolenaar exe lnum 194fe386641SBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname) 195fe386641SBram Moolenaar setlocal signcolumn=yes 196fe386641SBram Moolenaar endif 197fe386641SBram Moolenaar else 198fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 199fe386641SBram Moolenaar endif 200fe386641SBram Moolenaar 201fe386641SBram Moolenaar call win_gotoid(wid) 202fe386641SBram Moolenaar endif 203*e09ba7baSBram Moolenaarendfunc 204*e09ba7baSBram Moolenaar 205*e09ba7baSBram Moolenaar" Handle setting a breakpoint 206*e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 207*e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 208*e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 209*e09ba7baSBram Moolenaar if nr == 0 210*e09ba7baSBram Moolenaar return 211fe386641SBram Moolenaar endif 212*e09ba7baSBram Moolenaar 213*e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 214*e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 215*e09ba7baSBram Moolenaar else 216*e09ba7baSBram Moolenaar let entry = {} 217*e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 218fe386641SBram Moolenaar endif 219*e09ba7baSBram Moolenaar 220*e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 221*e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 222*e09ba7baSBram Moolenaar 223*e09ba7baSBram Moolenaar exe 'sign place ' . (s:break_id + nr) . ' line=' . lnum . ' name=debugBreakpoint file=' . fnameescape(fname) 224*e09ba7baSBram Moolenaar 225*e09ba7baSBram Moolenaar let entry['fname'] = fname 226*e09ba7baSBram Moolenaar let entry['lnum'] = lnum 227*e09ba7baSBram Moolenaarendfunc 228*e09ba7baSBram Moolenaar 229*e09ba7baSBram Moolenaar" Handle deleting a breakpoint 230*e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 231*e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 232*e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 233*e09ba7baSBram Moolenaar if nr == 0 234*e09ba7baSBram Moolenaar return 235*e09ba7baSBram Moolenaar endif 236*e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 237*e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 238c572da5fSBram Moolenaarendfunc 239