1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3b3307b5eSBram Moolenaar" Author: Bram Moolenaar 4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license" 5b3307b5eSBram Moolenaar" Last Update: 2018 Jun 3 6c572da5fSBram Moolenaar" 7b3307b5eSBram Moolenaar" WORK IN PROGRESS - Only the basics work 8b3307b5eSBram Moolenaar" Note: On MS-Windows you need a recent version of gdb. The one included with 9b3307b5eSBram Moolenaar" MingW is too old (7.6.1). 10b3307b5eSBram Moolenaar" I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb 11fe386641SBram Moolenaar" 12b3307b5eSBram Moolenaar" There are two ways to run gdb: 13b3307b5eSBram Moolenaar" - In a terminal window; used if possible, does not work on MS-Windows 14b3307b5eSBram Moolenaar" Not used when g:termdebug_use_prompt is set to 1. 15b3307b5eSBram Moolenaar" - Using a "prompt" buffer; may use a terminal window for the program 16b3307b5eSBram Moolenaar" 17b3307b5eSBram Moolenaar" For both the current window is used to view source code and shows the 18b3307b5eSBram Moolenaar" current statement from gdb. 19b3307b5eSBram Moolenaar" 20b3307b5eSBram Moolenaar" USING A TERMINAL WINDOW 21b3307b5eSBram Moolenaar" 22b3307b5eSBram Moolenaar" Opens two visible terminal windows: 23b3307b5eSBram Moolenaar" 1. runs a pty for the debugged program, as with ":term NONE" 24b3307b5eSBram Moolenaar" 2. runs gdb, passing the pty of the debugged program 25fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb. 26fe386641SBram Moolenaar" 27b3307b5eSBram Moolenaar" USING A PROMPT BUFFER 28b3307b5eSBram Moolenaar" 29b3307b5eSBram Moolenaar" Opens a window with a prompt buffer to communicate with gdb. 30b3307b5eSBram Moolenaar" Gdb is run as a job with callbacks for I/O. 31b3307b5eSBram Moolenaar" On Unix another terminal window is opened to run the debugged program 32b3307b5eSBram Moolenaar" On MS-Windows a separate console is opened to run the debugged program 33b3307b5eSBram Moolenaar" 34fe386641SBram Moolenaar" The communication with gdb uses GDB/MI. See: 35fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html 36c572da5fSBram Moolenaar 37b3307b5eSBram Moolenaar" In case this gets sourced twice. 3837c64c78SBram Moolenaarif exists(':Termdebug') 3937c64c78SBram Moolenaar finish 4037c64c78SBram Moolenaarendif 4137c64c78SBram Moolenaar 42b3307b5eSBram Moolenaar" Need either the +terminal feature or +channel and the prompt buffer. 43b3307b5eSBram Moolenaar" The terminal feature does not work with gdb on win32. 44b3307b5eSBram Moolenaarif has('terminal') && !has('win32') 45b3307b5eSBram Moolenaar let s:way = 'terminal' 46b3307b5eSBram Moolenaarelseif has('channel') && exists('*prompt_setprompt') 47b3307b5eSBram Moolenaar let s:way = 'prompt' 48b3307b5eSBram Moolenaarelse 49b3307b5eSBram Moolenaar if has('terminal') 50b3307b5eSBram Moolenaar let s:err = 'Cannot debug, missing prompt buffer support' 51b3307b5eSBram Moolenaar else 52b3307b5eSBram Moolenaar let s:err = 'Cannot debug, +channel feature is not supported' 53b3307b5eSBram Moolenaar endif 54b3307b5eSBram Moolenaar command -nargs=* -complete=file -bang Termdebug echoerr s:err 55b3307b5eSBram Moolenaar command -nargs=+ -complete=file -bang TermdebugCommand echoerr s:err 56b3307b5eSBram Moolenaar finish 57b3307b5eSBram Moolenaarendif 5860e73f2aSBram Moolenaar 59ca4cc018SBram Moolenaarlet s:keepcpo = &cpo 60ca4cc018SBram Moolenaarset cpo&vim 61ca4cc018SBram Moolenaar 62fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 63fe386641SBram Moolenaar" To end type "quit" in the gdb window. 6432c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>) 6532c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) 66c572da5fSBram Moolenaar 67fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 68e09ba7baSBram Moolenaarif !exists('termdebugger') 69e09ba7baSBram Moolenaar let termdebugger = 'gdb' 70c572da5fSBram Moolenaarendif 71c572da5fSBram Moolenaar 72fe386641SBram Moolenaarlet s:pc_id = 12 73de1a8314SBram Moolenaarlet s:break_id = 13 " breakpoint number is added to this 7460e73f2aSBram Moolenaarlet s:stopped = 1 75e09ba7baSBram Moolenaar 765378e1cfSBram Moolenaar" Take a breakpoint number as used by GDB and turn it into an integer. 7737402ed5SBram Moolenaar" The breakpoint may contain a dot: 123.4 -> 123004 7837402ed5SBram Moolenaar" The main breakpoint has a zero subid. 7937402ed5SBram Moolenaarfunc s:Breakpoint2SignNumber(id, subid) 8037402ed5SBram Moolenaar return s:break_id + a:id * 1000 + a:subid 815378e1cfSBram Moolenaarendfunction 825378e1cfSBram Moolenaar 83f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new) 84f07f9e73SBram Moolenaar let default = a:init ? 'default ' : '' 85f07f9e73SBram Moolenaar if a:new ==# 'light' && a:old !=# 'light' 86f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" 87f07f9e73SBram Moolenaar elseif a:new ==# 'dark' && a:old !=# 'dark' 88f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue" 89e09ba7baSBram Moolenaar endif 90f07f9e73SBram Moolenaarendfunc 91f07f9e73SBram Moolenaar 92f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background) 93e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 94fe386641SBram Moolenaar 9532c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 9632c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 9732c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 9832c67ba7SBram Moolenaarendfunc 9932c67ba7SBram Moolenaar 10032c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 10132c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 10232c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 10332c67ba7SBram Moolenaarendfunc 10432c67ba7SBram Moolenaar 10532c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 106b3623a38SBram Moolenaar if exists('s:gdbwin') 107b3623a38SBram Moolenaar echoerr 'Terminal debugger already running' 108b3623a38SBram Moolenaar return 109b3623a38SBram Moolenaar endif 110b3307b5eSBram Moolenaar let s:ptywin = 0 1114551c0a9SBram Moolenaar let s:pid = 0 112b3623a38SBram Moolenaar 113b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 114b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 115b3307b5eSBram Moolenaar 116b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 117fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 118fe386641SBram Moolenaar 11924a98a0eSBram Moolenaar let s:save_columns = 0 12068e6560bSBram Moolenaar let s:allleft = 0 12124a98a0eSBram Moolenaar if exists('g:termdebug_wide') 12224a98a0eSBram Moolenaar if &columns < g:termdebug_wide 12338baa3e6SBram Moolenaar let s:save_columns = &columns 12438baa3e6SBram Moolenaar let &columns = g:termdebug_wide 12568e6560bSBram Moolenaar " If we make the Vim window wider, use the whole left halve for the debug 12668e6560bSBram Moolenaar " windows. 12768e6560bSBram Moolenaar let s:allleft = 1 12824a98a0eSBram Moolenaar endif 129b3307b5eSBram Moolenaar let s:vertical = 1 13038baa3e6SBram Moolenaar else 131b3307b5eSBram Moolenaar let s:vertical = 0 13238baa3e6SBram Moolenaar endif 13338baa3e6SBram Moolenaar 134b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 135b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 136b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 137b3307b5eSBram Moolenaar let s:way = 'terminal' 138b3307b5eSBram Moolenaar else 139b3307b5eSBram Moolenaar let s:way = 'prompt' 140b3307b5eSBram Moolenaar endif 141b3307b5eSBram Moolenaar 142b3307b5eSBram Moolenaar if s:way == 'prompt' 143b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 144b3307b5eSBram Moolenaar else 145b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 146b3307b5eSBram Moolenaar endif 147b3307b5eSBram Moolenaarendfunc 148b3307b5eSBram Moolenaar 149ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended. 150ef3c6a5bSBram Moolenaarfunc s:CloseBuffers() 151ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:ptybuf 152ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:commbuf 153ef3c6a5bSBram Moolenaar unlet! s:gdbwin 154ef3c6a5bSBram Moolenaarendfunc 155ef3c6a5bSBram Moolenaar 156b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 157b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 158fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 159b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 160b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 161fe386641SBram Moolenaar \ }) 162fe386641SBram Moolenaar if s:ptybuf == 0 163fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 164fe386641SBram Moolenaar return 165fe386641SBram Moolenaar endif 166fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 16745d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 168b3307b5eSBram Moolenaar if s:vertical 16951b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 17051b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 17151b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 17268e6560bSBram Moolenaar if s:allleft 17368e6560bSBram Moolenaar " use the whole left column 17468e6560bSBram Moolenaar wincmd H 17568e6560bSBram Moolenaar endif 17651b0f370SBram Moolenaar endif 177fe386641SBram Moolenaar 178fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 179fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 180fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 181fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 182fe386641SBram Moolenaar \ 'hidden': 1, 183fe386641SBram Moolenaar \ }) 184fe386641SBram Moolenaar if s:commbuf == 0 185fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 186fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 187fe386641SBram Moolenaar return 188fe386641SBram Moolenaar endif 189fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 190c572da5fSBram Moolenaar 191c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 192c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 19332c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 19432c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 19532c67ba7SBram Moolenaar 19632c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 197b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 19860e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 199fe386641SBram Moolenaar \ 'term_finish': 'close', 200c572da5fSBram Moolenaar \ }) 20160e73f2aSBram Moolenaar if s:gdbbuf == 0 202fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 203ef3c6a5bSBram Moolenaar call s:CloseBuffers() 204fe386641SBram Moolenaar return 205fe386641SBram Moolenaar endif 20645d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 207fe386641SBram Moolenaar 20832c67ba7SBram Moolenaar " Set arguments to be run 20932c67ba7SBram Moolenaar if len(proc_args) 21032c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 21132c67ba7SBram Moolenaar endif 21232c67ba7SBram Moolenaar 213fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 21460e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 21560e73f2aSBram Moolenaar 2163e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 2173e4b84d0SBram Moolenaar " why the debugger doesn't work. 2183e4b84d0SBram Moolenaar let try_count = 0 2193e4b84d0SBram Moolenaar while 1 220ef3c6a5bSBram Moolenaar let gdbproc = term_getjob(s:gdbbuf) 221ef3c6a5bSBram Moolenaar if gdbproc == v:null || job_status(gdbproc) !=# 'run' 222ef3c6a5bSBram Moolenaar echoerr string(g:termdebugger) . ' exited unexpectedly' 223ef3c6a5bSBram Moolenaar call s:CloseBuffers() 224ef3c6a5bSBram Moolenaar return 225ef3c6a5bSBram Moolenaar endif 226ef3c6a5bSBram Moolenaar 2273e4b84d0SBram Moolenaar let response = '' 228b3623a38SBram Moolenaar for lnum in range(1, 200) 22919c8fe19SBram Moolenaar let line1 = term_getline(s:gdbbuf, lnum) 23019c8fe19SBram Moolenaar let line2 = term_getline(s:gdbbuf, lnum + 1) 23119c8fe19SBram Moolenaar if line1 =~ 'new-ui mi ' 232f63db65bSBram Moolenaar " response can be in the same line or the next line 23319c8fe19SBram Moolenaar let response = line1 . line2 2343e4b84d0SBram Moolenaar if response =~ 'Undefined command' 235f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 236ef3c6a5bSBram Moolenaar call s:CloseBuffers() 2373e4b84d0SBram Moolenaar return 2383e4b84d0SBram Moolenaar endif 2393e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2403e4b84d0SBram Moolenaar " Success! 2413e4b84d0SBram Moolenaar break 2423e4b84d0SBram Moolenaar endif 24319c8fe19SBram Moolenaar elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' 24419c8fe19SBram Moolenaar " Reading symbols might take a while, try more times 24506fe74aeSBram Moolenaar let try_count -= 1 24606fe74aeSBram Moolenaar endif 2473e4b84d0SBram Moolenaar endfor 2483e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2493e4b84d0SBram Moolenaar break 2503e4b84d0SBram Moolenaar endif 2513e4b84d0SBram Moolenaar let try_count += 1 2523e4b84d0SBram Moolenaar if try_count > 100 2533e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2543e4b84d0SBram Moolenaar break 2553e4b84d0SBram Moolenaar endif 2563e4b84d0SBram Moolenaar sleep 10m 2573e4b84d0SBram Moolenaar endwhile 2583e4b84d0SBram Moolenaar 25960e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 26060e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 26160e73f2aSBram Moolenaar " running. 26260e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 263b3307b5eSBram Moolenaar " Older gdb uses a different command. 264b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 265e09ba7baSBram Moolenaar 266f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 267f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 268b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 269f3ba14ffSBram Moolenaar 270ef3c6a5bSBram Moolenaar call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')}) 271b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 272b3307b5eSBram Moolenaarendfunc 273b3307b5eSBram Moolenaar 274b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 275b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 276b3307b5eSBram Moolenaar if s:vertical 277b3307b5eSBram Moolenaar vertical new 278b3307b5eSBram Moolenaar else 279b3307b5eSBram Moolenaar new 280b3307b5eSBram Moolenaar endif 281b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 282b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 283b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 284b3307b5eSBram Moolenaar set buftype=prompt 285b3307b5eSBram Moolenaar file gdb 286b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 287b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 288b3307b5eSBram Moolenaar 289b3307b5eSBram Moolenaar if s:vertical 290b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 291b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 292b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 293b3307b5eSBram Moolenaar endif 294b3307b5eSBram Moolenaar 295b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 296b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 297b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 298b3307b5eSBram Moolenaar 299b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 300b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 301b3307b5eSBram Moolenaar 302b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 303b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 304b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 305b3307b5eSBram Moolenaar \ }) 306b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 307b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 308b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 309b3307b5eSBram Moolenaar return 310b3307b5eSBram Moolenaar endif 3114551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 3124551c0a9SBram Moolenaar set modified 313b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 314b3307b5eSBram Moolenaar 315b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 316b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 317b3307b5eSBram Moolenaar " target is running. 318b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 319b3307b5eSBram Moolenaar " Older gdb uses a different command. 320b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 321b3307b5eSBram Moolenaar 322b3307b5eSBram Moolenaar let s:ptybuf = 0 323b3307b5eSBram Moolenaar if has('win32') 324b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 325b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 326b3307b5eSBram Moolenaar elseif has('terminal') 327b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 328b3307b5eSBram Moolenaar " gdb window. 329b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 330b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 331b3307b5eSBram Moolenaar \ }) 332b3307b5eSBram Moolenaar if s:ptybuf == 0 333b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 334b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 335b3307b5eSBram Moolenaar return 336b3307b5eSBram Moolenaar endif 337b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 338b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 339b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 340b3307b5eSBram Moolenaar 341b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 342b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 343b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 344b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 345b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 346b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 347b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 348b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 349b3307b5eSBram Moolenaar else 350b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 351b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 352b3307b5eSBram Moolenaar endif 353b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 354b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 355b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 356b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 357b3307b5eSBram Moolenaar 358b3307b5eSBram Moolenaar " Set arguments to be run 359b3307b5eSBram Moolenaar if len(proc_args) 360b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 361b3307b5eSBram Moolenaar endif 362b3307b5eSBram Moolenaar 363b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 364b3307b5eSBram Moolenaar startinsert 365b3307b5eSBram Moolenaarendfunc 366b3307b5eSBram Moolenaar 367b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 36838baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 36938baa3e6SBram Moolenaar " There can be only one. 37038baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 37138baa3e6SBram Moolenaar 37245d5f26dSBram Moolenaar " Install debugger commands in the text window. 373b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 374e09ba7baSBram Moolenaar call s:InstallCommands() 37545d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 376e09ba7baSBram Moolenaar 37751b0f370SBram Moolenaar " Enable showing a balloon with eval info 378246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 379246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 38051b0f370SBram Moolenaar if has("balloon_eval") 38151b0f370SBram Moolenaar set ballooneval 382246fe03dSBram Moolenaar endif 38351b0f370SBram Moolenaar if has("balloon_eval_term") 38451b0f370SBram Moolenaar set balloonevalterm 38551b0f370SBram Moolenaar endif 38651b0f370SBram Moolenaar endif 38751b0f370SBram Moolenaar 3885378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 3895378e1cfSBram Moolenaar " breakpoint number. 39037402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 39137402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 39237402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 39337402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 39437402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 395e09ba7baSBram Moolenaar let s:breakpoints = {} 3961b9645deSBram Moolenaar 39737402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 39837402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 39937402ed5SBram Moolenaar let s:breakpoint_locations = {} 40037402ed5SBram Moolenaar 4011b9645deSBram Moolenaar augroup TermDebug 4021b9645deSBram Moolenaar au BufRead * call s:BufRead() 4031b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 404f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 4051b9645deSBram Moolenaar augroup END 40632c67ba7SBram Moolenaar 407b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 408b3307b5eSBram Moolenaar " window. 40932c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 41032c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 41132c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 41232c67ba7SBram Moolenaar endif 413c572da5fSBram Moolenaarendfunc 414c572da5fSBram Moolenaar 415b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 416b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 417b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 418b3307b5eSBram Moolenaar if s:way == 'prompt' 419b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 420b3307b5eSBram Moolenaar else 421b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 422b3307b5eSBram Moolenaar endif 423b3307b5eSBram Moolenaarendfunc 424b3307b5eSBram Moolenaar 425b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 426b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 427b3307b5eSBram Moolenaar if s:way == 'prompt' 428b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 429b3307b5eSBram Moolenaar else 430b3307b5eSBram Moolenaar let do_continue = 0 431b3307b5eSBram Moolenaar if !s:stopped 432b3307b5eSBram Moolenaar let do_continue = 1 433b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 434b3307b5eSBram Moolenaar sleep 10m 435b3307b5eSBram Moolenaar endif 436b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 437b3307b5eSBram Moolenaar if do_continue 438b3307b5eSBram Moolenaar Continue 439b3307b5eSBram Moolenaar endif 440b3307b5eSBram Moolenaar endif 441b3307b5eSBram Moolenaarendfunc 442b3307b5eSBram Moolenaar 443b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 444b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 445b3307b5eSBram Moolenaar call s:SendCommand(a:text) 446b3307b5eSBram Moolenaarendfunc 447b3307b5eSBram Moolenaar 4484551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4494551c0a9SBram Moolenaar" breakpoint. 450b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4512ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4522ed890f1SBram Moolenaar if has('win32') 4532ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4542ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4554551c0a9SBram Moolenaar if s:pid == 0 4564551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4574551c0a9SBram Moolenaar else 4584551c0a9SBram Moolenaar call debugbreak(s:pid) 4594551c0a9SBram Moolenaar endif 4602ed890f1SBram Moolenaar else 4612ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4622ed890f1SBram Moolenaar endif 463b3307b5eSBram Moolenaarendfunc 464b3307b5eSBram Moolenaar 465b3307b5eSBram Moolenaar" Function called when gdb outputs text. 466b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 467b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 468b3307b5eSBram Moolenaar 469b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 470b3307b5eSBram Moolenaar " Drop status and echo'd commands. 471a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 472b3307b5eSBram Moolenaar return 473b3307b5eSBram Moolenaar endif 474b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 475b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 476b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 477b3307b5eSBram Moolenaar " Silently drop evaluation errors. 478b3307b5eSBram Moolenaar unlet s:evalexpr 479b3307b5eSBram Moolenaar return 480b3307b5eSBram Moolenaar endif 481b3307b5eSBram Moolenaar elseif a:text[0] == '~' 482b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 483b3307b5eSBram Moolenaar else 484b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 485b3307b5eSBram Moolenaar return 486b3307b5eSBram Moolenaar endif 487b3307b5eSBram Moolenaar 488b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 489b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 490b3307b5eSBram Moolenaar 491b3307b5eSBram Moolenaar " Add the output above the current prompt. 492b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4934551c0a9SBram Moolenaar set modified 494b3307b5eSBram Moolenaar 495b3307b5eSBram Moolenaar call win_gotoid(curwinid) 496b3307b5eSBram Moolenaarendfunc 497b3307b5eSBram Moolenaar 498b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 499b3307b5eSBram Moolenaar" to the next ", unescaping characters. 500b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 501b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 502a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 503b3307b5eSBram Moolenaar return 504b3307b5eSBram Moolenaar endif 505b3307b5eSBram Moolenaar let result = '' 506b3307b5eSBram Moolenaar let i = 1 507b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 508b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 509b3307b5eSBram Moolenaar let i += 1 510b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 511b3307b5eSBram Moolenaar " drop \n 512b3307b5eSBram Moolenaar let i += 1 513b3307b5eSBram Moolenaar continue 514*589edb34SBram Moolenaar elseif a:quotedText[i] == 't' 515*589edb34SBram Moolenaar " append \t 516*589edb34SBram Moolenaar let i += 1 517*589edb34SBram Moolenaar let result .= "\t" 518*589edb34SBram Moolenaar continue 519b3307b5eSBram Moolenaar endif 520b3307b5eSBram Moolenaar endif 521b3307b5eSBram Moolenaar let result .= a:quotedText[i] 522b3307b5eSBram Moolenaar let i += 1 523b3307b5eSBram Moolenaar endwhile 524b3307b5eSBram Moolenaar return result 525b3307b5eSBram Moolenaarendfunc 526b3307b5eSBram Moolenaar 527a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 528a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5295378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5305378e1cfSBram Moolenaar return '' 5315378e1cfSBram Moolenaar endif 532a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 533a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 534a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 535a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 536a15b0a93SBram Moolenaar endif 537a15b0a93SBram Moolenaar return name 538a15b0a93SBram Moolenaarendfunc 539a15b0a93SBram Moolenaar 540b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 541fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 542b3623a38SBram Moolenaar unlet s:gdbwin 543e09ba7baSBram Moolenaar 544b3307b5eSBram Moolenaar call s:EndDebugCommon() 545b3307b5eSBram Moolenaarendfunc 546b3307b5eSBram Moolenaar 547b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 548e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 549e09ba7baSBram Moolenaar 550b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 551b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 552b3307b5eSBram Moolenaar endif 553b3307b5eSBram Moolenaar 554b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 555e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 556e09ba7baSBram Moolenaar call s:DeleteCommands() 557e09ba7baSBram Moolenaar 558e09ba7baSBram Moolenaar call win_gotoid(curwinid) 559b3307b5eSBram Moolenaar 56038baa3e6SBram Moolenaar if s:save_columns > 0 56138baa3e6SBram Moolenaar let &columns = s:save_columns 56238baa3e6SBram Moolenaar endif 5631b9645deSBram Moolenaar 564246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 565246fe03dSBram Moolenaar set balloonexpr= 56651b0f370SBram Moolenaar if has("balloon_eval") 56751b0f370SBram Moolenaar set noballooneval 568246fe03dSBram Moolenaar endif 56951b0f370SBram Moolenaar if has("balloon_eval_term") 57051b0f370SBram Moolenaar set noballoonevalterm 57151b0f370SBram Moolenaar endif 57251b0f370SBram Moolenaar endif 57351b0f370SBram Moolenaar 5741b9645deSBram Moolenaar au! TermDebug 575fe386641SBram Moolenaarendfunc 576fe386641SBram Moolenaar 577b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 578b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 579b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5804551c0a9SBram Moolenaar set nomodified 581b3307b5eSBram Moolenaar close 582b3307b5eSBram Moolenaar if curwinid != s:gdbwin 583b3307b5eSBram Moolenaar call win_gotoid(curwinid) 584b3307b5eSBram Moolenaar endif 585b3307b5eSBram Moolenaar 586b3307b5eSBram Moolenaar call s:EndDebugCommon() 587b3307b5eSBram Moolenaar unlet s:gdbwin 588b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 589b3307b5eSBram Moolenaarendfunc 590b3307b5eSBram Moolenaar 591fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 592fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 593fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 594fe386641SBram Moolenaar 595fe386641SBram Moolenaar for msg in msgs 596fe386641SBram Moolenaar " remove prefixed NL 597fe386641SBram Moolenaar if msg[0] == "\n" 598fe386641SBram Moolenaar let msg = msg[1:] 599fe386641SBram Moolenaar endif 600fe386641SBram Moolenaar if msg != '' 6011b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 602e09ba7baSBram Moolenaar call s:HandleCursor(msg) 60345d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 604e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 605e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 606e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 6074551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 6084551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 60945d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 61045d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 61145d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 61245d5f26dSBram Moolenaar call s:HandleError(msg) 613e09ba7baSBram Moolenaar endif 614e09ba7baSBram Moolenaar endif 615e09ba7baSBram Moolenaar endfor 616e09ba7baSBram Moolenaarendfunc 617e09ba7baSBram Moolenaar 618*589edb34SBram Moolenaarfunc s:GotoProgram() 619*589edb34SBram Moolenaar if has('win32') 620*589edb34SBram Moolenaar if executable('powershell') 621*589edb34SBram Moolenaar call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid)) 622*589edb34SBram Moolenaar endif 623*589edb34SBram Moolenaar else 624*589edb34SBram Moolenaar win_gotoid(s:ptywin) 625*589edb34SBram Moolenaar endif 626*589edb34SBram Moolenaarendfunc 627*589edb34SBram Moolenaar 628e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 629e09ba7baSBram Moolenaarfunc s:InstallCommands() 630963c1ad5SBram Moolenaar let save_cpo = &cpo 631963c1ad5SBram Moolenaar set cpo&vim 632963c1ad5SBram Moolenaar 633*589edb34SBram Moolenaar command -nargs=? Break call s:SetBreakpoint(<q-args>) 63471137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 635e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 63645d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 637e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 63860e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 63960e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 64060e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 641b3307b5eSBram Moolenaar 642b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 643b3307b5eSBram Moolenaar if s:way == 'prompt' 644b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 645b3307b5eSBram Moolenaar else 646b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 647b3307b5eSBram Moolenaar endif 648b3307b5eSBram Moolenaar 64945d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 65045d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 651*589edb34SBram Moolenaar command Program call s:GotoProgram() 652b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 65371137fedSBram Moolenaar command Winbar call s:InstallWinbar() 65445d5f26dSBram Moolenaar 65545d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 65645d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 6571b9645deSBram Moolenaar 658f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 65971137fedSBram Moolenaar call s:InstallWinbar() 66071137fedSBram Moolenaar 66171137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 66271137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 66371137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 66471137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 66571137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 66671137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 66771137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 66871137fedSBram Moolenaar endif 66971137fedSBram Moolenaar endif 670963c1ad5SBram Moolenaar 671963c1ad5SBram Moolenaar let &cpo = save_cpo 67271137fedSBram Moolenaarendfunc 67371137fedSBram Moolenaar 67471137fedSBram Moolenaarlet s:winbar_winids = [] 67571137fedSBram Moolenaar 67671137fedSBram Moolenaar" Install the window toolbar in the current window. 67771137fedSBram Moolenaarfunc s:InstallWinbar() 678c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 67924a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 68024a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 68124a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 68224a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 68360e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 68424a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 68571137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 686c4b533e1SBram Moolenaar endif 687e09ba7baSBram Moolenaarendfunc 688e09ba7baSBram Moolenaar 689e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 690e09ba7baSBram Moolenaarfunc s:DeleteCommands() 691e09ba7baSBram Moolenaar delcommand Break 69271137fedSBram Moolenaar delcommand Clear 693e09ba7baSBram Moolenaar delcommand Step 69445d5f26dSBram Moolenaar delcommand Over 695e09ba7baSBram Moolenaar delcommand Finish 69660e73f2aSBram Moolenaar delcommand Run 69760e73f2aSBram Moolenaar delcommand Arguments 69860e73f2aSBram Moolenaar delcommand Stop 699e09ba7baSBram Moolenaar delcommand Continue 70045d5f26dSBram Moolenaar delcommand Evaluate 70145d5f26dSBram Moolenaar delcommand Gdb 70245d5f26dSBram Moolenaar delcommand Program 703b3623a38SBram Moolenaar delcommand Source 70471137fedSBram Moolenaar delcommand Winbar 70545d5f26dSBram Moolenaar 70645d5f26dSBram Moolenaar nunmap K 7071b9645deSBram Moolenaar 7081b9645deSBram Moolenaar if has('menu') 70971137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 71071137fedSBram Moolenaar let curwinid = win_getid(winnr()) 71171137fedSBram Moolenaar for winid in s:winbar_winids 71271137fedSBram Moolenaar if win_gotoid(winid) 7131b9645deSBram Moolenaar aunmenu WinBar.Step 7141b9645deSBram Moolenaar aunmenu WinBar.Next 7151b9645deSBram Moolenaar aunmenu WinBar.Finish 7161b9645deSBram Moolenaar aunmenu WinBar.Cont 71760e73f2aSBram Moolenaar aunmenu WinBar.Stop 7181b9645deSBram Moolenaar aunmenu WinBar.Eval 7191b9645deSBram Moolenaar endif 72071137fedSBram Moolenaar endfor 72171137fedSBram Moolenaar call win_gotoid(curwinid) 72271137fedSBram Moolenaar let s:winbar_winids = [] 72371137fedSBram Moolenaar 72471137fedSBram Moolenaar if exists('s:saved_mousemodel') 72571137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 72671137fedSBram Moolenaar unlet s:saved_mousemodel 72771137fedSBram Moolenaar aunmenu PopUp.-SEP3- 72871137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 72971137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 73071137fedSBram Moolenaar aunmenu PopUp.Evaluate 73171137fedSBram Moolenaar endif 73271137fedSBram Moolenaar endif 7331b9645deSBram Moolenaar 73445d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 73537402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 73637402ed5SBram Moolenaar for subid in keys(entries) 73737402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 73837402ed5SBram Moolenaar endfor 73945d5f26dSBram Moolenaar endfor 74045d5f26dSBram Moolenaar unlet s:breakpoints 74137402ed5SBram Moolenaar unlet s:breakpoint_locations 742a15b0a93SBram Moolenaar 743a15b0a93SBram Moolenaar sign undefine debugPC 744a15b0a93SBram Moolenaar for val in s:BreakpointSigns 745a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 746a15b0a93SBram Moolenaar endfor 7474551c0a9SBram Moolenaar let s:BreakpointSigns = [] 748e09ba7baSBram Moolenaarendfunc 749e09ba7baSBram Moolenaar 750e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 751*589edb34SBram Moolenaarfunc s:SetBreakpoint(at) 75260e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 75360e73f2aSBram Moolenaar " Interrupt to make it work. 75460e73f2aSBram Moolenaar let do_continue = 0 75560e73f2aSBram Moolenaar if !s:stopped 75660e73f2aSBram Moolenaar let do_continue = 1 757b3307b5eSBram Moolenaar if s:way == 'prompt' 7584551c0a9SBram Moolenaar call s:PromptInterrupt() 759b3307b5eSBram Moolenaar else 76060e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 761b3307b5eSBram Moolenaar endif 76260e73f2aSBram Moolenaar sleep 10m 76360e73f2aSBram Moolenaar endif 764*589edb34SBram Moolenaar 765a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 766*589edb34SBram Moolenaar let at = empty(a:at) ? 767*589edb34SBram Moolenaar \ fnameescape(expand('%:p')) . ':' . line('.') : a:at 768*589edb34SBram Moolenaar call s:SendCommand('-break-insert ' . at) 76960e73f2aSBram Moolenaar if do_continue 77060e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 77160e73f2aSBram Moolenaar endif 772e09ba7baSBram Moolenaarendfunc 773e09ba7baSBram Moolenaar 77471137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 77571137fedSBram Moolenaarfunc s:ClearBreakpoint() 776e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 777e09ba7baSBram Moolenaar let lnum = line('.') 77837402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 77937402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 78037402ed5SBram Moolenaar let idx = 0 78137402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 78237402ed5SBram Moolenaar if has_key(s:breakpoints, id) 78337402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 78437402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 78537402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 78637402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 78737402ed5SBram Moolenaar endfor 78837402ed5SBram Moolenaar unlet s:breakpoints[id] 78937402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 790e09ba7baSBram Moolenaar break 79137402ed5SBram Moolenaar else 79237402ed5SBram Moolenaar let idx += 1 793e09ba7baSBram Moolenaar endif 794e09ba7baSBram Moolenaar endfor 79537402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 79637402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 79737402ed5SBram Moolenaar endif 79837402ed5SBram Moolenaar endif 799e09ba7baSBram Moolenaarendfunc 800e09ba7baSBram Moolenaar 80160e73f2aSBram Moolenaarfunc s:Run(args) 80260e73f2aSBram Moolenaar if a:args != '' 80360e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 80460e73f2aSBram Moolenaar endif 80560e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 80660e73f2aSBram Moolenaarendfunc 80760e73f2aSBram Moolenaar 80851b0f370SBram Moolenaarfunc s:SendEval(expr) 80951b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 81051b0f370SBram Moolenaar let s:evalexpr = a:expr 81151b0f370SBram Moolenaarendfunc 81251b0f370SBram Moolenaar 81345d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 81445d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 81545d5f26dSBram Moolenaar if a:arg != '' 81645d5f26dSBram Moolenaar let expr = a:arg 81745d5f26dSBram Moolenaar elseif a:range == 2 81845d5f26dSBram Moolenaar let pos = getcurpos() 81945d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 82045d5f26dSBram Moolenaar let regt = getregtype('v') 82145d5f26dSBram Moolenaar normal! gv"vy 82245d5f26dSBram Moolenaar let expr = @v 82345d5f26dSBram Moolenaar call setpos('.', pos) 82445d5f26dSBram Moolenaar call setreg('v', reg, regt) 82545d5f26dSBram Moolenaar else 82645d5f26dSBram Moolenaar let expr = expand('<cexpr>') 82745d5f26dSBram Moolenaar endif 82822f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 82951b0f370SBram Moolenaar call s:SendEval(expr) 83045d5f26dSBram Moolenaarendfunc 83145d5f26dSBram Moolenaar 83222f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 83351b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 83451b0f370SBram Moolenaar 83545d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 83645d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 8371b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 8381b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 83951b0f370SBram Moolenaar if s:evalFromBalloonExpr 84051b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 84151b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 84251b0f370SBram Moolenaar else 84351b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 84451b0f370SBram Moolenaar endif 84551b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 84651b0f370SBram Moolenaar else 8471b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 84851b0f370SBram Moolenaar endif 8491b9645deSBram Moolenaar 8507f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 8511b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 85222f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 85351b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 85451b0f370SBram Moolenaar else 85551b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 8561b9645deSBram Moolenaar endif 85745d5f26dSBram Moolenaarendfunc 85845d5f26dSBram Moolenaar 85951b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 86051b0f370SBram Moolenaar" if there is any. 86151b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 862b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 863396e829fSBram Moolenaar return '' 864b3307b5eSBram Moolenaar endif 865b3307b5eSBram Moolenaar if !s:stopped 866b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 867b3307b5eSBram Moolenaar " mouse triggers a balloon. 868396e829fSBram Moolenaar return '' 86951b0f370SBram Moolenaar endif 87051b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 87151b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 87222f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 87322f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 87451b0f370SBram Moolenaar return '' 87551b0f370SBram Moolenaarendfunc 87651b0f370SBram Moolenaar 87745d5f26dSBram Moolenaar" Handle an error. 87845d5f26dSBram Moolenaarfunc s:HandleError(msg) 87922f1d0e3SBram Moolenaar if s:ignoreEvalError 88051b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 88122f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 88222f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 88351b0f370SBram Moolenaar return 88451b0f370SBram Moolenaar endif 88545d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 88645d5f26dSBram Moolenaarendfunc 88745d5f26dSBram Moolenaar 888b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 889b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 890c4b533e1SBram Moolenaar new 891b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 892c4b533e1SBram Moolenaar call s:InstallWinbar() 893c4b533e1SBram Moolenaar endif 894c4b533e1SBram Moolenaarendfunc 895c4b533e1SBram Moolenaar 896e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 897e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 898e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 899fe386641SBram Moolenaar let wid = win_getid(winnr()) 900fe386641SBram Moolenaar 90160e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 9024551c0a9SBram Moolenaar call ch_log('program stopped') 90360e73f2aSBram Moolenaar let s:stopped = 1 90460e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 9054551c0a9SBram Moolenaar call ch_log('program running') 90660e73f2aSBram Moolenaar let s:stopped = 0 90760e73f2aSBram Moolenaar endif 90860e73f2aSBram Moolenaar 909a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 910a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 911a15b0a93SBram Moolenaar else 912a15b0a93SBram Moolenaar let fname = '' 913a15b0a93SBram Moolenaar endif 9141b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 915e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 916fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 9174551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 9181b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 919fe386641SBram Moolenaar if &modified 920fe386641SBram Moolenaar " TODO: find existing window 921fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 922b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 923c4b533e1SBram Moolenaar call s:InstallWinbar() 924fe386641SBram Moolenaar else 925fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 926fe386641SBram Moolenaar endif 927fe386641SBram Moolenaar endif 928fe386641SBram Moolenaar exe lnum 92901164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 9301b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 931fe386641SBram Moolenaar setlocal signcolumn=yes 932fe386641SBram Moolenaar endif 9334551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 934fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 935fe386641SBram Moolenaar endif 936fe386641SBram Moolenaar 937fe386641SBram Moolenaar call win_gotoid(wid) 938e09ba7baSBram Moolenaarendfunc 939e09ba7baSBram Moolenaar 940de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 941a15b0a93SBram Moolenaar 94237402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 94337402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 94437402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 94537402ed5SBram Moolenaar call add(s:BreakpointSigns, nr) 94637402ed5SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 947de1a8314SBram Moolenaar endif 948de1a8314SBram Moolenaarendfunc 949de1a8314SBram Moolenaar 95037402ed5SBram Moolenaarfunc! s:SplitMsg(s) 95137402ed5SBram Moolenaar return split(a:s, '{.\{-}}\zs') 9525378e1cfSBram Moolenaarendfunction 9535378e1cfSBram Moolenaar 954e09ba7baSBram Moolenaar" Handle setting a breakpoint 955e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 956e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 9576dccc962SBram Moolenaar if a:msg !~ 'fullname=' 9586dccc962SBram Moolenaar " a watch does not have a file name 9596dccc962SBram Moolenaar return 9606dccc962SBram Moolenaar endif 9615378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 9625378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 9635378e1cfSBram Moolenaar if empty(fname) 9645378e1cfSBram Moolenaar continue 9655378e1cfSBram Moolenaar endif 9665378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 9675378e1cfSBram Moolenaar if empty(nr) 968e09ba7baSBram Moolenaar return 969fe386641SBram Moolenaar endif 970e09ba7baSBram Moolenaar 97137402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 97237402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 97337402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 97437402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 97537402ed5SBram Moolenaar 97637402ed5SBram Moolenaar if has_key(s:breakpoints, id) 97737402ed5SBram Moolenaar let entries = s:breakpoints[id] 97837402ed5SBram Moolenaar else 97937402ed5SBram Moolenaar let entries = {} 98037402ed5SBram Moolenaar let s:breakpoints[id] = entries 98137402ed5SBram Moolenaar endif 98237402ed5SBram Moolenaar if has_key(entries, subid) 98337402ed5SBram Moolenaar let entry = entries[subid] 984e09ba7baSBram Moolenaar else 985e09ba7baSBram Moolenaar let entry = {} 98637402ed5SBram Moolenaar let entries[subid] = entry 987fe386641SBram Moolenaar endif 988e09ba7baSBram Moolenaar 9895378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 990e09ba7baSBram Moolenaar let entry['fname'] = fname 991e09ba7baSBram Moolenaar let entry['lnum'] = lnum 9921b9645deSBram Moolenaar 99337402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 99437402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 99537402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 99637402ed5SBram Moolenaar endif 99737402ed5SBram Moolenaar let s:breakpoint_locations[bploc] += [id] 99837402ed5SBram Moolenaar 9991b9645deSBram Moolenaar if bufloaded(fname) 100037402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10011b9645deSBram Moolenaar endif 10025378e1cfSBram Moolenaar endfor 10031b9645deSBram Moolenaarendfunc 10041b9645deSBram Moolenaar 100537402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry) 100637402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 100737402ed5SBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname'] 10081b9645deSBram Moolenaar let a:entry['placed'] = 1 1009e09ba7baSBram Moolenaarendfunc 1010e09ba7baSBram Moolenaar 1011e09ba7baSBram Moolenaar" Handle deleting a breakpoint 1012e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 1013e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 101437402ed5SBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 101537402ed5SBram Moolenaar if empty(id) 1016e09ba7baSBram Moolenaar return 1017e09ba7baSBram Moolenaar endif 101837402ed5SBram Moolenaar if has_key(s:breakpoints, id) 101937402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 10201b9645deSBram Moolenaar if has_key(entry, 'placed') 102137402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 10221b9645deSBram Moolenaar unlet entry['placed'] 10231b9645deSBram Moolenaar endif 10245378e1cfSBram Moolenaar endfor 102537402ed5SBram Moolenaar unlet s:breakpoints[id] 102637402ed5SBram Moolenaar endif 1027c572da5fSBram Moolenaarendfunc 10281b9645deSBram Moolenaar 10294551c0a9SBram Moolenaar" Handle the debugged program starting to run. 10304551c0a9SBram Moolenaar" Will store the process ID in s:pid 10314551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 10324551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 10334551c0a9SBram Moolenaar if nr == 0 10344551c0a9SBram Moolenaar return 10354551c0a9SBram Moolenaar endif 10364551c0a9SBram Moolenaar let s:pid = nr 10374551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 10384551c0a9SBram Moolenaarendfunc 10394551c0a9SBram Moolenaar 10401b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 10411b9645deSBram Moolenaarfunc s:BufRead() 10421b9645deSBram Moolenaar let fname = expand('<afile>:p') 104337402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 104437402ed5SBram Moolenaar for [subid, entry] in items(entries) 10451b9645deSBram Moolenaar if entry['fname'] == fname 104637402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10471b9645deSBram Moolenaar endif 10481b9645deSBram Moolenaar endfor 104937402ed5SBram Moolenaar endfor 10501b9645deSBram Moolenaarendfunc 10511b9645deSBram Moolenaar 10521b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 10531b9645deSBram Moolenaarfunc s:BufUnloaded() 10541b9645deSBram Moolenaar let fname = expand('<afile>:p') 105537402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 105637402ed5SBram Moolenaar for [subid, entry] in items(entries) 10571b9645deSBram Moolenaar if entry['fname'] == fname 10581b9645deSBram Moolenaar let entry['placed'] = 0 10591b9645deSBram Moolenaar endif 10601b9645deSBram Moolenaar endfor 106137402ed5SBram Moolenaar endfor 10621b9645deSBram Moolenaarendfunc 1063ca4cc018SBram Moolenaar 1064ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 1065ca4cc018SBram Moolenaarunlet s:keepcpo 1066