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 120*68e6560bSBram 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 125*68e6560bSBram Moolenaar " If we make the Vim window wider, use the whole left halve for the debug 126*68e6560bSBram Moolenaar " windows. 127*68e6560bSBram 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 |" 172*68e6560bSBram Moolenaar if s:allleft 173*68e6560bSBram Moolenaar " use the whole left column 174*68e6560bSBram Moolenaar wincmd H 175*68e6560bSBram 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) 2293e4b84d0SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 230f63db65bSBram Moolenaar " response can be in the same line or the next line 231f63db65bSBram Moolenaar let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1) 2323e4b84d0SBram Moolenaar if response =~ 'Undefined command' 233f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 234ef3c6a5bSBram Moolenaar call s:CloseBuffers() 2353e4b84d0SBram Moolenaar return 2363e4b84d0SBram Moolenaar endif 2373e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2383e4b84d0SBram Moolenaar " Success! 2393e4b84d0SBram Moolenaar break 2403e4b84d0SBram Moolenaar endif 2413e4b84d0SBram Moolenaar endif 2423e4b84d0SBram Moolenaar endfor 2433e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2443e4b84d0SBram Moolenaar break 2453e4b84d0SBram Moolenaar endif 2463e4b84d0SBram Moolenaar let try_count += 1 2473e4b84d0SBram Moolenaar if try_count > 100 2483e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2493e4b84d0SBram Moolenaar break 2503e4b84d0SBram Moolenaar endif 2513e4b84d0SBram Moolenaar sleep 10m 2523e4b84d0SBram Moolenaar endwhile 2533e4b84d0SBram Moolenaar 25460e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 25560e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 25660e73f2aSBram Moolenaar " running. 25760e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 258b3307b5eSBram Moolenaar " Older gdb uses a different command. 259b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 260e09ba7baSBram Moolenaar 261f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 262f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 263b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 264f3ba14ffSBram Moolenaar 265ef3c6a5bSBram Moolenaar call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')}) 266b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 267b3307b5eSBram Moolenaarendfunc 268b3307b5eSBram Moolenaar 269b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 270b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 271b3307b5eSBram Moolenaar if s:vertical 272b3307b5eSBram Moolenaar vertical new 273b3307b5eSBram Moolenaar else 274b3307b5eSBram Moolenaar new 275b3307b5eSBram Moolenaar endif 276b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 277b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 278b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 279b3307b5eSBram Moolenaar set buftype=prompt 280b3307b5eSBram Moolenaar file gdb 281b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 282b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 283b3307b5eSBram Moolenaar 284b3307b5eSBram Moolenaar if s:vertical 285b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 286b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 287b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 288b3307b5eSBram Moolenaar endif 289b3307b5eSBram Moolenaar 290b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 291b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 292b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 293b3307b5eSBram Moolenaar 294b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 295b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 296b3307b5eSBram Moolenaar 297b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 298b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 299b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 300b3307b5eSBram Moolenaar \ }) 301b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 302b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 303b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 304b3307b5eSBram Moolenaar return 305b3307b5eSBram Moolenaar endif 3064551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 3074551c0a9SBram Moolenaar set modified 308b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 309b3307b5eSBram Moolenaar 310b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 311b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 312b3307b5eSBram Moolenaar " target is running. 313b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 314b3307b5eSBram Moolenaar " Older gdb uses a different command. 315b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 316b3307b5eSBram Moolenaar 317b3307b5eSBram Moolenaar let s:ptybuf = 0 318b3307b5eSBram Moolenaar if has('win32') 319b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 320b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 321b3307b5eSBram Moolenaar elseif has('terminal') 322b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 323b3307b5eSBram Moolenaar " gdb window. 324b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 325b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 326b3307b5eSBram Moolenaar \ }) 327b3307b5eSBram Moolenaar if s:ptybuf == 0 328b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 329b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 330b3307b5eSBram Moolenaar return 331b3307b5eSBram Moolenaar endif 332b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 333b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 334b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 335b3307b5eSBram Moolenaar 336b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 337b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 338b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 339b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 340b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 341b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 342b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 343b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 344b3307b5eSBram Moolenaar else 345b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 346b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 347b3307b5eSBram Moolenaar endif 348b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 349b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 350b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 351b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 352b3307b5eSBram Moolenaar 353b3307b5eSBram Moolenaar " Set arguments to be run 354b3307b5eSBram Moolenaar if len(proc_args) 355b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 356b3307b5eSBram Moolenaar endif 357b3307b5eSBram Moolenaar 358b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 359b3307b5eSBram Moolenaar startinsert 360b3307b5eSBram Moolenaarendfunc 361b3307b5eSBram Moolenaar 362b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 36338baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 36438baa3e6SBram Moolenaar " There can be only one. 36538baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 36638baa3e6SBram Moolenaar 36745d5f26dSBram Moolenaar " Install debugger commands in the text window. 368b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 369e09ba7baSBram Moolenaar call s:InstallCommands() 37045d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 371e09ba7baSBram Moolenaar 37251b0f370SBram Moolenaar " Enable showing a balloon with eval info 373246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 374246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 37551b0f370SBram Moolenaar if has("balloon_eval") 37651b0f370SBram Moolenaar set ballooneval 377246fe03dSBram Moolenaar endif 37851b0f370SBram Moolenaar if has("balloon_eval_term") 37951b0f370SBram Moolenaar set balloonevalterm 38051b0f370SBram Moolenaar endif 38151b0f370SBram Moolenaar endif 38251b0f370SBram Moolenaar 3835378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 3845378e1cfSBram Moolenaar " breakpoint number. 38537402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 38637402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 38737402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 38837402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 38937402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 390e09ba7baSBram Moolenaar let s:breakpoints = {} 3911b9645deSBram Moolenaar 39237402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 39337402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 39437402ed5SBram Moolenaar let s:breakpoint_locations = {} 39537402ed5SBram Moolenaar 3961b9645deSBram Moolenaar augroup TermDebug 3971b9645deSBram Moolenaar au BufRead * call s:BufRead() 3981b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 399f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 4001b9645deSBram Moolenaar augroup END 40132c67ba7SBram Moolenaar 402b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 403b3307b5eSBram Moolenaar " window. 40432c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 40532c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 40632c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 40732c67ba7SBram Moolenaar endif 408c572da5fSBram Moolenaarendfunc 409c572da5fSBram Moolenaar 410b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 411b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 412b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 413b3307b5eSBram Moolenaar if s:way == 'prompt' 414b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 415b3307b5eSBram Moolenaar else 416b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 417b3307b5eSBram Moolenaar endif 418b3307b5eSBram Moolenaarendfunc 419b3307b5eSBram Moolenaar 420b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 421b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 422b3307b5eSBram Moolenaar if s:way == 'prompt' 423b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 424b3307b5eSBram Moolenaar else 425b3307b5eSBram Moolenaar let do_continue = 0 426b3307b5eSBram Moolenaar if !s:stopped 427b3307b5eSBram Moolenaar let do_continue = 1 428b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 429b3307b5eSBram Moolenaar sleep 10m 430b3307b5eSBram Moolenaar endif 431b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 432b3307b5eSBram Moolenaar if do_continue 433b3307b5eSBram Moolenaar Continue 434b3307b5eSBram Moolenaar endif 435b3307b5eSBram Moolenaar endif 436b3307b5eSBram Moolenaarendfunc 437b3307b5eSBram Moolenaar 438b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 439b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 440b3307b5eSBram Moolenaar call s:SendCommand(a:text) 441b3307b5eSBram Moolenaarendfunc 442b3307b5eSBram Moolenaar 4434551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4444551c0a9SBram Moolenaar" breakpoint. 445b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4462ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4472ed890f1SBram Moolenaar if has('win32') 4482ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4492ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4504551c0a9SBram Moolenaar if s:pid == 0 4514551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4524551c0a9SBram Moolenaar else 4534551c0a9SBram Moolenaar call debugbreak(s:pid) 4544551c0a9SBram Moolenaar endif 4552ed890f1SBram Moolenaar else 4562ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4572ed890f1SBram Moolenaar endif 458b3307b5eSBram Moolenaarendfunc 459b3307b5eSBram Moolenaar 460b3307b5eSBram Moolenaar" Function called when gdb outputs text. 461b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 462b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 463b3307b5eSBram Moolenaar 464b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 465b3307b5eSBram Moolenaar " Drop status and echo'd commands. 466a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 467b3307b5eSBram Moolenaar return 468b3307b5eSBram Moolenaar endif 469b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 470b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 471b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 472b3307b5eSBram Moolenaar " Silently drop evaluation errors. 473b3307b5eSBram Moolenaar unlet s:evalexpr 474b3307b5eSBram Moolenaar return 475b3307b5eSBram Moolenaar endif 476b3307b5eSBram Moolenaar elseif a:text[0] == '~' 477b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 478b3307b5eSBram Moolenaar else 479b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 480b3307b5eSBram Moolenaar return 481b3307b5eSBram Moolenaar endif 482b3307b5eSBram Moolenaar 483b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 484b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 485b3307b5eSBram Moolenaar 486b3307b5eSBram Moolenaar " Add the output above the current prompt. 487b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4884551c0a9SBram Moolenaar set modified 489b3307b5eSBram Moolenaar 490b3307b5eSBram Moolenaar call win_gotoid(curwinid) 491b3307b5eSBram Moolenaarendfunc 492b3307b5eSBram Moolenaar 493b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 494b3307b5eSBram Moolenaar" to the next ", unescaping characters. 495b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 496b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 497a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 498b3307b5eSBram Moolenaar return 499b3307b5eSBram Moolenaar endif 500b3307b5eSBram Moolenaar let result = '' 501b3307b5eSBram Moolenaar let i = 1 502b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 503b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 504b3307b5eSBram Moolenaar let i += 1 505b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 506b3307b5eSBram Moolenaar " drop \n 507b3307b5eSBram Moolenaar let i += 1 508b3307b5eSBram Moolenaar continue 509b3307b5eSBram Moolenaar endif 510b3307b5eSBram Moolenaar endif 511b3307b5eSBram Moolenaar let result .= a:quotedText[i] 512b3307b5eSBram Moolenaar let i += 1 513b3307b5eSBram Moolenaar endwhile 514b3307b5eSBram Moolenaar return result 515b3307b5eSBram Moolenaarendfunc 516b3307b5eSBram Moolenaar 517a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 518a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5195378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5205378e1cfSBram Moolenaar return '' 5215378e1cfSBram Moolenaar endif 522a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 523a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 524a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 525a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 526a15b0a93SBram Moolenaar endif 527a15b0a93SBram Moolenaar return name 528a15b0a93SBram Moolenaarendfunc 529a15b0a93SBram Moolenaar 530b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 531fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 532b3623a38SBram Moolenaar unlet s:gdbwin 533e09ba7baSBram Moolenaar 534b3307b5eSBram Moolenaar call s:EndDebugCommon() 535b3307b5eSBram Moolenaarendfunc 536b3307b5eSBram Moolenaar 537b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 538e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 539e09ba7baSBram Moolenaar 540b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 541b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 542b3307b5eSBram Moolenaar endif 543b3307b5eSBram Moolenaar 544b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 545e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 546e09ba7baSBram Moolenaar call s:DeleteCommands() 547e09ba7baSBram Moolenaar 548e09ba7baSBram Moolenaar call win_gotoid(curwinid) 549b3307b5eSBram Moolenaar 55038baa3e6SBram Moolenaar if s:save_columns > 0 55138baa3e6SBram Moolenaar let &columns = s:save_columns 55238baa3e6SBram Moolenaar endif 5531b9645deSBram Moolenaar 554246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 555246fe03dSBram Moolenaar set balloonexpr= 55651b0f370SBram Moolenaar if has("balloon_eval") 55751b0f370SBram Moolenaar set noballooneval 558246fe03dSBram Moolenaar endif 55951b0f370SBram Moolenaar if has("balloon_eval_term") 56051b0f370SBram Moolenaar set noballoonevalterm 56151b0f370SBram Moolenaar endif 56251b0f370SBram Moolenaar endif 56351b0f370SBram Moolenaar 5641b9645deSBram Moolenaar au! TermDebug 565fe386641SBram Moolenaarendfunc 566fe386641SBram Moolenaar 567b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 568b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 569b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5704551c0a9SBram Moolenaar set nomodified 571b3307b5eSBram Moolenaar close 572b3307b5eSBram Moolenaar if curwinid != s:gdbwin 573b3307b5eSBram Moolenaar call win_gotoid(curwinid) 574b3307b5eSBram Moolenaar endif 575b3307b5eSBram Moolenaar 576b3307b5eSBram Moolenaar call s:EndDebugCommon() 577b3307b5eSBram Moolenaar unlet s:gdbwin 578b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 579b3307b5eSBram Moolenaarendfunc 580b3307b5eSBram Moolenaar 581fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 582fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 583fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 584fe386641SBram Moolenaar 585fe386641SBram Moolenaar for msg in msgs 586fe386641SBram Moolenaar " remove prefixed NL 587fe386641SBram Moolenaar if msg[0] == "\n" 588fe386641SBram Moolenaar let msg = msg[1:] 589fe386641SBram Moolenaar endif 590fe386641SBram Moolenaar if msg != '' 5911b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 592e09ba7baSBram Moolenaar call s:HandleCursor(msg) 59345d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 594e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 595e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 596e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 5974551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 5984551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 59945d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 60045d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 60145d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 60245d5f26dSBram Moolenaar call s:HandleError(msg) 603e09ba7baSBram Moolenaar endif 604e09ba7baSBram Moolenaar endif 605e09ba7baSBram Moolenaar endfor 606e09ba7baSBram Moolenaarendfunc 607e09ba7baSBram Moolenaar 608e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 609e09ba7baSBram Moolenaarfunc s:InstallCommands() 610963c1ad5SBram Moolenaar let save_cpo = &cpo 611963c1ad5SBram Moolenaar set cpo&vim 612963c1ad5SBram Moolenaar 613e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 61471137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 615e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 61645d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 617e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 61860e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 61960e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 62060e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 621b3307b5eSBram Moolenaar 622b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 623b3307b5eSBram Moolenaar if s:way == 'prompt' 624b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 625b3307b5eSBram Moolenaar else 626b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 627b3307b5eSBram Moolenaar endif 628b3307b5eSBram Moolenaar 62945d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 63045d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 63145d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 632b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 63371137fedSBram Moolenaar command Winbar call s:InstallWinbar() 63445d5f26dSBram Moolenaar 63545d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 63645d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 6371b9645deSBram Moolenaar 638f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 63971137fedSBram Moolenaar call s:InstallWinbar() 64071137fedSBram Moolenaar 64171137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 64271137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 64371137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 64471137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 64571137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 64671137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 64771137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 64871137fedSBram Moolenaar endif 64971137fedSBram Moolenaar endif 650963c1ad5SBram Moolenaar 651963c1ad5SBram Moolenaar let &cpo = save_cpo 65271137fedSBram Moolenaarendfunc 65371137fedSBram Moolenaar 65471137fedSBram Moolenaarlet s:winbar_winids = [] 65571137fedSBram Moolenaar 65671137fedSBram Moolenaar" Install the window toolbar in the current window. 65771137fedSBram Moolenaarfunc s:InstallWinbar() 658c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 65924a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 66024a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 66124a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 66224a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 66360e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 66424a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 66571137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 666c4b533e1SBram Moolenaar endif 667e09ba7baSBram Moolenaarendfunc 668e09ba7baSBram Moolenaar 669e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 670e09ba7baSBram Moolenaarfunc s:DeleteCommands() 671e09ba7baSBram Moolenaar delcommand Break 67271137fedSBram Moolenaar delcommand Clear 673e09ba7baSBram Moolenaar delcommand Step 67445d5f26dSBram Moolenaar delcommand Over 675e09ba7baSBram Moolenaar delcommand Finish 67660e73f2aSBram Moolenaar delcommand Run 67760e73f2aSBram Moolenaar delcommand Arguments 67860e73f2aSBram Moolenaar delcommand Stop 679e09ba7baSBram Moolenaar delcommand Continue 68045d5f26dSBram Moolenaar delcommand Evaluate 68145d5f26dSBram Moolenaar delcommand Gdb 68245d5f26dSBram Moolenaar delcommand Program 683b3623a38SBram Moolenaar delcommand Source 68471137fedSBram Moolenaar delcommand Winbar 68545d5f26dSBram Moolenaar 68645d5f26dSBram Moolenaar nunmap K 6871b9645deSBram Moolenaar 6881b9645deSBram Moolenaar if has('menu') 68971137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 69071137fedSBram Moolenaar let curwinid = win_getid(winnr()) 69171137fedSBram Moolenaar for winid in s:winbar_winids 69271137fedSBram Moolenaar if win_gotoid(winid) 6931b9645deSBram Moolenaar aunmenu WinBar.Step 6941b9645deSBram Moolenaar aunmenu WinBar.Next 6951b9645deSBram Moolenaar aunmenu WinBar.Finish 6961b9645deSBram Moolenaar aunmenu WinBar.Cont 69760e73f2aSBram Moolenaar aunmenu WinBar.Stop 6981b9645deSBram Moolenaar aunmenu WinBar.Eval 6991b9645deSBram Moolenaar endif 70071137fedSBram Moolenaar endfor 70171137fedSBram Moolenaar call win_gotoid(curwinid) 70271137fedSBram Moolenaar let s:winbar_winids = [] 70371137fedSBram Moolenaar 70471137fedSBram Moolenaar if exists('s:saved_mousemodel') 70571137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 70671137fedSBram Moolenaar unlet s:saved_mousemodel 70771137fedSBram Moolenaar aunmenu PopUp.-SEP3- 70871137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 70971137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 71071137fedSBram Moolenaar aunmenu PopUp.Evaluate 71171137fedSBram Moolenaar endif 71271137fedSBram Moolenaar endif 7131b9645deSBram Moolenaar 71445d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 71537402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 71637402ed5SBram Moolenaar for subid in keys(entries) 71737402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 71837402ed5SBram Moolenaar endfor 71945d5f26dSBram Moolenaar endfor 72045d5f26dSBram Moolenaar unlet s:breakpoints 72137402ed5SBram Moolenaar unlet s:breakpoint_locations 722a15b0a93SBram Moolenaar 723a15b0a93SBram Moolenaar sign undefine debugPC 724a15b0a93SBram Moolenaar for val in s:BreakpointSigns 725a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 726a15b0a93SBram Moolenaar endfor 7274551c0a9SBram Moolenaar let s:BreakpointSigns = [] 728e09ba7baSBram Moolenaarendfunc 729e09ba7baSBram Moolenaar 730e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 731e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 73260e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 73360e73f2aSBram Moolenaar " Interrupt to make it work. 73460e73f2aSBram Moolenaar let do_continue = 0 73560e73f2aSBram Moolenaar if !s:stopped 73660e73f2aSBram Moolenaar let do_continue = 1 737b3307b5eSBram Moolenaar if s:way == 'prompt' 7384551c0a9SBram Moolenaar call s:PromptInterrupt() 739b3307b5eSBram Moolenaar else 74060e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 741b3307b5eSBram Moolenaar endif 74260e73f2aSBram Moolenaar sleep 10m 74360e73f2aSBram Moolenaar endif 744a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 745a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 746a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 74760e73f2aSBram Moolenaar if do_continue 74860e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 74960e73f2aSBram Moolenaar endif 750e09ba7baSBram Moolenaarendfunc 751e09ba7baSBram Moolenaar 75271137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 75371137fedSBram Moolenaarfunc s:ClearBreakpoint() 754e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 755e09ba7baSBram Moolenaar let lnum = line('.') 75637402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 75737402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 75837402ed5SBram Moolenaar let idx = 0 75937402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 76037402ed5SBram Moolenaar if has_key(s:breakpoints, id) 76137402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 76237402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 76337402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 76437402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 76537402ed5SBram Moolenaar endfor 76637402ed5SBram Moolenaar unlet s:breakpoints[id] 76737402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 768e09ba7baSBram Moolenaar break 76937402ed5SBram Moolenaar else 77037402ed5SBram Moolenaar let idx += 1 771e09ba7baSBram Moolenaar endif 772e09ba7baSBram Moolenaar endfor 77337402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 77437402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 77537402ed5SBram Moolenaar endif 77637402ed5SBram Moolenaar endif 777e09ba7baSBram Moolenaarendfunc 778e09ba7baSBram Moolenaar 77960e73f2aSBram Moolenaarfunc s:Run(args) 78060e73f2aSBram Moolenaar if a:args != '' 78160e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 78260e73f2aSBram Moolenaar endif 78360e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 78460e73f2aSBram Moolenaarendfunc 78560e73f2aSBram Moolenaar 78651b0f370SBram Moolenaarfunc s:SendEval(expr) 78751b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 78851b0f370SBram Moolenaar let s:evalexpr = a:expr 78951b0f370SBram Moolenaarendfunc 79051b0f370SBram Moolenaar 79145d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 79245d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 79345d5f26dSBram Moolenaar if a:arg != '' 79445d5f26dSBram Moolenaar let expr = a:arg 79545d5f26dSBram Moolenaar elseif a:range == 2 79645d5f26dSBram Moolenaar let pos = getcurpos() 79745d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 79845d5f26dSBram Moolenaar let regt = getregtype('v') 79945d5f26dSBram Moolenaar normal! gv"vy 80045d5f26dSBram Moolenaar let expr = @v 80145d5f26dSBram Moolenaar call setpos('.', pos) 80245d5f26dSBram Moolenaar call setreg('v', reg, regt) 80345d5f26dSBram Moolenaar else 80445d5f26dSBram Moolenaar let expr = expand('<cexpr>') 80545d5f26dSBram Moolenaar endif 80622f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 80751b0f370SBram Moolenaar call s:SendEval(expr) 80845d5f26dSBram Moolenaarendfunc 80945d5f26dSBram Moolenaar 81022f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 81151b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 81251b0f370SBram Moolenaar 81345d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 81445d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 8151b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 8161b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 81751b0f370SBram Moolenaar if s:evalFromBalloonExpr 81851b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 81951b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 82051b0f370SBram Moolenaar else 82151b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 82251b0f370SBram Moolenaar endif 82351b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 82451b0f370SBram Moolenaar else 8251b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 82651b0f370SBram Moolenaar endif 8271b9645deSBram Moolenaar 8287f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 8291b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 83022f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 83151b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 83251b0f370SBram Moolenaar else 83351b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 8341b9645deSBram Moolenaar endif 83545d5f26dSBram Moolenaarendfunc 83645d5f26dSBram Moolenaar 83751b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 83851b0f370SBram Moolenaar" if there is any. 83951b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 840b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 841b3307b5eSBram Moolenaar return 842b3307b5eSBram Moolenaar endif 843b3307b5eSBram Moolenaar if !s:stopped 844b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 845b3307b5eSBram Moolenaar " mouse triggers a balloon. 84651b0f370SBram Moolenaar return 84751b0f370SBram Moolenaar endif 84851b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 84951b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 85022f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 85122f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 85251b0f370SBram Moolenaar return '' 85351b0f370SBram Moolenaarendfunc 85451b0f370SBram Moolenaar 85545d5f26dSBram Moolenaar" Handle an error. 85645d5f26dSBram Moolenaarfunc s:HandleError(msg) 85722f1d0e3SBram Moolenaar if s:ignoreEvalError 85851b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 85922f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 86022f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 86151b0f370SBram Moolenaar return 86251b0f370SBram Moolenaar endif 86345d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 86445d5f26dSBram Moolenaarendfunc 86545d5f26dSBram Moolenaar 866b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 867b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 868c4b533e1SBram Moolenaar new 869b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 870c4b533e1SBram Moolenaar call s:InstallWinbar() 871c4b533e1SBram Moolenaar endif 872c4b533e1SBram Moolenaarendfunc 873c4b533e1SBram Moolenaar 874e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 875e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 876e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 877fe386641SBram Moolenaar let wid = win_getid(winnr()) 878fe386641SBram Moolenaar 87960e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8804551c0a9SBram Moolenaar call ch_log('program stopped') 88160e73f2aSBram Moolenaar let s:stopped = 1 88260e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8834551c0a9SBram Moolenaar call ch_log('program running') 88460e73f2aSBram Moolenaar let s:stopped = 0 88560e73f2aSBram Moolenaar endif 88660e73f2aSBram Moolenaar 887a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 888a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 889a15b0a93SBram Moolenaar else 890a15b0a93SBram Moolenaar let fname = '' 891a15b0a93SBram Moolenaar endif 8921b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 893e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 894fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8954551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 8961b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 897fe386641SBram Moolenaar if &modified 898fe386641SBram Moolenaar " TODO: find existing window 899fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 900b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 901c4b533e1SBram Moolenaar call s:InstallWinbar() 902fe386641SBram Moolenaar else 903fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 904fe386641SBram Moolenaar endif 905fe386641SBram Moolenaar endif 906fe386641SBram Moolenaar exe lnum 90701164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 9081b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 909fe386641SBram Moolenaar setlocal signcolumn=yes 910fe386641SBram Moolenaar endif 9114551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 912fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 913fe386641SBram Moolenaar endif 914fe386641SBram Moolenaar 915fe386641SBram Moolenaar call win_gotoid(wid) 916e09ba7baSBram Moolenaarendfunc 917e09ba7baSBram Moolenaar 918de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 919a15b0a93SBram Moolenaar 92037402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 92137402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 92237402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 92337402ed5SBram Moolenaar call add(s:BreakpointSigns, nr) 92437402ed5SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 925de1a8314SBram Moolenaar endif 926de1a8314SBram Moolenaarendfunc 927de1a8314SBram Moolenaar 92837402ed5SBram Moolenaarfunc! s:SplitMsg(s) 92937402ed5SBram Moolenaar return split(a:s, '{.\{-}}\zs') 9305378e1cfSBram Moolenaarendfunction 9315378e1cfSBram Moolenaar 932e09ba7baSBram Moolenaar" Handle setting a breakpoint 933e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 934e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 9356dccc962SBram Moolenaar if a:msg !~ 'fullname=' 9366dccc962SBram Moolenaar " a watch does not have a file name 9376dccc962SBram Moolenaar return 9386dccc962SBram Moolenaar endif 9395378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 9405378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 9415378e1cfSBram Moolenaar if empty(fname) 9425378e1cfSBram Moolenaar continue 9435378e1cfSBram Moolenaar endif 9445378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 9455378e1cfSBram Moolenaar if empty(nr) 946e09ba7baSBram Moolenaar return 947fe386641SBram Moolenaar endif 948e09ba7baSBram Moolenaar 94937402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 95037402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 95137402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 95237402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 95337402ed5SBram Moolenaar 95437402ed5SBram Moolenaar if has_key(s:breakpoints, id) 95537402ed5SBram Moolenaar let entries = s:breakpoints[id] 95637402ed5SBram Moolenaar else 95737402ed5SBram Moolenaar let entries = {} 95837402ed5SBram Moolenaar let s:breakpoints[id] = entries 95937402ed5SBram Moolenaar endif 96037402ed5SBram Moolenaar if has_key(entries, subid) 96137402ed5SBram Moolenaar let entry = entries[subid] 962e09ba7baSBram Moolenaar else 963e09ba7baSBram Moolenaar let entry = {} 96437402ed5SBram Moolenaar let entries[subid] = entry 965fe386641SBram Moolenaar endif 966e09ba7baSBram Moolenaar 9675378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 968e09ba7baSBram Moolenaar let entry['fname'] = fname 969e09ba7baSBram Moolenaar let entry['lnum'] = lnum 9701b9645deSBram Moolenaar 97137402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 97237402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 97337402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 97437402ed5SBram Moolenaar endif 97537402ed5SBram Moolenaar let s:breakpoint_locations[bploc] += [id] 97637402ed5SBram Moolenaar 9771b9645deSBram Moolenaar if bufloaded(fname) 97837402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 9791b9645deSBram Moolenaar endif 9805378e1cfSBram Moolenaar endfor 9811b9645deSBram Moolenaarendfunc 9821b9645deSBram Moolenaar 98337402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry) 98437402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 98537402ed5SBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname'] 9861b9645deSBram Moolenaar let a:entry['placed'] = 1 987e09ba7baSBram Moolenaarendfunc 988e09ba7baSBram Moolenaar 989e09ba7baSBram Moolenaar" Handle deleting a breakpoint 990e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 991e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 99237402ed5SBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 99337402ed5SBram Moolenaar if empty(id) 994e09ba7baSBram Moolenaar return 995e09ba7baSBram Moolenaar endif 99637402ed5SBram Moolenaar if has_key(s:breakpoints, id) 99737402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 9981b9645deSBram Moolenaar if has_key(entry, 'placed') 99937402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 10001b9645deSBram Moolenaar unlet entry['placed'] 10011b9645deSBram Moolenaar endif 10025378e1cfSBram Moolenaar endfor 100337402ed5SBram Moolenaar unlet s:breakpoints[id] 100437402ed5SBram Moolenaar endif 1005c572da5fSBram Moolenaarendfunc 10061b9645deSBram Moolenaar 10074551c0a9SBram Moolenaar" Handle the debugged program starting to run. 10084551c0a9SBram Moolenaar" Will store the process ID in s:pid 10094551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 10104551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 10114551c0a9SBram Moolenaar if nr == 0 10124551c0a9SBram Moolenaar return 10134551c0a9SBram Moolenaar endif 10144551c0a9SBram Moolenaar let s:pid = nr 10154551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 10164551c0a9SBram Moolenaarendfunc 10174551c0a9SBram Moolenaar 10181b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 10191b9645deSBram Moolenaarfunc s:BufRead() 10201b9645deSBram Moolenaar let fname = expand('<afile>:p') 102137402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 102237402ed5SBram Moolenaar for [subid, entry] in items(entries) 10231b9645deSBram Moolenaar if entry['fname'] == fname 102437402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10251b9645deSBram Moolenaar endif 10261b9645deSBram Moolenaar endfor 102737402ed5SBram Moolenaar endfor 10281b9645deSBram Moolenaarendfunc 10291b9645deSBram Moolenaar 10301b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 10311b9645deSBram Moolenaarfunc s:BufUnloaded() 10321b9645deSBram Moolenaar let fname = expand('<afile>:p') 103337402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 103437402ed5SBram Moolenaar for [subid, entry] in items(entries) 10351b9645deSBram Moolenaar if entry['fname'] == fname 10361b9645deSBram Moolenaar let entry['placed'] = 0 10371b9645deSBram Moolenaar endif 10381b9645deSBram Moolenaar endfor 103937402ed5SBram Moolenaar endfor 10401b9645deSBram Moolenaarendfunc 1041ca4cc018SBram Moolenaar 1042ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 1043ca4cc018SBram Moolenaarunlet s:keepcpo 1044