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) 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 241*06fe74aeSBram Moolenaar if response =~ 'Reading symbols from' && response !~ 'new-ui' 242*06fe74aeSBram Moolenaar " Reading symbols might take a while 243*06fe74aeSBram Moolenaar let try_count -= 1 244*06fe74aeSBram Moolenaar endif 2453e4b84d0SBram Moolenaar endif 2463e4b84d0SBram Moolenaar endfor 2473e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2483e4b84d0SBram Moolenaar break 2493e4b84d0SBram Moolenaar endif 2503e4b84d0SBram Moolenaar let try_count += 1 2513e4b84d0SBram Moolenaar if try_count > 100 2523e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2533e4b84d0SBram Moolenaar break 2543e4b84d0SBram Moolenaar endif 2553e4b84d0SBram Moolenaar sleep 10m 2563e4b84d0SBram Moolenaar endwhile 2573e4b84d0SBram Moolenaar 25860e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 25960e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 26060e73f2aSBram Moolenaar " running. 26160e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 262b3307b5eSBram Moolenaar " Older gdb uses a different command. 263b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 264e09ba7baSBram Moolenaar 265f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 266f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 267b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 268f3ba14ffSBram Moolenaar 269ef3c6a5bSBram Moolenaar call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')}) 270b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 271b3307b5eSBram Moolenaarendfunc 272b3307b5eSBram Moolenaar 273b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 274b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 275b3307b5eSBram Moolenaar if s:vertical 276b3307b5eSBram Moolenaar vertical new 277b3307b5eSBram Moolenaar else 278b3307b5eSBram Moolenaar new 279b3307b5eSBram Moolenaar endif 280b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 281b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 282b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 283b3307b5eSBram Moolenaar set buftype=prompt 284b3307b5eSBram Moolenaar file gdb 285b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 286b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 287b3307b5eSBram Moolenaar 288b3307b5eSBram Moolenaar if s:vertical 289b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 290b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 291b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 292b3307b5eSBram Moolenaar endif 293b3307b5eSBram Moolenaar 294b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 295b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 296b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 297b3307b5eSBram Moolenaar 298b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 299b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 300b3307b5eSBram Moolenaar 301b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 302b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 303b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 304b3307b5eSBram Moolenaar \ }) 305b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 306b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 307b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 308b3307b5eSBram Moolenaar return 309b3307b5eSBram Moolenaar endif 3104551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 3114551c0a9SBram Moolenaar set modified 312b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 313b3307b5eSBram Moolenaar 314b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 315b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 316b3307b5eSBram Moolenaar " target is running. 317b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 318b3307b5eSBram Moolenaar " Older gdb uses a different command. 319b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 320b3307b5eSBram Moolenaar 321b3307b5eSBram Moolenaar let s:ptybuf = 0 322b3307b5eSBram Moolenaar if has('win32') 323b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 324b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 325b3307b5eSBram Moolenaar elseif has('terminal') 326b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 327b3307b5eSBram Moolenaar " gdb window. 328b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 329b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 330b3307b5eSBram Moolenaar \ }) 331b3307b5eSBram Moolenaar if s:ptybuf == 0 332b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 333b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 334b3307b5eSBram Moolenaar return 335b3307b5eSBram Moolenaar endif 336b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 337b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 338b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 339b3307b5eSBram Moolenaar 340b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 341b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 342b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 343b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 344b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 345b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 346b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 347b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 348b3307b5eSBram Moolenaar else 349b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 350b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 351b3307b5eSBram Moolenaar endif 352b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 353b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 354b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 355b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 356b3307b5eSBram Moolenaar 357b3307b5eSBram Moolenaar " Set arguments to be run 358b3307b5eSBram Moolenaar if len(proc_args) 359b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 360b3307b5eSBram Moolenaar endif 361b3307b5eSBram Moolenaar 362b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 363b3307b5eSBram Moolenaar startinsert 364b3307b5eSBram Moolenaarendfunc 365b3307b5eSBram Moolenaar 366b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 36738baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 36838baa3e6SBram Moolenaar " There can be only one. 36938baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 37038baa3e6SBram Moolenaar 37145d5f26dSBram Moolenaar " Install debugger commands in the text window. 372b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 373e09ba7baSBram Moolenaar call s:InstallCommands() 37445d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 375e09ba7baSBram Moolenaar 37651b0f370SBram Moolenaar " Enable showing a balloon with eval info 377246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 378246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 37951b0f370SBram Moolenaar if has("balloon_eval") 38051b0f370SBram Moolenaar set ballooneval 381246fe03dSBram Moolenaar endif 38251b0f370SBram Moolenaar if has("balloon_eval_term") 38351b0f370SBram Moolenaar set balloonevalterm 38451b0f370SBram Moolenaar endif 38551b0f370SBram Moolenaar endif 38651b0f370SBram Moolenaar 3875378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 3885378e1cfSBram Moolenaar " breakpoint number. 38937402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 39037402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 39137402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 39237402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 39337402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 394e09ba7baSBram Moolenaar let s:breakpoints = {} 3951b9645deSBram Moolenaar 39637402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 39737402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 39837402ed5SBram Moolenaar let s:breakpoint_locations = {} 39937402ed5SBram Moolenaar 4001b9645deSBram Moolenaar augroup TermDebug 4011b9645deSBram Moolenaar au BufRead * call s:BufRead() 4021b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 403f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 4041b9645deSBram Moolenaar augroup END 40532c67ba7SBram Moolenaar 406b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 407b3307b5eSBram Moolenaar " window. 40832c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 40932c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 41032c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 41132c67ba7SBram Moolenaar endif 412c572da5fSBram Moolenaarendfunc 413c572da5fSBram Moolenaar 414b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 415b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 416b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 417b3307b5eSBram Moolenaar if s:way == 'prompt' 418b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 419b3307b5eSBram Moolenaar else 420b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 421b3307b5eSBram Moolenaar endif 422b3307b5eSBram Moolenaarendfunc 423b3307b5eSBram Moolenaar 424b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 425b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 426b3307b5eSBram Moolenaar if s:way == 'prompt' 427b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 428b3307b5eSBram Moolenaar else 429b3307b5eSBram Moolenaar let do_continue = 0 430b3307b5eSBram Moolenaar if !s:stopped 431b3307b5eSBram Moolenaar let do_continue = 1 432b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 433b3307b5eSBram Moolenaar sleep 10m 434b3307b5eSBram Moolenaar endif 435b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 436b3307b5eSBram Moolenaar if do_continue 437b3307b5eSBram Moolenaar Continue 438b3307b5eSBram Moolenaar endif 439b3307b5eSBram Moolenaar endif 440b3307b5eSBram Moolenaarendfunc 441b3307b5eSBram Moolenaar 442b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 443b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 444b3307b5eSBram Moolenaar call s:SendCommand(a:text) 445b3307b5eSBram Moolenaarendfunc 446b3307b5eSBram Moolenaar 4474551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4484551c0a9SBram Moolenaar" breakpoint. 449b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4502ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4512ed890f1SBram Moolenaar if has('win32') 4522ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4532ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4544551c0a9SBram Moolenaar if s:pid == 0 4554551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4564551c0a9SBram Moolenaar else 4574551c0a9SBram Moolenaar call debugbreak(s:pid) 4584551c0a9SBram Moolenaar endif 4592ed890f1SBram Moolenaar else 4602ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4612ed890f1SBram Moolenaar endif 462b3307b5eSBram Moolenaarendfunc 463b3307b5eSBram Moolenaar 464b3307b5eSBram Moolenaar" Function called when gdb outputs text. 465b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 466b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 467b3307b5eSBram Moolenaar 468b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 469b3307b5eSBram Moolenaar " Drop status and echo'd commands. 470a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 471b3307b5eSBram Moolenaar return 472b3307b5eSBram Moolenaar endif 473b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 474b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 475b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 476b3307b5eSBram Moolenaar " Silently drop evaluation errors. 477b3307b5eSBram Moolenaar unlet s:evalexpr 478b3307b5eSBram Moolenaar return 479b3307b5eSBram Moolenaar endif 480b3307b5eSBram Moolenaar elseif a:text[0] == '~' 481b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 482b3307b5eSBram Moolenaar else 483b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 484b3307b5eSBram Moolenaar return 485b3307b5eSBram Moolenaar endif 486b3307b5eSBram Moolenaar 487b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 488b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 489b3307b5eSBram Moolenaar 490b3307b5eSBram Moolenaar " Add the output above the current prompt. 491b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4924551c0a9SBram Moolenaar set modified 493b3307b5eSBram Moolenaar 494b3307b5eSBram Moolenaar call win_gotoid(curwinid) 495b3307b5eSBram Moolenaarendfunc 496b3307b5eSBram Moolenaar 497b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 498b3307b5eSBram Moolenaar" to the next ", unescaping characters. 499b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 500b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 501a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 502b3307b5eSBram Moolenaar return 503b3307b5eSBram Moolenaar endif 504b3307b5eSBram Moolenaar let result = '' 505b3307b5eSBram Moolenaar let i = 1 506b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 507b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 508b3307b5eSBram Moolenaar let i += 1 509b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 510b3307b5eSBram Moolenaar " drop \n 511b3307b5eSBram Moolenaar let i += 1 512b3307b5eSBram Moolenaar continue 513b3307b5eSBram Moolenaar endif 514b3307b5eSBram Moolenaar endif 515b3307b5eSBram Moolenaar let result .= a:quotedText[i] 516b3307b5eSBram Moolenaar let i += 1 517b3307b5eSBram Moolenaar endwhile 518b3307b5eSBram Moolenaar return result 519b3307b5eSBram Moolenaarendfunc 520b3307b5eSBram Moolenaar 521a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 522a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5235378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5245378e1cfSBram Moolenaar return '' 5255378e1cfSBram Moolenaar endif 526a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 527a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 528a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 529a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 530a15b0a93SBram Moolenaar endif 531a15b0a93SBram Moolenaar return name 532a15b0a93SBram Moolenaarendfunc 533a15b0a93SBram Moolenaar 534b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 535fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 536b3623a38SBram Moolenaar unlet s:gdbwin 537e09ba7baSBram Moolenaar 538b3307b5eSBram Moolenaar call s:EndDebugCommon() 539b3307b5eSBram Moolenaarendfunc 540b3307b5eSBram Moolenaar 541b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 542e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 543e09ba7baSBram Moolenaar 544b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 545b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 546b3307b5eSBram Moolenaar endif 547b3307b5eSBram Moolenaar 548b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 549e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 550e09ba7baSBram Moolenaar call s:DeleteCommands() 551e09ba7baSBram Moolenaar 552e09ba7baSBram Moolenaar call win_gotoid(curwinid) 553b3307b5eSBram Moolenaar 55438baa3e6SBram Moolenaar if s:save_columns > 0 55538baa3e6SBram Moolenaar let &columns = s:save_columns 55638baa3e6SBram Moolenaar endif 5571b9645deSBram Moolenaar 558246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 559246fe03dSBram Moolenaar set balloonexpr= 56051b0f370SBram Moolenaar if has("balloon_eval") 56151b0f370SBram Moolenaar set noballooneval 562246fe03dSBram Moolenaar endif 56351b0f370SBram Moolenaar if has("balloon_eval_term") 56451b0f370SBram Moolenaar set noballoonevalterm 56551b0f370SBram Moolenaar endif 56651b0f370SBram Moolenaar endif 56751b0f370SBram Moolenaar 5681b9645deSBram Moolenaar au! TermDebug 569fe386641SBram Moolenaarendfunc 570fe386641SBram Moolenaar 571b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 572b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 573b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5744551c0a9SBram Moolenaar set nomodified 575b3307b5eSBram Moolenaar close 576b3307b5eSBram Moolenaar if curwinid != s:gdbwin 577b3307b5eSBram Moolenaar call win_gotoid(curwinid) 578b3307b5eSBram Moolenaar endif 579b3307b5eSBram Moolenaar 580b3307b5eSBram Moolenaar call s:EndDebugCommon() 581b3307b5eSBram Moolenaar unlet s:gdbwin 582b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 583b3307b5eSBram Moolenaarendfunc 584b3307b5eSBram Moolenaar 585fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 586fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 587fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 588fe386641SBram Moolenaar 589fe386641SBram Moolenaar for msg in msgs 590fe386641SBram Moolenaar " remove prefixed NL 591fe386641SBram Moolenaar if msg[0] == "\n" 592fe386641SBram Moolenaar let msg = msg[1:] 593fe386641SBram Moolenaar endif 594fe386641SBram Moolenaar if msg != '' 5951b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 596e09ba7baSBram Moolenaar call s:HandleCursor(msg) 59745d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 598e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 599e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 600e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 6014551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 6024551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 60345d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 60445d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 60545d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 60645d5f26dSBram Moolenaar call s:HandleError(msg) 607e09ba7baSBram Moolenaar endif 608e09ba7baSBram Moolenaar endif 609e09ba7baSBram Moolenaar endfor 610e09ba7baSBram Moolenaarendfunc 611e09ba7baSBram Moolenaar 612e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 613e09ba7baSBram Moolenaarfunc s:InstallCommands() 614963c1ad5SBram Moolenaar let save_cpo = &cpo 615963c1ad5SBram Moolenaar set cpo&vim 616963c1ad5SBram Moolenaar 617e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 61871137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 619e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 62045d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 621e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 62260e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 62360e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 62460e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 625b3307b5eSBram Moolenaar 626b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 627b3307b5eSBram Moolenaar if s:way == 'prompt' 628b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 629b3307b5eSBram Moolenaar else 630b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 631b3307b5eSBram Moolenaar endif 632b3307b5eSBram Moolenaar 63345d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 63445d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 63545d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 636b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 63771137fedSBram Moolenaar command Winbar call s:InstallWinbar() 63845d5f26dSBram Moolenaar 63945d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 64045d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 6411b9645deSBram Moolenaar 642f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 64371137fedSBram Moolenaar call s:InstallWinbar() 64471137fedSBram Moolenaar 64571137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 64671137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 64771137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 64871137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 64971137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 65071137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 65171137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 65271137fedSBram Moolenaar endif 65371137fedSBram Moolenaar endif 654963c1ad5SBram Moolenaar 655963c1ad5SBram Moolenaar let &cpo = save_cpo 65671137fedSBram Moolenaarendfunc 65771137fedSBram Moolenaar 65871137fedSBram Moolenaarlet s:winbar_winids = [] 65971137fedSBram Moolenaar 66071137fedSBram Moolenaar" Install the window toolbar in the current window. 66171137fedSBram Moolenaarfunc s:InstallWinbar() 662c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 66324a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 66424a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 66524a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 66624a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 66760e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 66824a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 66971137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 670c4b533e1SBram Moolenaar endif 671e09ba7baSBram Moolenaarendfunc 672e09ba7baSBram Moolenaar 673e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 674e09ba7baSBram Moolenaarfunc s:DeleteCommands() 675e09ba7baSBram Moolenaar delcommand Break 67671137fedSBram Moolenaar delcommand Clear 677e09ba7baSBram Moolenaar delcommand Step 67845d5f26dSBram Moolenaar delcommand Over 679e09ba7baSBram Moolenaar delcommand Finish 68060e73f2aSBram Moolenaar delcommand Run 68160e73f2aSBram Moolenaar delcommand Arguments 68260e73f2aSBram Moolenaar delcommand Stop 683e09ba7baSBram Moolenaar delcommand Continue 68445d5f26dSBram Moolenaar delcommand Evaluate 68545d5f26dSBram Moolenaar delcommand Gdb 68645d5f26dSBram Moolenaar delcommand Program 687b3623a38SBram Moolenaar delcommand Source 68871137fedSBram Moolenaar delcommand Winbar 68945d5f26dSBram Moolenaar 69045d5f26dSBram Moolenaar nunmap K 6911b9645deSBram Moolenaar 6921b9645deSBram Moolenaar if has('menu') 69371137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 69471137fedSBram Moolenaar let curwinid = win_getid(winnr()) 69571137fedSBram Moolenaar for winid in s:winbar_winids 69671137fedSBram Moolenaar if win_gotoid(winid) 6971b9645deSBram Moolenaar aunmenu WinBar.Step 6981b9645deSBram Moolenaar aunmenu WinBar.Next 6991b9645deSBram Moolenaar aunmenu WinBar.Finish 7001b9645deSBram Moolenaar aunmenu WinBar.Cont 70160e73f2aSBram Moolenaar aunmenu WinBar.Stop 7021b9645deSBram Moolenaar aunmenu WinBar.Eval 7031b9645deSBram Moolenaar endif 70471137fedSBram Moolenaar endfor 70571137fedSBram Moolenaar call win_gotoid(curwinid) 70671137fedSBram Moolenaar let s:winbar_winids = [] 70771137fedSBram Moolenaar 70871137fedSBram Moolenaar if exists('s:saved_mousemodel') 70971137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 71071137fedSBram Moolenaar unlet s:saved_mousemodel 71171137fedSBram Moolenaar aunmenu PopUp.-SEP3- 71271137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 71371137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 71471137fedSBram Moolenaar aunmenu PopUp.Evaluate 71571137fedSBram Moolenaar endif 71671137fedSBram Moolenaar endif 7171b9645deSBram Moolenaar 71845d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 71937402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 72037402ed5SBram Moolenaar for subid in keys(entries) 72137402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 72237402ed5SBram Moolenaar endfor 72345d5f26dSBram Moolenaar endfor 72445d5f26dSBram Moolenaar unlet s:breakpoints 72537402ed5SBram Moolenaar unlet s:breakpoint_locations 726a15b0a93SBram Moolenaar 727a15b0a93SBram Moolenaar sign undefine debugPC 728a15b0a93SBram Moolenaar for val in s:BreakpointSigns 729a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 730a15b0a93SBram Moolenaar endfor 7314551c0a9SBram Moolenaar let s:BreakpointSigns = [] 732e09ba7baSBram Moolenaarendfunc 733e09ba7baSBram Moolenaar 734e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 735e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 73660e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 73760e73f2aSBram Moolenaar " Interrupt to make it work. 73860e73f2aSBram Moolenaar let do_continue = 0 73960e73f2aSBram Moolenaar if !s:stopped 74060e73f2aSBram Moolenaar let do_continue = 1 741b3307b5eSBram Moolenaar if s:way == 'prompt' 7424551c0a9SBram Moolenaar call s:PromptInterrupt() 743b3307b5eSBram Moolenaar else 74460e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 745b3307b5eSBram Moolenaar endif 74660e73f2aSBram Moolenaar sleep 10m 74760e73f2aSBram Moolenaar endif 748a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 749a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 750a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 75160e73f2aSBram Moolenaar if do_continue 75260e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 75360e73f2aSBram Moolenaar endif 754e09ba7baSBram Moolenaarendfunc 755e09ba7baSBram Moolenaar 75671137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 75771137fedSBram Moolenaarfunc s:ClearBreakpoint() 758e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 759e09ba7baSBram Moolenaar let lnum = line('.') 76037402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 76137402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 76237402ed5SBram Moolenaar let idx = 0 76337402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 76437402ed5SBram Moolenaar if has_key(s:breakpoints, id) 76537402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 76637402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 76737402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 76837402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 76937402ed5SBram Moolenaar endfor 77037402ed5SBram Moolenaar unlet s:breakpoints[id] 77137402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 772e09ba7baSBram Moolenaar break 77337402ed5SBram Moolenaar else 77437402ed5SBram Moolenaar let idx += 1 775e09ba7baSBram Moolenaar endif 776e09ba7baSBram Moolenaar endfor 77737402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 77837402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 77937402ed5SBram Moolenaar endif 78037402ed5SBram Moolenaar endif 781e09ba7baSBram Moolenaarendfunc 782e09ba7baSBram Moolenaar 78360e73f2aSBram Moolenaarfunc s:Run(args) 78460e73f2aSBram Moolenaar if a:args != '' 78560e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 78660e73f2aSBram Moolenaar endif 78760e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 78860e73f2aSBram Moolenaarendfunc 78960e73f2aSBram Moolenaar 79051b0f370SBram Moolenaarfunc s:SendEval(expr) 79151b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 79251b0f370SBram Moolenaar let s:evalexpr = a:expr 79351b0f370SBram Moolenaarendfunc 79451b0f370SBram Moolenaar 79545d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 79645d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 79745d5f26dSBram Moolenaar if a:arg != '' 79845d5f26dSBram Moolenaar let expr = a:arg 79945d5f26dSBram Moolenaar elseif a:range == 2 80045d5f26dSBram Moolenaar let pos = getcurpos() 80145d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 80245d5f26dSBram Moolenaar let regt = getregtype('v') 80345d5f26dSBram Moolenaar normal! gv"vy 80445d5f26dSBram Moolenaar let expr = @v 80545d5f26dSBram Moolenaar call setpos('.', pos) 80645d5f26dSBram Moolenaar call setreg('v', reg, regt) 80745d5f26dSBram Moolenaar else 80845d5f26dSBram Moolenaar let expr = expand('<cexpr>') 80945d5f26dSBram Moolenaar endif 81022f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 81151b0f370SBram Moolenaar call s:SendEval(expr) 81245d5f26dSBram Moolenaarendfunc 81345d5f26dSBram Moolenaar 81422f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 81551b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 81651b0f370SBram Moolenaar 81745d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 81845d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 8191b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 8201b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 82151b0f370SBram Moolenaar if s:evalFromBalloonExpr 82251b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 82351b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 82451b0f370SBram Moolenaar else 82551b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 82651b0f370SBram Moolenaar endif 82751b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 82851b0f370SBram Moolenaar else 8291b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 83051b0f370SBram Moolenaar endif 8311b9645deSBram Moolenaar 8327f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 8331b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 83422f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 83551b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 83651b0f370SBram Moolenaar else 83751b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 8381b9645deSBram Moolenaar endif 83945d5f26dSBram Moolenaarendfunc 84045d5f26dSBram Moolenaar 84151b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 84251b0f370SBram Moolenaar" if there is any. 84351b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 844b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 845396e829fSBram Moolenaar return '' 846b3307b5eSBram Moolenaar endif 847b3307b5eSBram Moolenaar if !s:stopped 848b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 849b3307b5eSBram Moolenaar " mouse triggers a balloon. 850396e829fSBram Moolenaar return '' 85151b0f370SBram Moolenaar endif 85251b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 85351b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 85422f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 85522f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 85651b0f370SBram Moolenaar return '' 85751b0f370SBram Moolenaarendfunc 85851b0f370SBram Moolenaar 85945d5f26dSBram Moolenaar" Handle an error. 86045d5f26dSBram Moolenaarfunc s:HandleError(msg) 86122f1d0e3SBram Moolenaar if s:ignoreEvalError 86251b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 86322f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 86422f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 86551b0f370SBram Moolenaar return 86651b0f370SBram Moolenaar endif 86745d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 86845d5f26dSBram Moolenaarendfunc 86945d5f26dSBram Moolenaar 870b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 871b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 872c4b533e1SBram Moolenaar new 873b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 874c4b533e1SBram Moolenaar call s:InstallWinbar() 875c4b533e1SBram Moolenaar endif 876c4b533e1SBram Moolenaarendfunc 877c4b533e1SBram Moolenaar 878e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 879e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 880e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 881fe386641SBram Moolenaar let wid = win_getid(winnr()) 882fe386641SBram Moolenaar 88360e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8844551c0a9SBram Moolenaar call ch_log('program stopped') 88560e73f2aSBram Moolenaar let s:stopped = 1 88660e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8874551c0a9SBram Moolenaar call ch_log('program running') 88860e73f2aSBram Moolenaar let s:stopped = 0 88960e73f2aSBram Moolenaar endif 89060e73f2aSBram Moolenaar 891a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 892a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 893a15b0a93SBram Moolenaar else 894a15b0a93SBram Moolenaar let fname = '' 895a15b0a93SBram Moolenaar endif 8961b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 897e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 898fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8994551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 9001b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 901fe386641SBram Moolenaar if &modified 902fe386641SBram Moolenaar " TODO: find existing window 903fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 904b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 905c4b533e1SBram Moolenaar call s:InstallWinbar() 906fe386641SBram Moolenaar else 907fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 908fe386641SBram Moolenaar endif 909fe386641SBram Moolenaar endif 910fe386641SBram Moolenaar exe lnum 91101164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 9121b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 913fe386641SBram Moolenaar setlocal signcolumn=yes 914fe386641SBram Moolenaar endif 9154551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 916fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 917fe386641SBram Moolenaar endif 918fe386641SBram Moolenaar 919fe386641SBram Moolenaar call win_gotoid(wid) 920e09ba7baSBram Moolenaarendfunc 921e09ba7baSBram Moolenaar 922de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 923a15b0a93SBram Moolenaar 92437402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 92537402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 92637402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 92737402ed5SBram Moolenaar call add(s:BreakpointSigns, nr) 92837402ed5SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 929de1a8314SBram Moolenaar endif 930de1a8314SBram Moolenaarendfunc 931de1a8314SBram Moolenaar 93237402ed5SBram Moolenaarfunc! s:SplitMsg(s) 93337402ed5SBram Moolenaar return split(a:s, '{.\{-}}\zs') 9345378e1cfSBram Moolenaarendfunction 9355378e1cfSBram Moolenaar 936e09ba7baSBram Moolenaar" Handle setting a breakpoint 937e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 938e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 9396dccc962SBram Moolenaar if a:msg !~ 'fullname=' 9406dccc962SBram Moolenaar " a watch does not have a file name 9416dccc962SBram Moolenaar return 9426dccc962SBram Moolenaar endif 9435378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 9445378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 9455378e1cfSBram Moolenaar if empty(fname) 9465378e1cfSBram Moolenaar continue 9475378e1cfSBram Moolenaar endif 9485378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 9495378e1cfSBram Moolenaar if empty(nr) 950e09ba7baSBram Moolenaar return 951fe386641SBram Moolenaar endif 952e09ba7baSBram Moolenaar 95337402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 95437402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 95537402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 95637402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 95737402ed5SBram Moolenaar 95837402ed5SBram Moolenaar if has_key(s:breakpoints, id) 95937402ed5SBram Moolenaar let entries = s:breakpoints[id] 96037402ed5SBram Moolenaar else 96137402ed5SBram Moolenaar let entries = {} 96237402ed5SBram Moolenaar let s:breakpoints[id] = entries 96337402ed5SBram Moolenaar endif 96437402ed5SBram Moolenaar if has_key(entries, subid) 96537402ed5SBram Moolenaar let entry = entries[subid] 966e09ba7baSBram Moolenaar else 967e09ba7baSBram Moolenaar let entry = {} 96837402ed5SBram Moolenaar let entries[subid] = entry 969fe386641SBram Moolenaar endif 970e09ba7baSBram Moolenaar 9715378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 972e09ba7baSBram Moolenaar let entry['fname'] = fname 973e09ba7baSBram Moolenaar let entry['lnum'] = lnum 9741b9645deSBram Moolenaar 97537402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 97637402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 97737402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 97837402ed5SBram Moolenaar endif 97937402ed5SBram Moolenaar let s:breakpoint_locations[bploc] += [id] 98037402ed5SBram Moolenaar 9811b9645deSBram Moolenaar if bufloaded(fname) 98237402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 9831b9645deSBram Moolenaar endif 9845378e1cfSBram Moolenaar endfor 9851b9645deSBram Moolenaarendfunc 9861b9645deSBram Moolenaar 98737402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry) 98837402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 98937402ed5SBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname'] 9901b9645deSBram Moolenaar let a:entry['placed'] = 1 991e09ba7baSBram Moolenaarendfunc 992e09ba7baSBram Moolenaar 993e09ba7baSBram Moolenaar" Handle deleting a breakpoint 994e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 995e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 99637402ed5SBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 99737402ed5SBram Moolenaar if empty(id) 998e09ba7baSBram Moolenaar return 999e09ba7baSBram Moolenaar endif 100037402ed5SBram Moolenaar if has_key(s:breakpoints, id) 100137402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 10021b9645deSBram Moolenaar if has_key(entry, 'placed') 100337402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 10041b9645deSBram Moolenaar unlet entry['placed'] 10051b9645deSBram Moolenaar endif 10065378e1cfSBram Moolenaar endfor 100737402ed5SBram Moolenaar unlet s:breakpoints[id] 100837402ed5SBram Moolenaar endif 1009c572da5fSBram Moolenaarendfunc 10101b9645deSBram Moolenaar 10114551c0a9SBram Moolenaar" Handle the debugged program starting to run. 10124551c0a9SBram Moolenaar" Will store the process ID in s:pid 10134551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 10144551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 10154551c0a9SBram Moolenaar if nr == 0 10164551c0a9SBram Moolenaar return 10174551c0a9SBram Moolenaar endif 10184551c0a9SBram Moolenaar let s:pid = nr 10194551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 10204551c0a9SBram Moolenaarendfunc 10214551c0a9SBram Moolenaar 10221b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 10231b9645deSBram Moolenaarfunc s:BufRead() 10241b9645deSBram Moolenaar let fname = expand('<afile>:p') 102537402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 102637402ed5SBram Moolenaar for [subid, entry] in items(entries) 10271b9645deSBram Moolenaar if entry['fname'] == fname 102837402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10291b9645deSBram Moolenaar endif 10301b9645deSBram Moolenaar endfor 103137402ed5SBram Moolenaar endfor 10321b9645deSBram Moolenaarendfunc 10331b9645deSBram Moolenaar 10341b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 10351b9645deSBram Moolenaarfunc s:BufUnloaded() 10361b9645deSBram Moolenaar let fname = expand('<afile>:p') 103737402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 103837402ed5SBram Moolenaar for [subid, entry] in items(entries) 10391b9645deSBram Moolenaar if entry['fname'] == fname 10401b9645deSBram Moolenaar let entry['placed'] = 0 10411b9645deSBram Moolenaar endif 10421b9645deSBram Moolenaar endfor 104337402ed5SBram Moolenaar endfor 10441b9645deSBram Moolenaarendfunc 1045ca4cc018SBram Moolenaar 1046ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 1047ca4cc018SBram Moolenaarunlet s:keepcpo 1048