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 12024a98a0eSBram Moolenaar if exists('g:termdebug_wide') 12124a98a0eSBram Moolenaar if &columns < g:termdebug_wide 12238baa3e6SBram Moolenaar let s:save_columns = &columns 12338baa3e6SBram Moolenaar let &columns = g:termdebug_wide 12424a98a0eSBram Moolenaar endif 125b3307b5eSBram Moolenaar let s:vertical = 1 12638baa3e6SBram Moolenaar else 127b3307b5eSBram Moolenaar let s:vertical = 0 12838baa3e6SBram Moolenaar endif 12938baa3e6SBram Moolenaar 130b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 131b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 132b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 133b3307b5eSBram Moolenaar let s:way = 'terminal' 134b3307b5eSBram Moolenaar else 135b3307b5eSBram Moolenaar let s:way = 'prompt' 136b3307b5eSBram Moolenaar endif 137b3307b5eSBram Moolenaar 138b3307b5eSBram Moolenaar if s:way == 'prompt' 139b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 140b3307b5eSBram Moolenaar else 141b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 142b3307b5eSBram Moolenaar endif 143b3307b5eSBram Moolenaarendfunc 144b3307b5eSBram Moolenaar 145*ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended. 146*ef3c6a5bSBram Moolenaarfunc s:CloseBuffers() 147*ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:ptybuf 148*ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:commbuf 149*ef3c6a5bSBram Moolenaar unlet! s:gdbwin 150*ef3c6a5bSBram Moolenaarendfunc 151*ef3c6a5bSBram Moolenaar 152b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 153b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 154fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 155b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 156b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 157fe386641SBram Moolenaar \ }) 158fe386641SBram Moolenaar if s:ptybuf == 0 159fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 160fe386641SBram Moolenaar return 161fe386641SBram Moolenaar endif 162fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 16345d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 164b3307b5eSBram Moolenaar if s:vertical 16551b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 16651b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 16751b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 16851b0f370SBram Moolenaar endif 169fe386641SBram Moolenaar 170fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 171fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 172fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 173fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 174fe386641SBram Moolenaar \ 'hidden': 1, 175fe386641SBram Moolenaar \ }) 176fe386641SBram Moolenaar if s:commbuf == 0 177fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 178fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 179fe386641SBram Moolenaar return 180fe386641SBram Moolenaar endif 181fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 182c572da5fSBram Moolenaar 183c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 184c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 18532c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 18632c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 18732c67ba7SBram Moolenaar 18832c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 189b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 19060e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 191fe386641SBram Moolenaar \ 'term_finish': 'close', 192c572da5fSBram Moolenaar \ }) 19360e73f2aSBram Moolenaar if s:gdbbuf == 0 194fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 195*ef3c6a5bSBram Moolenaar call s:CloseBuffers() 196fe386641SBram Moolenaar return 197fe386641SBram Moolenaar endif 19845d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 199fe386641SBram Moolenaar 20032c67ba7SBram Moolenaar " Set arguments to be run 20132c67ba7SBram Moolenaar if len(proc_args) 20232c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 20332c67ba7SBram Moolenaar endif 20432c67ba7SBram Moolenaar 205fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 20660e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 20760e73f2aSBram Moolenaar 2083e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 2093e4b84d0SBram Moolenaar " why the debugger doesn't work. 2103e4b84d0SBram Moolenaar let try_count = 0 2113e4b84d0SBram Moolenaar while 1 212*ef3c6a5bSBram Moolenaar let gdbproc = term_getjob(s:gdbbuf) 213*ef3c6a5bSBram Moolenaar if gdbproc == v:null || job_status(gdbproc) !=# 'run' 214*ef3c6a5bSBram Moolenaar echoerr string(g:termdebugger) . ' exited unexpectedly' 215*ef3c6a5bSBram Moolenaar call s:CloseBuffers() 216*ef3c6a5bSBram Moolenaar return 217*ef3c6a5bSBram Moolenaar endif 218*ef3c6a5bSBram Moolenaar 2193e4b84d0SBram Moolenaar let response = '' 220b3623a38SBram Moolenaar for lnum in range(1,200) 2213e4b84d0SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 222f63db65bSBram Moolenaar " response can be in the same line or the next line 223f63db65bSBram Moolenaar let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1) 2243e4b84d0SBram Moolenaar if response =~ 'Undefined command' 225f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 226*ef3c6a5bSBram Moolenaar call s:CloseBuffers() 2273e4b84d0SBram Moolenaar return 2283e4b84d0SBram Moolenaar endif 2293e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2303e4b84d0SBram Moolenaar " Success! 2313e4b84d0SBram Moolenaar break 2323e4b84d0SBram Moolenaar endif 2333e4b84d0SBram Moolenaar endif 2343e4b84d0SBram Moolenaar endfor 2353e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2363e4b84d0SBram Moolenaar break 2373e4b84d0SBram Moolenaar endif 2383e4b84d0SBram Moolenaar let try_count += 1 2393e4b84d0SBram Moolenaar if try_count > 100 2403e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2413e4b84d0SBram Moolenaar break 2423e4b84d0SBram Moolenaar endif 2433e4b84d0SBram Moolenaar sleep 10m 2443e4b84d0SBram Moolenaar endwhile 2453e4b84d0SBram Moolenaar 24660e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 24760e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 24860e73f2aSBram Moolenaar " running. 24960e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 250b3307b5eSBram Moolenaar " Older gdb uses a different command. 251b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 252e09ba7baSBram Moolenaar 253f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 254f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 255b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 256f3ba14ffSBram Moolenaar 257*ef3c6a5bSBram Moolenaar call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')}) 258b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 259b3307b5eSBram Moolenaarendfunc 260b3307b5eSBram Moolenaar 261b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 262b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 263b3307b5eSBram Moolenaar if s:vertical 264b3307b5eSBram Moolenaar vertical new 265b3307b5eSBram Moolenaar else 266b3307b5eSBram Moolenaar new 267b3307b5eSBram Moolenaar endif 268b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 269b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 270b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 271b3307b5eSBram Moolenaar set buftype=prompt 272b3307b5eSBram Moolenaar file gdb 273b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 274b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 275b3307b5eSBram Moolenaar 276b3307b5eSBram Moolenaar if s:vertical 277b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 278b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 279b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 280b3307b5eSBram Moolenaar endif 281b3307b5eSBram Moolenaar 282b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 283b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 284b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 285b3307b5eSBram Moolenaar 286b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 287b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 288b3307b5eSBram Moolenaar 289b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 290b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 291b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 292b3307b5eSBram Moolenaar \ }) 293b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 294b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 295b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 296b3307b5eSBram Moolenaar return 297b3307b5eSBram Moolenaar endif 2984551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 2994551c0a9SBram Moolenaar set modified 300b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 301b3307b5eSBram Moolenaar 302b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 303b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 304b3307b5eSBram Moolenaar " target is running. 305b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 306b3307b5eSBram Moolenaar " Older gdb uses a different command. 307b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 308b3307b5eSBram Moolenaar 309b3307b5eSBram Moolenaar let s:ptybuf = 0 310b3307b5eSBram Moolenaar if has('win32') 311b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 312b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 313b3307b5eSBram Moolenaar elseif has('terminal') 314b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 315b3307b5eSBram Moolenaar " gdb window. 316b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 317b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 318b3307b5eSBram Moolenaar \ }) 319b3307b5eSBram Moolenaar if s:ptybuf == 0 320b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 321b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 322b3307b5eSBram Moolenaar return 323b3307b5eSBram Moolenaar endif 324b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 325b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 326b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 327b3307b5eSBram Moolenaar 328b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 329b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 330b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 331b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 332b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 333b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 334b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 335b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 336b3307b5eSBram Moolenaar else 337b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 338b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 339b3307b5eSBram Moolenaar endif 340b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 341b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 342b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 343b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 344b3307b5eSBram Moolenaar 345b3307b5eSBram Moolenaar " Set arguments to be run 346b3307b5eSBram Moolenaar if len(proc_args) 347b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 348b3307b5eSBram Moolenaar endif 349b3307b5eSBram Moolenaar 350b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 351b3307b5eSBram Moolenaar startinsert 352b3307b5eSBram Moolenaarendfunc 353b3307b5eSBram Moolenaar 354b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 35538baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 35638baa3e6SBram Moolenaar " There can be only one. 35738baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 35838baa3e6SBram Moolenaar 35945d5f26dSBram Moolenaar " Install debugger commands in the text window. 360b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 361e09ba7baSBram Moolenaar call s:InstallCommands() 36245d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 363e09ba7baSBram Moolenaar 36451b0f370SBram Moolenaar " Enable showing a balloon with eval info 365246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 366246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 36751b0f370SBram Moolenaar if has("balloon_eval") 36851b0f370SBram Moolenaar set ballooneval 369246fe03dSBram Moolenaar endif 37051b0f370SBram Moolenaar if has("balloon_eval_term") 37151b0f370SBram Moolenaar set balloonevalterm 37251b0f370SBram Moolenaar endif 37351b0f370SBram Moolenaar endif 37451b0f370SBram Moolenaar 3755378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 3765378e1cfSBram Moolenaar " breakpoint number. 37737402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 37837402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 37937402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 38037402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 38137402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 382e09ba7baSBram Moolenaar let s:breakpoints = {} 3831b9645deSBram Moolenaar 38437402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 38537402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 38637402ed5SBram Moolenaar let s:breakpoint_locations = {} 38737402ed5SBram Moolenaar 3881b9645deSBram Moolenaar augroup TermDebug 3891b9645deSBram Moolenaar au BufRead * call s:BufRead() 3901b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 391f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 3921b9645deSBram Moolenaar augroup END 39332c67ba7SBram Moolenaar 394b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 395b3307b5eSBram Moolenaar " window. 39632c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 39732c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 39832c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 39932c67ba7SBram Moolenaar endif 400c572da5fSBram Moolenaarendfunc 401c572da5fSBram Moolenaar 402b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 403b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 404b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 405b3307b5eSBram Moolenaar if s:way == 'prompt' 406b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 407b3307b5eSBram Moolenaar else 408b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 409b3307b5eSBram Moolenaar endif 410b3307b5eSBram Moolenaarendfunc 411b3307b5eSBram Moolenaar 412b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 413b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 414b3307b5eSBram Moolenaar if s:way == 'prompt' 415b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 416b3307b5eSBram Moolenaar else 417b3307b5eSBram Moolenaar let do_continue = 0 418b3307b5eSBram Moolenaar if !s:stopped 419b3307b5eSBram Moolenaar let do_continue = 1 420b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 421b3307b5eSBram Moolenaar sleep 10m 422b3307b5eSBram Moolenaar endif 423b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 424b3307b5eSBram Moolenaar if do_continue 425b3307b5eSBram Moolenaar Continue 426b3307b5eSBram Moolenaar endif 427b3307b5eSBram Moolenaar endif 428b3307b5eSBram Moolenaarendfunc 429b3307b5eSBram Moolenaar 430b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 431b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 432b3307b5eSBram Moolenaar call s:SendCommand(a:text) 433b3307b5eSBram Moolenaarendfunc 434b3307b5eSBram Moolenaar 4354551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4364551c0a9SBram Moolenaar" breakpoint. 437b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4382ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4392ed890f1SBram Moolenaar if has('win32') 4402ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4412ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4424551c0a9SBram Moolenaar if s:pid == 0 4434551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4444551c0a9SBram Moolenaar else 4454551c0a9SBram Moolenaar call debugbreak(s:pid) 4464551c0a9SBram Moolenaar endif 4472ed890f1SBram Moolenaar else 4482ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4492ed890f1SBram Moolenaar endif 450b3307b5eSBram Moolenaarendfunc 451b3307b5eSBram Moolenaar 452b3307b5eSBram Moolenaar" Function called when gdb outputs text. 453b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 454b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 455b3307b5eSBram Moolenaar 456b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 457b3307b5eSBram Moolenaar " Drop status and echo'd commands. 458a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 459b3307b5eSBram Moolenaar return 460b3307b5eSBram Moolenaar endif 461b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 462b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 463b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 464b3307b5eSBram Moolenaar " Silently drop evaluation errors. 465b3307b5eSBram Moolenaar unlet s:evalexpr 466b3307b5eSBram Moolenaar return 467b3307b5eSBram Moolenaar endif 468b3307b5eSBram Moolenaar elseif a:text[0] == '~' 469b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 470b3307b5eSBram Moolenaar else 471b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 472b3307b5eSBram Moolenaar return 473b3307b5eSBram Moolenaar endif 474b3307b5eSBram Moolenaar 475b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 476b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 477b3307b5eSBram Moolenaar 478b3307b5eSBram Moolenaar " Add the output above the current prompt. 479b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4804551c0a9SBram Moolenaar set modified 481b3307b5eSBram Moolenaar 482b3307b5eSBram Moolenaar call win_gotoid(curwinid) 483b3307b5eSBram Moolenaarendfunc 484b3307b5eSBram Moolenaar 485b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 486b3307b5eSBram Moolenaar" to the next ", unescaping characters. 487b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 488b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 489a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 490b3307b5eSBram Moolenaar return 491b3307b5eSBram Moolenaar endif 492b3307b5eSBram Moolenaar let result = '' 493b3307b5eSBram Moolenaar let i = 1 494b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 495b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 496b3307b5eSBram Moolenaar let i += 1 497b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 498b3307b5eSBram Moolenaar " drop \n 499b3307b5eSBram Moolenaar let i += 1 500b3307b5eSBram Moolenaar continue 501b3307b5eSBram Moolenaar endif 502b3307b5eSBram Moolenaar endif 503b3307b5eSBram Moolenaar let result .= a:quotedText[i] 504b3307b5eSBram Moolenaar let i += 1 505b3307b5eSBram Moolenaar endwhile 506b3307b5eSBram Moolenaar return result 507b3307b5eSBram Moolenaarendfunc 508b3307b5eSBram Moolenaar 509a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 510a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5115378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5125378e1cfSBram Moolenaar return '' 5135378e1cfSBram Moolenaar endif 514a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 515a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 516a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 517a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 518a15b0a93SBram Moolenaar endif 519a15b0a93SBram Moolenaar return name 520a15b0a93SBram Moolenaarendfunc 521a15b0a93SBram Moolenaar 522b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 523fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 524b3623a38SBram Moolenaar unlet s:gdbwin 525e09ba7baSBram Moolenaar 526b3307b5eSBram Moolenaar call s:EndDebugCommon() 527b3307b5eSBram Moolenaarendfunc 528b3307b5eSBram Moolenaar 529b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 530e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 531e09ba7baSBram Moolenaar 532b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 533b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 534b3307b5eSBram Moolenaar endif 535b3307b5eSBram Moolenaar 536b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 537e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 538e09ba7baSBram Moolenaar call s:DeleteCommands() 539e09ba7baSBram Moolenaar 540e09ba7baSBram Moolenaar call win_gotoid(curwinid) 541b3307b5eSBram Moolenaar 54238baa3e6SBram Moolenaar if s:save_columns > 0 54338baa3e6SBram Moolenaar let &columns = s:save_columns 54438baa3e6SBram Moolenaar endif 5451b9645deSBram Moolenaar 546246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 547246fe03dSBram Moolenaar set balloonexpr= 54851b0f370SBram Moolenaar if has("balloon_eval") 54951b0f370SBram Moolenaar set noballooneval 550246fe03dSBram Moolenaar endif 55151b0f370SBram Moolenaar if has("balloon_eval_term") 55251b0f370SBram Moolenaar set noballoonevalterm 55351b0f370SBram Moolenaar endif 55451b0f370SBram Moolenaar endif 55551b0f370SBram Moolenaar 5561b9645deSBram Moolenaar au! TermDebug 557fe386641SBram Moolenaarendfunc 558fe386641SBram Moolenaar 559b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 560b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 561b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5624551c0a9SBram Moolenaar set nomodified 563b3307b5eSBram Moolenaar close 564b3307b5eSBram Moolenaar if curwinid != s:gdbwin 565b3307b5eSBram Moolenaar call win_gotoid(curwinid) 566b3307b5eSBram Moolenaar endif 567b3307b5eSBram Moolenaar 568b3307b5eSBram Moolenaar call s:EndDebugCommon() 569b3307b5eSBram Moolenaar unlet s:gdbwin 570b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 571b3307b5eSBram Moolenaarendfunc 572b3307b5eSBram Moolenaar 573fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 574fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 575fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 576fe386641SBram Moolenaar 577fe386641SBram Moolenaar for msg in msgs 578fe386641SBram Moolenaar " remove prefixed NL 579fe386641SBram Moolenaar if msg[0] == "\n" 580fe386641SBram Moolenaar let msg = msg[1:] 581fe386641SBram Moolenaar endif 582fe386641SBram Moolenaar if msg != '' 5831b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 584e09ba7baSBram Moolenaar call s:HandleCursor(msg) 58545d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 586e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 587e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 588e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 5894551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 5904551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 59145d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 59245d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 59345d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 59445d5f26dSBram Moolenaar call s:HandleError(msg) 595e09ba7baSBram Moolenaar endif 596e09ba7baSBram Moolenaar endif 597e09ba7baSBram Moolenaar endfor 598e09ba7baSBram Moolenaarendfunc 599e09ba7baSBram Moolenaar 600e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 601e09ba7baSBram Moolenaarfunc s:InstallCommands() 602963c1ad5SBram Moolenaar let save_cpo = &cpo 603963c1ad5SBram Moolenaar set cpo&vim 604963c1ad5SBram Moolenaar 605e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 60671137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 607e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 60845d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 609e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 61060e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 61160e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 61260e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 613b3307b5eSBram Moolenaar 614b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 615b3307b5eSBram Moolenaar if s:way == 'prompt' 616b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 617b3307b5eSBram Moolenaar else 618b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 619b3307b5eSBram Moolenaar endif 620b3307b5eSBram Moolenaar 62145d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 62245d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 62345d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 624b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 62571137fedSBram Moolenaar command Winbar call s:InstallWinbar() 62645d5f26dSBram Moolenaar 62745d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 62845d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 6291b9645deSBram Moolenaar 630f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 63171137fedSBram Moolenaar call s:InstallWinbar() 63271137fedSBram Moolenaar 63371137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 63471137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 63571137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 63671137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 63771137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 63871137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 63971137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 64071137fedSBram Moolenaar endif 64171137fedSBram Moolenaar endif 642963c1ad5SBram Moolenaar 643963c1ad5SBram Moolenaar let &cpo = save_cpo 64471137fedSBram Moolenaarendfunc 64571137fedSBram Moolenaar 64671137fedSBram Moolenaarlet s:winbar_winids = [] 64771137fedSBram Moolenaar 64871137fedSBram Moolenaar" Install the window toolbar in the current window. 64971137fedSBram Moolenaarfunc s:InstallWinbar() 650c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 65124a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 65224a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 65324a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 65424a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 65560e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 65624a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 65771137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 658c4b533e1SBram Moolenaar endif 659e09ba7baSBram Moolenaarendfunc 660e09ba7baSBram Moolenaar 661e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 662e09ba7baSBram Moolenaarfunc s:DeleteCommands() 663e09ba7baSBram Moolenaar delcommand Break 66471137fedSBram Moolenaar delcommand Clear 665e09ba7baSBram Moolenaar delcommand Step 66645d5f26dSBram Moolenaar delcommand Over 667e09ba7baSBram Moolenaar delcommand Finish 66860e73f2aSBram Moolenaar delcommand Run 66960e73f2aSBram Moolenaar delcommand Arguments 67060e73f2aSBram Moolenaar delcommand Stop 671e09ba7baSBram Moolenaar delcommand Continue 67245d5f26dSBram Moolenaar delcommand Evaluate 67345d5f26dSBram Moolenaar delcommand Gdb 67445d5f26dSBram Moolenaar delcommand Program 675b3623a38SBram Moolenaar delcommand Source 67671137fedSBram Moolenaar delcommand Winbar 67745d5f26dSBram Moolenaar 67845d5f26dSBram Moolenaar nunmap K 6791b9645deSBram Moolenaar 6801b9645deSBram Moolenaar if has('menu') 68171137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 68271137fedSBram Moolenaar let curwinid = win_getid(winnr()) 68371137fedSBram Moolenaar for winid in s:winbar_winids 68471137fedSBram Moolenaar if win_gotoid(winid) 6851b9645deSBram Moolenaar aunmenu WinBar.Step 6861b9645deSBram Moolenaar aunmenu WinBar.Next 6871b9645deSBram Moolenaar aunmenu WinBar.Finish 6881b9645deSBram Moolenaar aunmenu WinBar.Cont 68960e73f2aSBram Moolenaar aunmenu WinBar.Stop 6901b9645deSBram Moolenaar aunmenu WinBar.Eval 6911b9645deSBram Moolenaar endif 69271137fedSBram Moolenaar endfor 69371137fedSBram Moolenaar call win_gotoid(curwinid) 69471137fedSBram Moolenaar let s:winbar_winids = [] 69571137fedSBram Moolenaar 69671137fedSBram Moolenaar if exists('s:saved_mousemodel') 69771137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 69871137fedSBram Moolenaar unlet s:saved_mousemodel 69971137fedSBram Moolenaar aunmenu PopUp.-SEP3- 70071137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 70171137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 70271137fedSBram Moolenaar aunmenu PopUp.Evaluate 70371137fedSBram Moolenaar endif 70471137fedSBram Moolenaar endif 7051b9645deSBram Moolenaar 70645d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 70737402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 70837402ed5SBram Moolenaar for subid in keys(entries) 70937402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 71037402ed5SBram Moolenaar endfor 71145d5f26dSBram Moolenaar endfor 71245d5f26dSBram Moolenaar unlet s:breakpoints 71337402ed5SBram Moolenaar unlet s:breakpoint_locations 714a15b0a93SBram Moolenaar 715a15b0a93SBram Moolenaar sign undefine debugPC 716a15b0a93SBram Moolenaar for val in s:BreakpointSigns 717a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 718a15b0a93SBram Moolenaar endfor 7194551c0a9SBram Moolenaar let s:BreakpointSigns = [] 720e09ba7baSBram Moolenaarendfunc 721e09ba7baSBram Moolenaar 722e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 723e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 72460e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 72560e73f2aSBram Moolenaar " Interrupt to make it work. 72660e73f2aSBram Moolenaar let do_continue = 0 72760e73f2aSBram Moolenaar if !s:stopped 72860e73f2aSBram Moolenaar let do_continue = 1 729b3307b5eSBram Moolenaar if s:way == 'prompt' 7304551c0a9SBram Moolenaar call s:PromptInterrupt() 731b3307b5eSBram Moolenaar else 73260e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 733b3307b5eSBram Moolenaar endif 73460e73f2aSBram Moolenaar sleep 10m 73560e73f2aSBram Moolenaar endif 736a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 737a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 738a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 73960e73f2aSBram Moolenaar if do_continue 74060e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 74160e73f2aSBram Moolenaar endif 742e09ba7baSBram Moolenaarendfunc 743e09ba7baSBram Moolenaar 74471137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 74571137fedSBram Moolenaarfunc s:ClearBreakpoint() 746e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 747e09ba7baSBram Moolenaar let lnum = line('.') 74837402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 74937402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 75037402ed5SBram Moolenaar let idx = 0 75137402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 75237402ed5SBram Moolenaar if has_key(s:breakpoints, id) 75337402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 75437402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 75537402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 75637402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 75737402ed5SBram Moolenaar endfor 75837402ed5SBram Moolenaar unlet s:breakpoints[id] 75937402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 760e09ba7baSBram Moolenaar break 76137402ed5SBram Moolenaar else 76237402ed5SBram Moolenaar let idx += 1 763e09ba7baSBram Moolenaar endif 764e09ba7baSBram Moolenaar endfor 76537402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 76637402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 76737402ed5SBram Moolenaar endif 76837402ed5SBram Moolenaar endif 769e09ba7baSBram Moolenaarendfunc 770e09ba7baSBram Moolenaar 77160e73f2aSBram Moolenaarfunc s:Run(args) 77260e73f2aSBram Moolenaar if a:args != '' 77360e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 77460e73f2aSBram Moolenaar endif 77560e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 77660e73f2aSBram Moolenaarendfunc 77760e73f2aSBram Moolenaar 77851b0f370SBram Moolenaarfunc s:SendEval(expr) 77951b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 78051b0f370SBram Moolenaar let s:evalexpr = a:expr 78151b0f370SBram Moolenaarendfunc 78251b0f370SBram Moolenaar 78345d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 78445d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 78545d5f26dSBram Moolenaar if a:arg != '' 78645d5f26dSBram Moolenaar let expr = a:arg 78745d5f26dSBram Moolenaar elseif a:range == 2 78845d5f26dSBram Moolenaar let pos = getcurpos() 78945d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 79045d5f26dSBram Moolenaar let regt = getregtype('v') 79145d5f26dSBram Moolenaar normal! gv"vy 79245d5f26dSBram Moolenaar let expr = @v 79345d5f26dSBram Moolenaar call setpos('.', pos) 79445d5f26dSBram Moolenaar call setreg('v', reg, regt) 79545d5f26dSBram Moolenaar else 79645d5f26dSBram Moolenaar let expr = expand('<cexpr>') 79745d5f26dSBram Moolenaar endif 79822f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 79951b0f370SBram Moolenaar call s:SendEval(expr) 80045d5f26dSBram Moolenaarendfunc 80145d5f26dSBram Moolenaar 80222f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 80351b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 80451b0f370SBram Moolenaar 80545d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 80645d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 8071b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 8081b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 80951b0f370SBram Moolenaar if s:evalFromBalloonExpr 81051b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 81151b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 81251b0f370SBram Moolenaar else 81351b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 81451b0f370SBram Moolenaar endif 81551b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 81651b0f370SBram Moolenaar else 8171b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 81851b0f370SBram Moolenaar endif 8191b9645deSBram Moolenaar 8207f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 8211b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 82222f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 82351b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 82451b0f370SBram Moolenaar else 82551b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 8261b9645deSBram Moolenaar endif 82745d5f26dSBram Moolenaarendfunc 82845d5f26dSBram Moolenaar 82951b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 83051b0f370SBram Moolenaar" if there is any. 83151b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 832b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 833b3307b5eSBram Moolenaar return 834b3307b5eSBram Moolenaar endif 835b3307b5eSBram Moolenaar if !s:stopped 836b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 837b3307b5eSBram Moolenaar " mouse triggers a balloon. 83851b0f370SBram Moolenaar return 83951b0f370SBram Moolenaar endif 84051b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 84151b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 84222f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 84322f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 84451b0f370SBram Moolenaar return '' 84551b0f370SBram Moolenaarendfunc 84651b0f370SBram Moolenaar 84745d5f26dSBram Moolenaar" Handle an error. 84845d5f26dSBram Moolenaarfunc s:HandleError(msg) 84922f1d0e3SBram Moolenaar if s:ignoreEvalError 85051b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 85122f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 85222f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 85351b0f370SBram Moolenaar return 85451b0f370SBram Moolenaar endif 85545d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 85645d5f26dSBram Moolenaarendfunc 85745d5f26dSBram Moolenaar 858b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 859b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 860c4b533e1SBram Moolenaar new 861b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 862c4b533e1SBram Moolenaar call s:InstallWinbar() 863c4b533e1SBram Moolenaar endif 864c4b533e1SBram Moolenaarendfunc 865c4b533e1SBram Moolenaar 866e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 867e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 868e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 869fe386641SBram Moolenaar let wid = win_getid(winnr()) 870fe386641SBram Moolenaar 87160e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8724551c0a9SBram Moolenaar call ch_log('program stopped') 87360e73f2aSBram Moolenaar let s:stopped = 1 87460e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8754551c0a9SBram Moolenaar call ch_log('program running') 87660e73f2aSBram Moolenaar let s:stopped = 0 87760e73f2aSBram Moolenaar endif 87860e73f2aSBram Moolenaar 879a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 880a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 881a15b0a93SBram Moolenaar else 882a15b0a93SBram Moolenaar let fname = '' 883a15b0a93SBram Moolenaar endif 8841b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 885e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 886fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8874551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 8881b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 889fe386641SBram Moolenaar if &modified 890fe386641SBram Moolenaar " TODO: find existing window 891fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 892b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 893c4b533e1SBram Moolenaar call s:InstallWinbar() 894fe386641SBram Moolenaar else 895fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 896fe386641SBram Moolenaar endif 897fe386641SBram Moolenaar endif 898fe386641SBram Moolenaar exe lnum 89901164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 9001b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 901fe386641SBram Moolenaar setlocal signcolumn=yes 902fe386641SBram Moolenaar endif 9034551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 904fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 905fe386641SBram Moolenaar endif 906fe386641SBram Moolenaar 907fe386641SBram Moolenaar call win_gotoid(wid) 908e09ba7baSBram Moolenaarendfunc 909e09ba7baSBram Moolenaar 910de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 911a15b0a93SBram Moolenaar 91237402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 91337402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 91437402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 91537402ed5SBram Moolenaar call add(s:BreakpointSigns, nr) 91637402ed5SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 917de1a8314SBram Moolenaar endif 918de1a8314SBram Moolenaarendfunc 919de1a8314SBram Moolenaar 92037402ed5SBram Moolenaarfunc! s:SplitMsg(s) 92137402ed5SBram Moolenaar return split(a:s, '{.\{-}}\zs') 9225378e1cfSBram Moolenaarendfunction 9235378e1cfSBram Moolenaar 924e09ba7baSBram Moolenaar" Handle setting a breakpoint 925e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 926e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 9276dccc962SBram Moolenaar if a:msg !~ 'fullname=' 9286dccc962SBram Moolenaar " a watch does not have a file name 9296dccc962SBram Moolenaar return 9306dccc962SBram Moolenaar endif 9315378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 9325378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 9335378e1cfSBram Moolenaar if empty(fname) 9345378e1cfSBram Moolenaar continue 9355378e1cfSBram Moolenaar endif 9365378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 9375378e1cfSBram Moolenaar if empty(nr) 938e09ba7baSBram Moolenaar return 939fe386641SBram Moolenaar endif 940e09ba7baSBram Moolenaar 94137402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 94237402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 94337402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 94437402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 94537402ed5SBram Moolenaar 94637402ed5SBram Moolenaar if has_key(s:breakpoints, id) 94737402ed5SBram Moolenaar let entries = s:breakpoints[id] 94837402ed5SBram Moolenaar else 94937402ed5SBram Moolenaar let entries = {} 95037402ed5SBram Moolenaar let s:breakpoints[id] = entries 95137402ed5SBram Moolenaar endif 95237402ed5SBram Moolenaar if has_key(entries, subid) 95337402ed5SBram Moolenaar let entry = entries[subid] 954e09ba7baSBram Moolenaar else 955e09ba7baSBram Moolenaar let entry = {} 95637402ed5SBram Moolenaar let entries[subid] = entry 957fe386641SBram Moolenaar endif 958e09ba7baSBram Moolenaar 9595378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 960e09ba7baSBram Moolenaar let entry['fname'] = fname 961e09ba7baSBram Moolenaar let entry['lnum'] = lnum 9621b9645deSBram Moolenaar 96337402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 96437402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 96537402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 96637402ed5SBram Moolenaar endif 96737402ed5SBram Moolenaar let s:breakpoint_locations[bploc] += [id] 96837402ed5SBram Moolenaar 9691b9645deSBram Moolenaar if bufloaded(fname) 97037402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 9711b9645deSBram Moolenaar endif 9725378e1cfSBram Moolenaar endfor 9731b9645deSBram Moolenaarendfunc 9741b9645deSBram Moolenaar 97537402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry) 97637402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 97737402ed5SBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname'] 9781b9645deSBram Moolenaar let a:entry['placed'] = 1 979e09ba7baSBram Moolenaarendfunc 980e09ba7baSBram Moolenaar 981e09ba7baSBram Moolenaar" Handle deleting a breakpoint 982e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 983e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 98437402ed5SBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 98537402ed5SBram Moolenaar if empty(id) 986e09ba7baSBram Moolenaar return 987e09ba7baSBram Moolenaar endif 98837402ed5SBram Moolenaar if has_key(s:breakpoints, id) 98937402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 9901b9645deSBram Moolenaar if has_key(entry, 'placed') 99137402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 9921b9645deSBram Moolenaar unlet entry['placed'] 9931b9645deSBram Moolenaar endif 9945378e1cfSBram Moolenaar endfor 99537402ed5SBram Moolenaar unlet s:breakpoints[id] 99637402ed5SBram Moolenaar endif 997c572da5fSBram Moolenaarendfunc 9981b9645deSBram Moolenaar 9994551c0a9SBram Moolenaar" Handle the debugged program starting to run. 10004551c0a9SBram Moolenaar" Will store the process ID in s:pid 10014551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 10024551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 10034551c0a9SBram Moolenaar if nr == 0 10044551c0a9SBram Moolenaar return 10054551c0a9SBram Moolenaar endif 10064551c0a9SBram Moolenaar let s:pid = nr 10074551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 10084551c0a9SBram Moolenaarendfunc 10094551c0a9SBram Moolenaar 10101b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 10111b9645deSBram Moolenaarfunc s:BufRead() 10121b9645deSBram Moolenaar let fname = expand('<afile>:p') 101337402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 101437402ed5SBram Moolenaar for [subid, entry] in items(entries) 10151b9645deSBram Moolenaar if entry['fname'] == fname 101637402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10171b9645deSBram Moolenaar endif 10181b9645deSBram Moolenaar endfor 101937402ed5SBram Moolenaar endfor 10201b9645deSBram Moolenaarendfunc 10211b9645deSBram Moolenaar 10221b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 10231b9645deSBram Moolenaarfunc s:BufUnloaded() 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 10281b9645deSBram Moolenaar let entry['placed'] = 0 10291b9645deSBram Moolenaar endif 10301b9645deSBram Moolenaar endfor 103137402ed5SBram Moolenaar endfor 10321b9645deSBram Moolenaarendfunc 1033ca4cc018SBram Moolenaar 1034ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 1035ca4cc018SBram Moolenaarunlet s:keepcpo 1036