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 1837c64c78SBram Moolenaar" In case this gets loaded twice. 1937c64c78SBram Moolenaarif exists(':Termdebug') 2037c64c78SBram Moolenaar finish 2137c64c78SBram Moolenaarendif 2237c64c78SBram Moolenaar 2360e73f2aSBram Moolenaar" Uncomment this line to write logging in "debuglog". 2460e73f2aSBram Moolenaar" call ch_logfile('debuglog', 'w') 2560e73f2aSBram Moolenaar 26fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 27fe386641SBram Moolenaar" To end type "quit" in the gdb window. 28*32c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>) 29*32c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) 30c572da5fSBram Moolenaar 31fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 32e09ba7baSBram Moolenaarif !exists('termdebugger') 33e09ba7baSBram Moolenaar let termdebugger = 'gdb' 34c572da5fSBram Moolenaarendif 35c572da5fSBram Moolenaar 36fe386641SBram Moolenaarlet s:pc_id = 12 37e09ba7baSBram Moolenaarlet s:break_id = 13 3860e73f2aSBram Moolenaarlet s:stopped = 1 39e09ba7baSBram Moolenaar 40e09ba7baSBram Moolenaarif &background == 'light' 41e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=lightblue guibg=lightblue 42e09ba7baSBram Moolenaarelse 43e09ba7baSBram Moolenaar hi default debugPC term=reverse ctermbg=darkblue guibg=darkblue 44e09ba7baSBram Moolenaarendif 45e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 46fe386641SBram Moolenaar 47*32c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 48*32c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 49*32c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 50*32c67ba7SBram Moolenaarendfunc 51*32c67ba7SBram Moolenaar 52*32c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 53*32c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 54*32c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 55*32c67ba7SBram Moolenaarendfunc 56*32c67ba7SBram Moolenaar 57*32c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 58b3623a38SBram Moolenaar if exists('s:gdbwin') 59b3623a38SBram Moolenaar echoerr 'Terminal debugger already running' 60b3623a38SBram Moolenaar return 61b3623a38SBram Moolenaar endif 62b3623a38SBram Moolenaar 63fe386641SBram Moolenaar let s:startwin = win_getid(winnr()) 64fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 65fe386641SBram Moolenaar 6624a98a0eSBram Moolenaar let s:save_columns = 0 6724a98a0eSBram Moolenaar if exists('g:termdebug_wide') 6824a98a0eSBram Moolenaar if &columns < g:termdebug_wide 6938baa3e6SBram Moolenaar let s:save_columns = &columns 7038baa3e6SBram Moolenaar let &columns = g:termdebug_wide 7124a98a0eSBram Moolenaar endif 7238baa3e6SBram Moolenaar let vertical = 1 7338baa3e6SBram Moolenaar else 7438baa3e6SBram Moolenaar let vertical = 0 7538baa3e6SBram Moolenaar endif 7638baa3e6SBram Moolenaar 77c572da5fSBram Moolenaar " Open a terminal window without a job, to run the debugged program 78fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 79fe386641SBram Moolenaar \ 'term_name': 'gdb program', 8038baa3e6SBram Moolenaar \ 'vertical': vertical, 81fe386641SBram Moolenaar \ }) 82fe386641SBram Moolenaar if s:ptybuf == 0 83fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 84fe386641SBram Moolenaar return 85fe386641SBram Moolenaar endif 86fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 8745d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 8851b0f370SBram Moolenaar if vertical 8951b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 9051b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 9151b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 9251b0f370SBram Moolenaar endif 93fe386641SBram Moolenaar 94fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 95fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 96fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 97fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 98fe386641SBram Moolenaar \ 'hidden': 1, 99fe386641SBram Moolenaar \ }) 100fe386641SBram Moolenaar if s:commbuf == 0 101fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 102fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 103fe386641SBram Moolenaar return 104fe386641SBram Moolenaar endif 105fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 106c572da5fSBram Moolenaar 107c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 108c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 109*32c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 110*32c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 111*32c67ba7SBram Moolenaar 112*32c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 113c572da5fSBram Moolenaar echomsg 'executing "' . join(cmd) . '"' 11460e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 115c572da5fSBram Moolenaar \ 'exit_cb': function('s:EndDebug'), 116fe386641SBram Moolenaar \ 'term_finish': 'close', 117c572da5fSBram Moolenaar \ }) 11860e73f2aSBram Moolenaar if s:gdbbuf == 0 119fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 120fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 121fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 122fe386641SBram Moolenaar return 123fe386641SBram Moolenaar endif 12445d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 125fe386641SBram Moolenaar 126*32c67ba7SBram Moolenaar " Set arguments to be run 127*32c67ba7SBram Moolenaar if len(proc_args) 128*32c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 129*32c67ba7SBram Moolenaar endif 130*32c67ba7SBram Moolenaar 131fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 13260e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 13360e73f2aSBram Moolenaar 1343e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 1353e4b84d0SBram Moolenaar " why the debugger doesn't work. 1363e4b84d0SBram Moolenaar let try_count = 0 1373e4b84d0SBram Moolenaar while 1 1383e4b84d0SBram Moolenaar let response = '' 139b3623a38SBram Moolenaar for lnum in range(1,200) 1403e4b84d0SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 1413e4b84d0SBram Moolenaar let response = term_getline(s:gdbbuf, lnum + 1) 1423e4b84d0SBram Moolenaar if response =~ 'Undefined command' 143f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 1443e4b84d0SBram Moolenaar exe 'bwipe! ' . s:ptybuf 1453e4b84d0SBram Moolenaar exe 'bwipe! ' . s:commbuf 1463e4b84d0SBram Moolenaar return 1473e4b84d0SBram Moolenaar endif 1483e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 1493e4b84d0SBram Moolenaar " Success! 1503e4b84d0SBram Moolenaar break 1513e4b84d0SBram Moolenaar endif 1523e4b84d0SBram Moolenaar endif 1533e4b84d0SBram Moolenaar endfor 1543e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 1553e4b84d0SBram Moolenaar break 1563e4b84d0SBram Moolenaar endif 1573e4b84d0SBram Moolenaar let try_count += 1 1583e4b84d0SBram Moolenaar if try_count > 100 1593e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 1603e4b84d0SBram Moolenaar break 1613e4b84d0SBram Moolenaar endif 1623e4b84d0SBram Moolenaar sleep 10m 1633e4b84d0SBram Moolenaar endwhile 1643e4b84d0SBram Moolenaar 16560e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 16660e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 16760e73f2aSBram Moolenaar " running. 16860e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 169e09ba7baSBram Moolenaar 170f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 171f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 172f3ba14ffSBram Moolenaar call s:SendCommand('-gdb-set pagination off') 173f3ba14ffSBram Moolenaar 17438baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 17538baa3e6SBram Moolenaar " There can be only one. 17638baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 17738baa3e6SBram Moolenaar 17838baa3e6SBram Moolenaar " Sign used to indicate a breakpoint. 17938baa3e6SBram Moolenaar " Can be used multiple times. 18038baa3e6SBram Moolenaar sign define debugBreakpoint text=>> texthl=debugBreakpoint 18138baa3e6SBram Moolenaar 18245d5f26dSBram Moolenaar " Install debugger commands in the text window. 18345d5f26dSBram Moolenaar call win_gotoid(s:startwin) 184e09ba7baSBram Moolenaar call s:InstallCommands() 18545d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 186e09ba7baSBram Moolenaar 18751b0f370SBram Moolenaar " Enable showing a balloon with eval info 188246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 189246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 19051b0f370SBram Moolenaar if has("balloon_eval") 19151b0f370SBram Moolenaar set ballooneval 192246fe03dSBram Moolenaar endif 19351b0f370SBram Moolenaar if has("balloon_eval_term") 19451b0f370SBram Moolenaar set balloonevalterm 19551b0f370SBram Moolenaar endif 19651b0f370SBram Moolenaar endif 19751b0f370SBram Moolenaar 198e09ba7baSBram Moolenaar let s:breakpoints = {} 1991b9645deSBram Moolenaar 2001b9645deSBram Moolenaar augroup TermDebug 2011b9645deSBram Moolenaar au BufRead * call s:BufRead() 2021b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 2031b9645deSBram Moolenaar augroup END 204*32c67ba7SBram Moolenaar 205*32c67ba7SBram Moolenaar " Run the command if the bang attribute was given 206*32c67ba7SBram Moolenaar " and got to the window 207*32c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 208*32c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 209*32c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 210*32c67ba7SBram Moolenaar endif 211*32c67ba7SBram Moolenaar 212c572da5fSBram Moolenaarendfunc 213c572da5fSBram Moolenaar 214c572da5fSBram Moolenaarfunc s:EndDebug(job, status) 215c572da5fSBram Moolenaar exe 'bwipe! ' . s:ptybuf 216fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 217b3623a38SBram Moolenaar unlet s:gdbwin 218e09ba7baSBram Moolenaar 219e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 220e09ba7baSBram Moolenaar 221e09ba7baSBram Moolenaar call win_gotoid(s:startwin) 222e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 223e09ba7baSBram Moolenaar call s:DeleteCommands() 224e09ba7baSBram Moolenaar 225e09ba7baSBram Moolenaar call win_gotoid(curwinid) 22638baa3e6SBram Moolenaar if s:save_columns > 0 22738baa3e6SBram Moolenaar let &columns = s:save_columns 22838baa3e6SBram Moolenaar endif 2291b9645deSBram Moolenaar 230246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 231246fe03dSBram Moolenaar set balloonexpr= 23251b0f370SBram Moolenaar if has("balloon_eval") 23351b0f370SBram Moolenaar set noballooneval 234246fe03dSBram Moolenaar endif 23551b0f370SBram Moolenaar if has("balloon_eval_term") 23651b0f370SBram Moolenaar set noballoonevalterm 23751b0f370SBram Moolenaar endif 23851b0f370SBram Moolenaar endif 23951b0f370SBram Moolenaar 2401b9645deSBram Moolenaar au! TermDebug 241fe386641SBram Moolenaarendfunc 242fe386641SBram Moolenaar 243fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 244fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 245fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 246fe386641SBram Moolenaar 247fe386641SBram Moolenaar for msg in msgs 248fe386641SBram Moolenaar " remove prefixed NL 249fe386641SBram Moolenaar if msg[0] == "\n" 250fe386641SBram Moolenaar let msg = msg[1:] 251fe386641SBram Moolenaar endif 252fe386641SBram Moolenaar if msg != '' 2531b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 254e09ba7baSBram Moolenaar call s:HandleCursor(msg) 25545d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 256e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 257e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 258e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 25945d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 26045d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 26145d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 26245d5f26dSBram Moolenaar call s:HandleError(msg) 263e09ba7baSBram Moolenaar endif 264e09ba7baSBram Moolenaar endif 265e09ba7baSBram Moolenaar endfor 266e09ba7baSBram Moolenaarendfunc 267e09ba7baSBram Moolenaar 268e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 269e09ba7baSBram Moolenaarfunc s:InstallCommands() 270e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 27171137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 272e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 27345d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 274e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 27560e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 27660e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 27760e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 278e09ba7baSBram Moolenaar command Continue call s:SendCommand('-exec-continue') 27945d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 28045d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 28145d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 282c4b533e1SBram Moolenaar command Source call s:GotoStartwinOrCreateIt() 28371137fedSBram Moolenaar command Winbar call s:InstallWinbar() 28445d5f26dSBram Moolenaar 28545d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 28645d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 2871b9645deSBram Moolenaar 288f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 28971137fedSBram Moolenaar call s:InstallWinbar() 29071137fedSBram Moolenaar 29171137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 29271137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 29371137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 29471137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 29571137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 29671137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 29771137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 29871137fedSBram Moolenaar endif 29971137fedSBram Moolenaar endif 30071137fedSBram Moolenaarendfunc 30171137fedSBram Moolenaar 30271137fedSBram Moolenaarlet s:winbar_winids = [] 30371137fedSBram Moolenaar 30471137fedSBram Moolenaar" Install the window toolbar in the current window. 30571137fedSBram Moolenaarfunc s:InstallWinbar() 306c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 30724a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 30824a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 30924a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 31024a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 31160e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 31224a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 31371137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 314c4b533e1SBram Moolenaar endif 315e09ba7baSBram Moolenaarendfunc 316e09ba7baSBram Moolenaar 317e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 318e09ba7baSBram Moolenaarfunc s:DeleteCommands() 319e09ba7baSBram Moolenaar delcommand Break 32071137fedSBram Moolenaar delcommand Clear 321e09ba7baSBram Moolenaar delcommand Step 32245d5f26dSBram Moolenaar delcommand Over 323e09ba7baSBram Moolenaar delcommand Finish 32460e73f2aSBram Moolenaar delcommand Run 32560e73f2aSBram Moolenaar delcommand Arguments 32660e73f2aSBram Moolenaar delcommand Stop 327e09ba7baSBram Moolenaar delcommand Continue 32845d5f26dSBram Moolenaar delcommand Evaluate 32945d5f26dSBram Moolenaar delcommand Gdb 33045d5f26dSBram Moolenaar delcommand Program 331b3623a38SBram Moolenaar delcommand Source 33271137fedSBram Moolenaar delcommand Winbar 33345d5f26dSBram Moolenaar 33445d5f26dSBram Moolenaar nunmap K 3351b9645deSBram Moolenaar 3361b9645deSBram Moolenaar if has('menu') 33771137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 33871137fedSBram Moolenaar let curwinid = win_getid(winnr()) 33971137fedSBram Moolenaar for winid in s:winbar_winids 34071137fedSBram Moolenaar if win_gotoid(winid) 3411b9645deSBram Moolenaar aunmenu WinBar.Step 3421b9645deSBram Moolenaar aunmenu WinBar.Next 3431b9645deSBram Moolenaar aunmenu WinBar.Finish 3441b9645deSBram Moolenaar aunmenu WinBar.Cont 34560e73f2aSBram Moolenaar aunmenu WinBar.Stop 3461b9645deSBram Moolenaar aunmenu WinBar.Eval 3471b9645deSBram Moolenaar endif 34871137fedSBram Moolenaar endfor 34971137fedSBram Moolenaar call win_gotoid(curwinid) 35071137fedSBram Moolenaar let s:winbar_winids = [] 35171137fedSBram Moolenaar 35271137fedSBram Moolenaar if exists('s:saved_mousemodel') 35371137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 35471137fedSBram Moolenaar unlet s:saved_mousemodel 35571137fedSBram Moolenaar aunmenu PopUp.-SEP3- 35671137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 35771137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 35871137fedSBram Moolenaar aunmenu PopUp.Evaluate 35971137fedSBram Moolenaar endif 36071137fedSBram Moolenaar endif 3611b9645deSBram Moolenaar 36245d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 36345d5f26dSBram Moolenaar for key in keys(s:breakpoints) 36445d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 36545d5f26dSBram Moolenaar endfor 36638baa3e6SBram Moolenaar sign undefine debugPC 36738baa3e6SBram Moolenaar sign undefine debugBreakpoint 36845d5f26dSBram Moolenaar unlet s:breakpoints 369e09ba7baSBram Moolenaarendfunc 370e09ba7baSBram Moolenaar 371e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 372e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 37360e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 37460e73f2aSBram Moolenaar " Interrupt to make it work. 37560e73f2aSBram Moolenaar let do_continue = 0 37660e73f2aSBram Moolenaar if !s:stopped 37760e73f2aSBram Moolenaar let do_continue = 1 37860e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 37960e73f2aSBram Moolenaar sleep 10m 38060e73f2aSBram Moolenaar endif 38160e73f2aSBram Moolenaar call s:SendCommand('-break-insert --source ' 38260e73f2aSBram Moolenaar \ . fnameescape(expand('%:p')) . ' --line ' . line('.')) 38360e73f2aSBram Moolenaar if do_continue 38460e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 38560e73f2aSBram Moolenaar endif 386e09ba7baSBram Moolenaarendfunc 387e09ba7baSBram Moolenaar 38871137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 38971137fedSBram Moolenaarfunc s:ClearBreakpoint() 390e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 391e09ba7baSBram Moolenaar let lnum = line('.') 392e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 393e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 394e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, '-break-delete ' . key . "\r") 395e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 396e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 397e09ba7baSBram Moolenaar unlet s:breakpoints[key] 398e09ba7baSBram Moolenaar break 399e09ba7baSBram Moolenaar endif 400e09ba7baSBram Moolenaar endfor 401e09ba7baSBram Moolenaarendfunc 402e09ba7baSBram Moolenaar 403e09ba7baSBram Moolenaar" :Next, :Continue, etc - send a command to gdb 404e09ba7baSBram Moolenaarfunc s:SendCommand(cmd) 405e09ba7baSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 406e09ba7baSBram Moolenaarendfunc 407e09ba7baSBram Moolenaar 40860e73f2aSBram Moolenaarfunc s:Run(args) 40960e73f2aSBram Moolenaar if a:args != '' 41060e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 41160e73f2aSBram Moolenaar endif 41260e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 41360e73f2aSBram Moolenaarendfunc 41460e73f2aSBram Moolenaar 41551b0f370SBram Moolenaarfunc s:SendEval(expr) 41651b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 41751b0f370SBram Moolenaar let s:evalexpr = a:expr 41851b0f370SBram Moolenaarendfunc 41951b0f370SBram Moolenaar 42045d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 42145d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 42245d5f26dSBram Moolenaar if a:arg != '' 42345d5f26dSBram Moolenaar let expr = a:arg 42445d5f26dSBram Moolenaar elseif a:range == 2 42545d5f26dSBram Moolenaar let pos = getcurpos() 42645d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 42745d5f26dSBram Moolenaar let regt = getregtype('v') 42845d5f26dSBram Moolenaar normal! gv"vy 42945d5f26dSBram Moolenaar let expr = @v 43045d5f26dSBram Moolenaar call setpos('.', pos) 43145d5f26dSBram Moolenaar call setreg('v', reg, regt) 43245d5f26dSBram Moolenaar else 43345d5f26dSBram Moolenaar let expr = expand('<cexpr>') 43445d5f26dSBram Moolenaar endif 43522f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 43651b0f370SBram Moolenaar call s:SendEval(expr) 43745d5f26dSBram Moolenaarendfunc 43845d5f26dSBram Moolenaar 43922f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 44051b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 44151b0f370SBram Moolenaar 44245d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 44345d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 4441b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 4451b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 44651b0f370SBram Moolenaar if s:evalFromBalloonExpr 44751b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 44851b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 44951b0f370SBram Moolenaar else 45051b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 45151b0f370SBram Moolenaar endif 45251b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 45351b0f370SBram Moolenaar else 4541b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 45551b0f370SBram Moolenaar endif 4561b9645deSBram Moolenaar 4577f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 4581b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 45922f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 46051b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 46151b0f370SBram Moolenaar else 46251b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 4631b9645deSBram Moolenaar endif 46445d5f26dSBram Moolenaarendfunc 46545d5f26dSBram Moolenaar 46651b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 46751b0f370SBram Moolenaar" if there is any. 46851b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 46951b0f370SBram Moolenaar if v:beval_winid != s:startwin 47051b0f370SBram Moolenaar return 47151b0f370SBram Moolenaar endif 47251b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 47351b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 47422f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 47522f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 47651b0f370SBram Moolenaar return '' 47751b0f370SBram Moolenaarendfunc 47851b0f370SBram Moolenaar 47945d5f26dSBram Moolenaar" Handle an error. 48045d5f26dSBram Moolenaarfunc s:HandleError(msg) 48122f1d0e3SBram Moolenaar if s:ignoreEvalError 48251b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 48322f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 48422f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 48551b0f370SBram Moolenaar return 48651b0f370SBram Moolenaar endif 48745d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 48845d5f26dSBram Moolenaarendfunc 48945d5f26dSBram Moolenaar 490c4b533e1SBram Moolenaarfunc s:GotoStartwinOrCreateIt() 491c4b533e1SBram Moolenaar if !win_gotoid(s:startwin) 492c4b533e1SBram Moolenaar new 493c4b533e1SBram Moolenaar let s:startwin = win_getid(winnr()) 494c4b533e1SBram Moolenaar call s:InstallWinbar() 495c4b533e1SBram Moolenaar endif 496c4b533e1SBram Moolenaarendfunc 497c4b533e1SBram Moolenaar 498e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 499e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 500e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 501fe386641SBram Moolenaar let wid = win_getid(winnr()) 502fe386641SBram Moolenaar 50360e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 50460e73f2aSBram Moolenaar let s:stopped = 1 50560e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 50660e73f2aSBram Moolenaar let s:stopped = 0 50760e73f2aSBram Moolenaar endif 50860e73f2aSBram Moolenaar 509c4b533e1SBram Moolenaar call s:GotoStartwinOrCreateIt() 510c4b533e1SBram Moolenaar 511e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 5121b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 513e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 514fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 5151b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 516fe386641SBram Moolenaar if &modified 517fe386641SBram Moolenaar " TODO: find existing window 518fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 519fe386641SBram Moolenaar let s:startwin = win_getid(winnr()) 520c4b533e1SBram Moolenaar call s:InstallWinbar() 521fe386641SBram Moolenaar else 522fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 523fe386641SBram Moolenaar endif 524fe386641SBram Moolenaar endif 525fe386641SBram Moolenaar exe lnum 52601164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 5271b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 528fe386641SBram Moolenaar setlocal signcolumn=yes 529fe386641SBram Moolenaar endif 530fe386641SBram Moolenaar else 531fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 532fe386641SBram Moolenaar endif 533fe386641SBram Moolenaar 534fe386641SBram Moolenaar call win_gotoid(wid) 535e09ba7baSBram Moolenaarendfunc 536e09ba7baSBram Moolenaar 537e09ba7baSBram Moolenaar" Handle setting a breakpoint 538e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 539e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 540e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 541e09ba7baSBram Moolenaar if nr == 0 542e09ba7baSBram Moolenaar return 543fe386641SBram Moolenaar endif 544e09ba7baSBram Moolenaar 545e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 546e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 547e09ba7baSBram Moolenaar else 548e09ba7baSBram Moolenaar let entry = {} 549e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 550fe386641SBram Moolenaar endif 551e09ba7baSBram Moolenaar 552e09ba7baSBram Moolenaar let fname = substitute(a:msg, '.*fullname="\([^"]*\)".*', '\1', '') 553e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 554e09ba7baSBram Moolenaar let entry['fname'] = fname 555e09ba7baSBram Moolenaar let entry['lnum'] = lnum 5561b9645deSBram Moolenaar 5571b9645deSBram Moolenaar if bufloaded(fname) 5581b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 5591b9645deSBram Moolenaar endif 5601b9645deSBram Moolenaarendfunc 5611b9645deSBram Moolenaar 5621b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 5631b9645deSBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint file=' . a:entry['fname'] 5641b9645deSBram Moolenaar let a:entry['placed'] = 1 565e09ba7baSBram Moolenaarendfunc 566e09ba7baSBram Moolenaar 567e09ba7baSBram Moolenaar" Handle deleting a breakpoint 568e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 569e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 570e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 571e09ba7baSBram Moolenaar if nr == 0 572e09ba7baSBram Moolenaar return 573e09ba7baSBram Moolenaar endif 5741b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 5751b9645deSBram Moolenaar let entry = s:breakpoints[nr] 5761b9645deSBram Moolenaar if has_key(entry, 'placed') 577e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 5781b9645deSBram Moolenaar unlet entry['placed'] 5791b9645deSBram Moolenaar endif 580e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 5811b9645deSBram Moolenaar endif 582c572da5fSBram Moolenaarendfunc 5831b9645deSBram Moolenaar 5841b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 5851b9645deSBram Moolenaarfunc s:BufRead() 5861b9645deSBram Moolenaar let fname = expand('<afile>:p') 5871b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 5881b9645deSBram Moolenaar if entry['fname'] == fname 5891b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 5901b9645deSBram Moolenaar endif 5911b9645deSBram Moolenaar endfor 5921b9645deSBram Moolenaarendfunc 5931b9645deSBram Moolenaar 5941b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 5951b9645deSBram Moolenaarfunc s:BufUnloaded() 5961b9645deSBram Moolenaar let fname = expand('<afile>:p') 5971b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 5981b9645deSBram Moolenaar if entry['fname'] == fname 5991b9645deSBram Moolenaar let entry['placed'] = 0 6001b9645deSBram Moolenaar endif 6011b9645deSBram Moolenaar endfor 6021b9645deSBram Moolenaarendfunc 6031b9645deSBram Moolenaar 604