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) 229*19c8fe19SBram Moolenaar let line1 = term_getline(s:gdbbuf, lnum) 230*19c8fe19SBram Moolenaar let line2 = term_getline(s:gdbbuf, lnum + 1) 231*19c8fe19SBram Moolenaar if line1 =~ 'new-ui mi ' 232f63db65bSBram Moolenaar " response can be in the same line or the next line 233*19c8fe19SBram 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 243*19c8fe19SBram Moolenaar elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' 244*19c8fe19SBram 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 514b3307b5eSBram Moolenaar endif 515b3307b5eSBram Moolenaar endif 516b3307b5eSBram Moolenaar let result .= a:quotedText[i] 517b3307b5eSBram Moolenaar let i += 1 518b3307b5eSBram Moolenaar endwhile 519b3307b5eSBram Moolenaar return result 520b3307b5eSBram Moolenaarendfunc 521b3307b5eSBram Moolenaar 522a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 523a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5245378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5255378e1cfSBram Moolenaar return '' 5265378e1cfSBram Moolenaar endif 527a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 528a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 529a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 530a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 531a15b0a93SBram Moolenaar endif 532a15b0a93SBram Moolenaar return name 533a15b0a93SBram Moolenaarendfunc 534a15b0a93SBram Moolenaar 535b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 536fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 537b3623a38SBram Moolenaar unlet s:gdbwin 538e09ba7baSBram Moolenaar 539b3307b5eSBram Moolenaar call s:EndDebugCommon() 540b3307b5eSBram Moolenaarendfunc 541b3307b5eSBram Moolenaar 542b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 543e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 544e09ba7baSBram Moolenaar 545b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 546b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 547b3307b5eSBram Moolenaar endif 548b3307b5eSBram Moolenaar 549b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 550e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 551e09ba7baSBram Moolenaar call s:DeleteCommands() 552e09ba7baSBram Moolenaar 553e09ba7baSBram Moolenaar call win_gotoid(curwinid) 554b3307b5eSBram Moolenaar 55538baa3e6SBram Moolenaar if s:save_columns > 0 55638baa3e6SBram Moolenaar let &columns = s:save_columns 55738baa3e6SBram Moolenaar endif 5581b9645deSBram Moolenaar 559246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 560246fe03dSBram Moolenaar set balloonexpr= 56151b0f370SBram Moolenaar if has("balloon_eval") 56251b0f370SBram Moolenaar set noballooneval 563246fe03dSBram Moolenaar endif 56451b0f370SBram Moolenaar if has("balloon_eval_term") 56551b0f370SBram Moolenaar set noballoonevalterm 56651b0f370SBram Moolenaar endif 56751b0f370SBram Moolenaar endif 56851b0f370SBram Moolenaar 5691b9645deSBram Moolenaar au! TermDebug 570fe386641SBram Moolenaarendfunc 571fe386641SBram Moolenaar 572b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 573b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 574b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5754551c0a9SBram Moolenaar set nomodified 576b3307b5eSBram Moolenaar close 577b3307b5eSBram Moolenaar if curwinid != s:gdbwin 578b3307b5eSBram Moolenaar call win_gotoid(curwinid) 579b3307b5eSBram Moolenaar endif 580b3307b5eSBram Moolenaar 581b3307b5eSBram Moolenaar call s:EndDebugCommon() 582b3307b5eSBram Moolenaar unlet s:gdbwin 583b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 584b3307b5eSBram Moolenaarendfunc 585b3307b5eSBram Moolenaar 586fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 587fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 588fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 589fe386641SBram Moolenaar 590fe386641SBram Moolenaar for msg in msgs 591fe386641SBram Moolenaar " remove prefixed NL 592fe386641SBram Moolenaar if msg[0] == "\n" 593fe386641SBram Moolenaar let msg = msg[1:] 594fe386641SBram Moolenaar endif 595fe386641SBram Moolenaar if msg != '' 5961b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 597e09ba7baSBram Moolenaar call s:HandleCursor(msg) 59845d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 599e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 600e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 601e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 6024551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 6034551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 60445d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 60545d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 60645d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 60745d5f26dSBram Moolenaar call s:HandleError(msg) 608e09ba7baSBram Moolenaar endif 609e09ba7baSBram Moolenaar endif 610e09ba7baSBram Moolenaar endfor 611e09ba7baSBram Moolenaarendfunc 612e09ba7baSBram Moolenaar 613e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 614e09ba7baSBram Moolenaarfunc s:InstallCommands() 615963c1ad5SBram Moolenaar let save_cpo = &cpo 616963c1ad5SBram Moolenaar set cpo&vim 617963c1ad5SBram Moolenaar 618e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 61971137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 620e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 62145d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 622e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 62360e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 62460e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 62560e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 626b3307b5eSBram Moolenaar 627b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 628b3307b5eSBram Moolenaar if s:way == 'prompt' 629b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 630b3307b5eSBram Moolenaar else 631b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 632b3307b5eSBram Moolenaar endif 633b3307b5eSBram Moolenaar 63445d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 63545d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 63645d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 637b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 63871137fedSBram Moolenaar command Winbar call s:InstallWinbar() 63945d5f26dSBram Moolenaar 64045d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 64145d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 6421b9645deSBram Moolenaar 643f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 64471137fedSBram Moolenaar call s:InstallWinbar() 64571137fedSBram Moolenaar 64671137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 64771137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 64871137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 64971137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 65071137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 65171137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 65271137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 65371137fedSBram Moolenaar endif 65471137fedSBram Moolenaar endif 655963c1ad5SBram Moolenaar 656963c1ad5SBram Moolenaar let &cpo = save_cpo 65771137fedSBram Moolenaarendfunc 65871137fedSBram Moolenaar 65971137fedSBram Moolenaarlet s:winbar_winids = [] 66071137fedSBram Moolenaar 66171137fedSBram Moolenaar" Install the window toolbar in the current window. 66271137fedSBram Moolenaarfunc s:InstallWinbar() 663c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 66424a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 66524a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 66624a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 66724a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 66860e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 66924a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 67071137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 671c4b533e1SBram Moolenaar endif 672e09ba7baSBram Moolenaarendfunc 673e09ba7baSBram Moolenaar 674e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 675e09ba7baSBram Moolenaarfunc s:DeleteCommands() 676e09ba7baSBram Moolenaar delcommand Break 67771137fedSBram Moolenaar delcommand Clear 678e09ba7baSBram Moolenaar delcommand Step 67945d5f26dSBram Moolenaar delcommand Over 680e09ba7baSBram Moolenaar delcommand Finish 68160e73f2aSBram Moolenaar delcommand Run 68260e73f2aSBram Moolenaar delcommand Arguments 68360e73f2aSBram Moolenaar delcommand Stop 684e09ba7baSBram Moolenaar delcommand Continue 68545d5f26dSBram Moolenaar delcommand Evaluate 68645d5f26dSBram Moolenaar delcommand Gdb 68745d5f26dSBram Moolenaar delcommand Program 688b3623a38SBram Moolenaar delcommand Source 68971137fedSBram Moolenaar delcommand Winbar 69045d5f26dSBram Moolenaar 69145d5f26dSBram Moolenaar nunmap K 6921b9645deSBram Moolenaar 6931b9645deSBram Moolenaar if has('menu') 69471137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 69571137fedSBram Moolenaar let curwinid = win_getid(winnr()) 69671137fedSBram Moolenaar for winid in s:winbar_winids 69771137fedSBram Moolenaar if win_gotoid(winid) 6981b9645deSBram Moolenaar aunmenu WinBar.Step 6991b9645deSBram Moolenaar aunmenu WinBar.Next 7001b9645deSBram Moolenaar aunmenu WinBar.Finish 7011b9645deSBram Moolenaar aunmenu WinBar.Cont 70260e73f2aSBram Moolenaar aunmenu WinBar.Stop 7031b9645deSBram Moolenaar aunmenu WinBar.Eval 7041b9645deSBram Moolenaar endif 70571137fedSBram Moolenaar endfor 70671137fedSBram Moolenaar call win_gotoid(curwinid) 70771137fedSBram Moolenaar let s:winbar_winids = [] 70871137fedSBram Moolenaar 70971137fedSBram Moolenaar if exists('s:saved_mousemodel') 71071137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 71171137fedSBram Moolenaar unlet s:saved_mousemodel 71271137fedSBram Moolenaar aunmenu PopUp.-SEP3- 71371137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 71471137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 71571137fedSBram Moolenaar aunmenu PopUp.Evaluate 71671137fedSBram Moolenaar endif 71771137fedSBram Moolenaar endif 7181b9645deSBram Moolenaar 71945d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 72037402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 72137402ed5SBram Moolenaar for subid in keys(entries) 72237402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 72337402ed5SBram Moolenaar endfor 72445d5f26dSBram Moolenaar endfor 72545d5f26dSBram Moolenaar unlet s:breakpoints 72637402ed5SBram Moolenaar unlet s:breakpoint_locations 727a15b0a93SBram Moolenaar 728a15b0a93SBram Moolenaar sign undefine debugPC 729a15b0a93SBram Moolenaar for val in s:BreakpointSigns 730a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 731a15b0a93SBram Moolenaar endfor 7324551c0a9SBram Moolenaar let s:BreakpointSigns = [] 733e09ba7baSBram Moolenaarendfunc 734e09ba7baSBram Moolenaar 735e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 736e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 73760e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 73860e73f2aSBram Moolenaar " Interrupt to make it work. 73960e73f2aSBram Moolenaar let do_continue = 0 74060e73f2aSBram Moolenaar if !s:stopped 74160e73f2aSBram Moolenaar let do_continue = 1 742b3307b5eSBram Moolenaar if s:way == 'prompt' 7434551c0a9SBram Moolenaar call s:PromptInterrupt() 744b3307b5eSBram Moolenaar else 74560e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 746b3307b5eSBram Moolenaar endif 74760e73f2aSBram Moolenaar sleep 10m 74860e73f2aSBram Moolenaar endif 749a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 750a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 751a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 75260e73f2aSBram Moolenaar if do_continue 75360e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 75460e73f2aSBram Moolenaar endif 755e09ba7baSBram Moolenaarendfunc 756e09ba7baSBram Moolenaar 75771137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 75871137fedSBram Moolenaarfunc s:ClearBreakpoint() 759e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 760e09ba7baSBram Moolenaar let lnum = line('.') 76137402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 76237402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 76337402ed5SBram Moolenaar let idx = 0 76437402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 76537402ed5SBram Moolenaar if has_key(s:breakpoints, id) 76637402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 76737402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 76837402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 76937402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 77037402ed5SBram Moolenaar endfor 77137402ed5SBram Moolenaar unlet s:breakpoints[id] 77237402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 773e09ba7baSBram Moolenaar break 77437402ed5SBram Moolenaar else 77537402ed5SBram Moolenaar let idx += 1 776e09ba7baSBram Moolenaar endif 777e09ba7baSBram Moolenaar endfor 77837402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 77937402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 78037402ed5SBram Moolenaar endif 78137402ed5SBram Moolenaar endif 782e09ba7baSBram Moolenaarendfunc 783e09ba7baSBram Moolenaar 78460e73f2aSBram Moolenaarfunc s:Run(args) 78560e73f2aSBram Moolenaar if a:args != '' 78660e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 78760e73f2aSBram Moolenaar endif 78860e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 78960e73f2aSBram Moolenaarendfunc 79060e73f2aSBram Moolenaar 79151b0f370SBram Moolenaarfunc s:SendEval(expr) 79251b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 79351b0f370SBram Moolenaar let s:evalexpr = a:expr 79451b0f370SBram Moolenaarendfunc 79551b0f370SBram Moolenaar 79645d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 79745d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 79845d5f26dSBram Moolenaar if a:arg != '' 79945d5f26dSBram Moolenaar let expr = a:arg 80045d5f26dSBram Moolenaar elseif a:range == 2 80145d5f26dSBram Moolenaar let pos = getcurpos() 80245d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 80345d5f26dSBram Moolenaar let regt = getregtype('v') 80445d5f26dSBram Moolenaar normal! gv"vy 80545d5f26dSBram Moolenaar let expr = @v 80645d5f26dSBram Moolenaar call setpos('.', pos) 80745d5f26dSBram Moolenaar call setreg('v', reg, regt) 80845d5f26dSBram Moolenaar else 80945d5f26dSBram Moolenaar let expr = expand('<cexpr>') 81045d5f26dSBram Moolenaar endif 81122f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 81251b0f370SBram Moolenaar call s:SendEval(expr) 81345d5f26dSBram Moolenaarendfunc 81445d5f26dSBram Moolenaar 81522f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 81651b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 81751b0f370SBram Moolenaar 81845d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 81945d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 8201b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 8211b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 82251b0f370SBram Moolenaar if s:evalFromBalloonExpr 82351b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 82451b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 82551b0f370SBram Moolenaar else 82651b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 82751b0f370SBram Moolenaar endif 82851b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 82951b0f370SBram Moolenaar else 8301b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 83151b0f370SBram Moolenaar endif 8321b9645deSBram Moolenaar 8337f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 8341b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 83522f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 83651b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 83751b0f370SBram Moolenaar else 83851b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 8391b9645deSBram Moolenaar endif 84045d5f26dSBram Moolenaarendfunc 84145d5f26dSBram Moolenaar 84251b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 84351b0f370SBram Moolenaar" if there is any. 84451b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 845b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 846396e829fSBram Moolenaar return '' 847b3307b5eSBram Moolenaar endif 848b3307b5eSBram Moolenaar if !s:stopped 849b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 850b3307b5eSBram Moolenaar " mouse triggers a balloon. 851396e829fSBram Moolenaar return '' 85251b0f370SBram Moolenaar endif 85351b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 85451b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 85522f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 85622f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 85751b0f370SBram Moolenaar return '' 85851b0f370SBram Moolenaarendfunc 85951b0f370SBram Moolenaar 86045d5f26dSBram Moolenaar" Handle an error. 86145d5f26dSBram Moolenaarfunc s:HandleError(msg) 86222f1d0e3SBram Moolenaar if s:ignoreEvalError 86351b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 86422f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 86522f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 86651b0f370SBram Moolenaar return 86751b0f370SBram Moolenaar endif 86845d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 86945d5f26dSBram Moolenaarendfunc 87045d5f26dSBram Moolenaar 871b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 872b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 873c4b533e1SBram Moolenaar new 874b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 875c4b533e1SBram Moolenaar call s:InstallWinbar() 876c4b533e1SBram Moolenaar endif 877c4b533e1SBram Moolenaarendfunc 878c4b533e1SBram Moolenaar 879e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 880e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 881e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 882fe386641SBram Moolenaar let wid = win_getid(winnr()) 883fe386641SBram Moolenaar 88460e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8854551c0a9SBram Moolenaar call ch_log('program stopped') 88660e73f2aSBram Moolenaar let s:stopped = 1 88760e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8884551c0a9SBram Moolenaar call ch_log('program running') 88960e73f2aSBram Moolenaar let s:stopped = 0 89060e73f2aSBram Moolenaar endif 89160e73f2aSBram Moolenaar 892a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 893a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 894a15b0a93SBram Moolenaar else 895a15b0a93SBram Moolenaar let fname = '' 896a15b0a93SBram Moolenaar endif 8971b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 898e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 899fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 9004551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 9011b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 902fe386641SBram Moolenaar if &modified 903fe386641SBram Moolenaar " TODO: find existing window 904fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 905b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 906c4b533e1SBram Moolenaar call s:InstallWinbar() 907fe386641SBram Moolenaar else 908fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 909fe386641SBram Moolenaar endif 910fe386641SBram Moolenaar endif 911fe386641SBram Moolenaar exe lnum 91201164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 9131b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 914fe386641SBram Moolenaar setlocal signcolumn=yes 915fe386641SBram Moolenaar endif 9164551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 917fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 918fe386641SBram Moolenaar endif 919fe386641SBram Moolenaar 920fe386641SBram Moolenaar call win_gotoid(wid) 921e09ba7baSBram Moolenaarendfunc 922e09ba7baSBram Moolenaar 923de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 924a15b0a93SBram Moolenaar 92537402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 92637402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 92737402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 92837402ed5SBram Moolenaar call add(s:BreakpointSigns, nr) 92937402ed5SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 930de1a8314SBram Moolenaar endif 931de1a8314SBram Moolenaarendfunc 932de1a8314SBram Moolenaar 93337402ed5SBram Moolenaarfunc! s:SplitMsg(s) 93437402ed5SBram Moolenaar return split(a:s, '{.\{-}}\zs') 9355378e1cfSBram Moolenaarendfunction 9365378e1cfSBram Moolenaar 937e09ba7baSBram Moolenaar" Handle setting a breakpoint 938e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 939e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 9406dccc962SBram Moolenaar if a:msg !~ 'fullname=' 9416dccc962SBram Moolenaar " a watch does not have a file name 9426dccc962SBram Moolenaar return 9436dccc962SBram Moolenaar endif 9445378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 9455378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 9465378e1cfSBram Moolenaar if empty(fname) 9475378e1cfSBram Moolenaar continue 9485378e1cfSBram Moolenaar endif 9495378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 9505378e1cfSBram Moolenaar if empty(nr) 951e09ba7baSBram Moolenaar return 952fe386641SBram Moolenaar endif 953e09ba7baSBram Moolenaar 95437402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 95537402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 95637402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 95737402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 95837402ed5SBram Moolenaar 95937402ed5SBram Moolenaar if has_key(s:breakpoints, id) 96037402ed5SBram Moolenaar let entries = s:breakpoints[id] 96137402ed5SBram Moolenaar else 96237402ed5SBram Moolenaar let entries = {} 96337402ed5SBram Moolenaar let s:breakpoints[id] = entries 96437402ed5SBram Moolenaar endif 96537402ed5SBram Moolenaar if has_key(entries, subid) 96637402ed5SBram Moolenaar let entry = entries[subid] 967e09ba7baSBram Moolenaar else 968e09ba7baSBram Moolenaar let entry = {} 96937402ed5SBram Moolenaar let entries[subid] = entry 970fe386641SBram Moolenaar endif 971e09ba7baSBram Moolenaar 9725378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 973e09ba7baSBram Moolenaar let entry['fname'] = fname 974e09ba7baSBram Moolenaar let entry['lnum'] = lnum 9751b9645deSBram Moolenaar 97637402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 97737402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 97837402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 97937402ed5SBram Moolenaar endif 98037402ed5SBram Moolenaar let s:breakpoint_locations[bploc] += [id] 98137402ed5SBram Moolenaar 9821b9645deSBram Moolenaar if bufloaded(fname) 98337402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 9841b9645deSBram Moolenaar endif 9855378e1cfSBram Moolenaar endfor 9861b9645deSBram Moolenaarendfunc 9871b9645deSBram Moolenaar 98837402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry) 98937402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 99037402ed5SBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname'] 9911b9645deSBram Moolenaar let a:entry['placed'] = 1 992e09ba7baSBram Moolenaarendfunc 993e09ba7baSBram Moolenaar 994e09ba7baSBram Moolenaar" Handle deleting a breakpoint 995e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 996e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 99737402ed5SBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 99837402ed5SBram Moolenaar if empty(id) 999e09ba7baSBram Moolenaar return 1000e09ba7baSBram Moolenaar endif 100137402ed5SBram Moolenaar if has_key(s:breakpoints, id) 100237402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 10031b9645deSBram Moolenaar if has_key(entry, 'placed') 100437402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 10051b9645deSBram Moolenaar unlet entry['placed'] 10061b9645deSBram Moolenaar endif 10075378e1cfSBram Moolenaar endfor 100837402ed5SBram Moolenaar unlet s:breakpoints[id] 100937402ed5SBram Moolenaar endif 1010c572da5fSBram Moolenaarendfunc 10111b9645deSBram Moolenaar 10124551c0a9SBram Moolenaar" Handle the debugged program starting to run. 10134551c0a9SBram Moolenaar" Will store the process ID in s:pid 10144551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 10154551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 10164551c0a9SBram Moolenaar if nr == 0 10174551c0a9SBram Moolenaar return 10184551c0a9SBram Moolenaar endif 10194551c0a9SBram Moolenaar let s:pid = nr 10204551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 10214551c0a9SBram Moolenaarendfunc 10224551c0a9SBram Moolenaar 10231b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 10241b9645deSBram Moolenaarfunc s:BufRead() 10251b9645deSBram Moolenaar let fname = expand('<afile>:p') 102637402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 102737402ed5SBram Moolenaar for [subid, entry] in items(entries) 10281b9645deSBram Moolenaar if entry['fname'] == fname 102937402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10301b9645deSBram Moolenaar endif 10311b9645deSBram Moolenaar endfor 103237402ed5SBram Moolenaar endfor 10331b9645deSBram Moolenaarendfunc 10341b9645deSBram Moolenaar 10351b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 10361b9645deSBram Moolenaarfunc s:BufUnloaded() 10371b9645deSBram Moolenaar let fname = expand('<afile>:p') 103837402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 103937402ed5SBram Moolenaar for [subid, entry] in items(entries) 10401b9645deSBram Moolenaar if entry['fname'] == fname 10411b9645deSBram Moolenaar let entry['placed'] = 0 10421b9645deSBram Moolenaar endif 10431b9645deSBram Moolenaar endfor 104437402ed5SBram Moolenaar endfor 10451b9645deSBram Moolenaarendfunc 1046ca4cc018SBram Moolenaar 1047ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 1048ca4cc018SBram Moolenaarunlet s:keepcpo 1049