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". 23e09ba7baSBram Moolenaarif !exists('termdebugger') 24e09ba7baSBram Moolenaar let termdebugger = 'gdb' 25c572da5fSBram Moolenaarendif 26c572da5fSBram Moolenaar 27fe386641SBram Moolenaar" Sign used to highlight the line where the program has stopped. 28e09ba7baSBram Moolenaar" There can be only one. 29fe386641SBram Moolenaarsign define debugPC linehl=debugPC 30fe386641SBram Moolenaarlet s:pc_id = 12 31e09ba7baSBram Moolenaarlet s:break_id = 13 32e09ba7baSBram Moolenaar 33e09ba7baSBram Moolenaar" Sign used to indicate a breakpoint. 34e09ba7baSBram Moolenaar" Can be used multiple times. 35e09ba7baSBram Moolenaarsign define debugBreakpoint text=>> texthl=debugBreakpoint 36e09ba7baSBram Moolenaar 37e09ba7baSBram Moolenaarif &background == 'light' 38e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue 39e09ba7baSBram Moolenaarelse 40e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue 41e09ba7baSBram Moolenaarendif 42e09ba7baSBram 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'] 57*45d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 58fe386641SBram Moolenaar 59fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 60fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 61fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 62fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 63fe386641SBram Moolenaar \ 'hidden': 1, 64fe386641SBram Moolenaar \ }) 65fe386641SBram Moolenaar if s:commbuf == 0 66fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 67fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 68fe386641SBram Moolenaar return 69fe386641SBram Moolenaar endif 70fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 71c572da5fSBram Moolenaar 72c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 73e09ba7baSBram Moolenaar let cmd = [g:termdebugger, '-tty', pty, a:cmd] 74c572da5fSBram Moolenaar echomsg 'executing "' . join(cmd) . '"' 75c572da5fSBram Moolenaar let gdbbuf = term_start(cmd, { 76c572da5fSBram Moolenaar \ 'exit_cb': function('s:EndDebug'), 77fe386641SBram Moolenaar \ 'term_finish': 'close', 78c572da5fSBram Moolenaar \ }) 79fe386641SBram Moolenaar if gdbbuf == 0 80fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 81fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 82fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 83fe386641SBram Moolenaar return 84fe386641SBram Moolenaar endif 85*45d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 86fe386641SBram Moolenaar 87fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 88fe386641SBram Moolenaar call term_sendkeys(gdbbuf, 'new-ui mi ' . commpty . "\r") 89e09ba7baSBram Moolenaar 90*45d5f26dSBram Moolenaar " Install debugger commands in the text window. 91*45d5f26dSBram Moolenaar call win_gotoid(s:startwin) 92e09ba7baSBram Moolenaar call s:InstallCommands() 93*45d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 94e09ba7baSBram Moolenaar 95e09ba7baSBram Moolenaar let s:breakpoints = {} 96c572da5fSBram Moolenaarendfunc 97c572da5fSBram Moolenaar 98c572da5fSBram Moolenaarfunc s:EndDebug(job, status) 99c572da5fSBram Moolenaar exe 'bwipe! ' . s:ptybuf 100fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 101e09ba7baSBram Moolenaar 102e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 103e09ba7baSBram Moolenaar 104e09ba7baSBram Moolenaar call win_gotoid(s:startwin) 105e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 106e09ba7baSBram Moolenaar call s:DeleteCommands() 107e09ba7baSBram Moolenaar 108e09ba7baSBram Moolenaar call win_gotoid(curwinid) 109fe386641SBram Moolenaarendfunc 110fe386641SBram Moolenaar 111fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 112fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 113fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 114fe386641SBram Moolenaar 115fe386641SBram Moolenaar for msg in msgs 116fe386641SBram Moolenaar " remove prefixed NL 117fe386641SBram Moolenaar if msg[0] == "\n" 118fe386641SBram Moolenaar let msg = msg[1:] 119fe386641SBram Moolenaar endif 120fe386641SBram Moolenaar if msg != '' 121fe386641SBram Moolenaar if msg =~ '^\*\(stopped\|running\)' 122e09ba7baSBram Moolenaar call s:HandleCursor(msg) 123*45d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 124e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 125e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 126e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 127*45d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 128*45d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 129*45d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 130*45d5f26dSBram Moolenaar call s:HandleError(msg) 131e09ba7baSBram Moolenaar endif 132e09ba7baSBram Moolenaar endif 133e09ba7baSBram Moolenaar endfor 134e09ba7baSBram Moolenaarendfunc 135e09ba7baSBram Moolenaar 136e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 137e09ba7baSBram Moolenaarfunc s:InstallCommands() 138e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 139e09ba7baSBram Moolenaar command Delete call s:DeleteBreakpoint() 140e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 141*45d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 142e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 143e09ba7baSBram Moolenaar command Continue call s:SendCommand('-exec-continue') 144*45d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 145*45d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 146*45d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 147*45d5f26dSBram Moolenaar 148*45d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 149*45d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 150e09ba7baSBram Moolenaarendfunc 151e09ba7baSBram Moolenaar 152e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 153e09ba7baSBram Moolenaarfunc s:DeleteCommands() 154e09ba7baSBram Moolenaar delcommand Break 155e09ba7baSBram Moolenaar delcommand Delete 156e09ba7baSBram Moolenaar delcommand Step 157*45d5f26dSBram Moolenaar delcommand Over 158e09ba7baSBram Moolenaar delcommand Finish 159e09ba7baSBram Moolenaar delcommand Continue 160*45d5f26dSBram Moolenaar delcommand Evaluate 161*45d5f26dSBram Moolenaar delcommand Gdb 162*45d5f26dSBram Moolenaar delcommand Program 163*45d5f26dSBram Moolenaar 164*45d5f26dSBram Moolenaar nunmap K 165*45d5f26dSBram Moolenaar sign undefine debugPC 166*45d5f26dSBram Moolenaar sign undefine debugBreakpoint 167*45d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 168*45d5f26dSBram Moolenaar for key in keys(s:breakpoints) 169*45d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 170*45d5f26dSBram Moolenaar endfor 171*45d5f26dSBram Moolenaar unlet s:breakpoints 172e09ba7baSBram Moolenaarendfunc 173e09ba7baSBram Moolenaar 174e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 175e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 176e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, '-break-insert --source ' 177e09ba7baSBram Moolenaar \ . fnameescape(expand('%:p')) . ' --line ' . line('.') . "\r") 178e09ba7baSBram Moolenaarendfunc 179e09ba7baSBram Moolenaar 180e09ba7baSBram Moolenaar" :Delete - Delete a breakpoint at the cursor position. 181e09ba7baSBram Moolenaarfunc s:DeleteBreakpoint() 182e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 183e09ba7baSBram Moolenaar let lnum = line('.') 184e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 185e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 186e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r") 187e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 188e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 189e09ba7baSBram Moolenaar unlet s:breakpoints[key] 190e09ba7baSBram Moolenaar break 191e09ba7baSBram Moolenaar endif 192e09ba7baSBram Moolenaar endfor 193e09ba7baSBram Moolenaarendfunc 194e09ba7baSBram Moolenaar 195e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb 196e09ba7baSBram Moolenaarfunc s:SendCommand(cmd) 197e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 198e09ba7baSBram Moolenaarendfunc 199e09ba7baSBram Moolenaar 200*45d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 201*45d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 202*45d5f26dSBram Moolenaar if a:arg != '' 203*45d5f26dSBram Moolenaar let expr = a:arg 204*45d5f26dSBram Moolenaar elseif a:range == 2 205*45d5f26dSBram Moolenaar let pos = getcurpos() 206*45d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 207*45d5f26dSBram Moolenaar let regt = getregtype('v') 208*45d5f26dSBram Moolenaar normal! gv"vy 209*45d5f26dSBram Moolenaar let expr = @v 210*45d5f26dSBram Moolenaar call setpos('.', pos) 211*45d5f26dSBram Moolenaar call setreg('v', reg, regt) 212*45d5f26dSBram Moolenaar else 213*45d5f26dSBram Moolenaar let expr = expand('<cexpr>') 214*45d5f26dSBram Moolenaar endif 215*45d5f26dSBram Moolenaar call term_sendkeys(s:commbuf, '-data-evaluate-expression "' . expr . "\"\r") 216*45d5f26dSBram Moolenaar let s:evalexpr = expr 217*45d5f26dSBram Moolenaarendfunc 218*45d5f26dSBram Moolenaar 219*45d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 220*45d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 221*45d5f26dSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . substitute(a:msg, '.*value="\(.*\)"', '\1', '') 222*45d5f26dSBram Moolenaarendfunc 223*45d5f26dSBram Moolenaar 224*45d5f26dSBram Moolenaar" Handle an error. 225*45d5f26dSBram Moolenaarfunc s:HandleError(msg) 226*45d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 227*45d5f26dSBram Moolenaarendfunc 228*45d5f26dSBram Moolenaar 229e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 230e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 231e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 232fe386641SBram Moolenaar let wid = win_getid(winnr()) 233fe386641SBram Moolenaar 234fe386641SBram Moolenaar if win_gotoid(s:startwin) 235e09ba7baSBram Moolenaar if a:msg =~ '^\*stopped' 236e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 237e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 238fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 239fe386641SBram Moolenaar if expand('%:h') != fname 240fe386641SBram Moolenaar if &modified 241fe386641SBram Moolenaar " TODO: find existing window 242fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 243fe386641SBram Moolenaar let s:startwin = win_getid(winnr()) 244fe386641SBram Moolenaar else 245fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 246fe386641SBram Moolenaar endif 247fe386641SBram Moolenaar endif 248fe386641SBram Moolenaar exe lnum 249fe386641SBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fnameescape(fname) 250fe386641SBram Moolenaar setlocal signcolumn=yes 251fe386641SBram Moolenaar endif 252fe386641SBram Moolenaar else 253fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 254fe386641SBram Moolenaar endif 255fe386641SBram Moolenaar 256fe386641SBram Moolenaar call win_gotoid(wid) 257fe386641SBram Moolenaar endif 258e09ba7baSBram Moolenaarendfunc 259e09ba7baSBram Moolenaar 260e09ba7baSBram Moolenaar" Handle setting a breakpoint 261e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 262e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 263e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 264e09ba7baSBram Moolenaar if nr == 0 265e09ba7baSBram Moolenaar return 266fe386641SBram Moolenaar endif 267e09ba7baSBram Moolenaar 268e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 269e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 270e09ba7baSBram Moolenaar else 271e09ba7baSBram Moolenaar let entry = {} 272e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 273fe386641SBram Moolenaar endif 274e09ba7baSBram Moolenaar 275e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 276e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 277e09ba7baSBram Moolenaar 278e09ba7baSBram Moolenaar exe 'sign place ' . (s:break_id + nr) . ' line=' . lnum . ' name=debugBreakpoint file=' . fnameescape(fname) 279e09ba7baSBram Moolenaar 280e09ba7baSBram Moolenaar let entry['fname'] = fname 281e09ba7baSBram Moolenaar let entry['lnum'] = lnum 282e09ba7baSBram Moolenaarendfunc 283e09ba7baSBram Moolenaar 284e09ba7baSBram Moolenaar" Handle deleting a breakpoint 285e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 286e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 287e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 288e09ba7baSBram Moolenaar if nr == 0 289e09ba7baSBram Moolenaar return 290e09ba7baSBram Moolenaar endif 291e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 292e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 293c572da5fSBram Moolenaarendfunc 294