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. 77*37402ed5SBram Moolenaar" The breakpoint may contain a dot: 123.4 -> 123004 78*37402ed5SBram Moolenaar" The main breakpoint has a zero subid. 79*37402ed5SBram Moolenaarfunc s:Breakpoint2SignNumber(id, subid) 80*37402ed5SBram 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 145b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 146b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 147fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 148b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 149b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 150fe386641SBram Moolenaar \ }) 151fe386641SBram Moolenaar if s:ptybuf == 0 152fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 153fe386641SBram Moolenaar return 154fe386641SBram Moolenaar endif 155fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 15645d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 157b3307b5eSBram Moolenaar if s:vertical 15851b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 15951b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 16051b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 16151b0f370SBram Moolenaar endif 162fe386641SBram Moolenaar 163fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 164fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 165fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 166fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 167fe386641SBram Moolenaar \ 'hidden': 1, 168fe386641SBram Moolenaar \ }) 169fe386641SBram Moolenaar if s:commbuf == 0 170fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 171fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 172fe386641SBram Moolenaar return 173fe386641SBram Moolenaar endif 174fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 175c572da5fSBram Moolenaar 176c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 177c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 17832c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 17932c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 18032c67ba7SBram Moolenaar 18132c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 182b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 18360e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 184b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndTermDebug'), 185fe386641SBram Moolenaar \ 'term_finish': 'close', 186c572da5fSBram Moolenaar \ }) 18760e73f2aSBram Moolenaar if s:gdbbuf == 0 188fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 189fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 190fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 191fe386641SBram Moolenaar return 192fe386641SBram Moolenaar endif 19345d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 194fe386641SBram Moolenaar 19532c67ba7SBram Moolenaar " Set arguments to be run 19632c67ba7SBram Moolenaar if len(proc_args) 19732c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 19832c67ba7SBram Moolenaar endif 19932c67ba7SBram Moolenaar 200fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 20160e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 20260e73f2aSBram Moolenaar 2033e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 2043e4b84d0SBram Moolenaar " why the debugger doesn't work. 2053e4b84d0SBram Moolenaar let try_count = 0 2063e4b84d0SBram Moolenaar while 1 2073e4b84d0SBram Moolenaar let response = '' 208b3623a38SBram Moolenaar for lnum in range(1,200) 2093e4b84d0SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 210f63db65bSBram Moolenaar " response can be in the same line or the next line 211f63db65bSBram Moolenaar let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1) 2123e4b84d0SBram Moolenaar if response =~ 'Undefined command' 213f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 2143e4b84d0SBram Moolenaar exe 'bwipe! ' . s:ptybuf 2153e4b84d0SBram Moolenaar exe 'bwipe! ' . s:commbuf 2163e4b84d0SBram Moolenaar return 2173e4b84d0SBram Moolenaar endif 2183e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2193e4b84d0SBram Moolenaar " Success! 2203e4b84d0SBram Moolenaar break 2213e4b84d0SBram Moolenaar endif 2223e4b84d0SBram Moolenaar endif 2233e4b84d0SBram Moolenaar endfor 2243e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2253e4b84d0SBram Moolenaar break 2263e4b84d0SBram Moolenaar endif 2273e4b84d0SBram Moolenaar let try_count += 1 2283e4b84d0SBram Moolenaar if try_count > 100 2293e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2303e4b84d0SBram Moolenaar break 2313e4b84d0SBram Moolenaar endif 2323e4b84d0SBram Moolenaar sleep 10m 2333e4b84d0SBram Moolenaar endwhile 2343e4b84d0SBram Moolenaar 23560e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 23660e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 23760e73f2aSBram Moolenaar " running. 23860e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 239b3307b5eSBram Moolenaar " Older gdb uses a different command. 240b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 241e09ba7baSBram Moolenaar 242f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 243f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 244b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 245f3ba14ffSBram Moolenaar 246b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 247b3307b5eSBram Moolenaarendfunc 248b3307b5eSBram Moolenaar 249b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 250b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 251b3307b5eSBram Moolenaar if s:vertical 252b3307b5eSBram Moolenaar vertical new 253b3307b5eSBram Moolenaar else 254b3307b5eSBram Moolenaar new 255b3307b5eSBram Moolenaar endif 256b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 257b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 258b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 259b3307b5eSBram Moolenaar set buftype=prompt 260b3307b5eSBram Moolenaar file gdb 261b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 262b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 263b3307b5eSBram Moolenaar 264b3307b5eSBram Moolenaar if s:vertical 265b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 266b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 267b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 268b3307b5eSBram Moolenaar endif 269b3307b5eSBram Moolenaar 270b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 271b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 272b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 273b3307b5eSBram Moolenaar 274b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 275b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 276b3307b5eSBram Moolenaar 277b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 278b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 279b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 280b3307b5eSBram Moolenaar \ }) 281b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 282b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 283b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 284b3307b5eSBram Moolenaar return 285b3307b5eSBram Moolenaar endif 2864551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 2874551c0a9SBram Moolenaar set modified 288b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 289b3307b5eSBram Moolenaar 290b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 291b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 292b3307b5eSBram Moolenaar " target is running. 293b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 294b3307b5eSBram Moolenaar " Older gdb uses a different command. 295b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 296b3307b5eSBram Moolenaar 297b3307b5eSBram Moolenaar let s:ptybuf = 0 298b3307b5eSBram Moolenaar if has('win32') 299b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 300b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 301b3307b5eSBram Moolenaar elseif has('terminal') 302b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 303b3307b5eSBram Moolenaar " gdb window. 304b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 305b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 306b3307b5eSBram Moolenaar \ }) 307b3307b5eSBram Moolenaar if s:ptybuf == 0 308b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 309b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 310b3307b5eSBram Moolenaar return 311b3307b5eSBram Moolenaar endif 312b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 313b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 314b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 315b3307b5eSBram Moolenaar 316b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 317b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 318b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 319b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 320b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 321b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 322b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 323b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 324b3307b5eSBram Moolenaar else 325b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 326b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 327b3307b5eSBram Moolenaar endif 328b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 329b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 330b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 331b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 332b3307b5eSBram Moolenaar 333b3307b5eSBram Moolenaar " Set arguments to be run 334b3307b5eSBram Moolenaar if len(proc_args) 335b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 336b3307b5eSBram Moolenaar endif 337b3307b5eSBram Moolenaar 338b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 339b3307b5eSBram Moolenaar startinsert 340b3307b5eSBram Moolenaarendfunc 341b3307b5eSBram Moolenaar 342b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 34338baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 34438baa3e6SBram Moolenaar " There can be only one. 34538baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 34638baa3e6SBram Moolenaar 34745d5f26dSBram Moolenaar " Install debugger commands in the text window. 348b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 349e09ba7baSBram Moolenaar call s:InstallCommands() 35045d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 351e09ba7baSBram Moolenaar 35251b0f370SBram Moolenaar " Enable showing a balloon with eval info 353246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 354246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 35551b0f370SBram Moolenaar if has("balloon_eval") 35651b0f370SBram Moolenaar set ballooneval 357246fe03dSBram Moolenaar endif 35851b0f370SBram Moolenaar if has("balloon_eval_term") 35951b0f370SBram Moolenaar set balloonevalterm 36051b0f370SBram Moolenaar endif 36151b0f370SBram Moolenaar endif 36251b0f370SBram Moolenaar 3635378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 3645378e1cfSBram Moolenaar " breakpoint number. 365*37402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 366*37402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 367*37402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 368*37402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 369*37402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 370e09ba7baSBram Moolenaar let s:breakpoints = {} 3711b9645deSBram Moolenaar 372*37402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 373*37402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 374*37402ed5SBram Moolenaar let s:breakpoint_locations = {} 375*37402ed5SBram Moolenaar 3761b9645deSBram Moolenaar augroup TermDebug 3771b9645deSBram Moolenaar au BufRead * call s:BufRead() 3781b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 379f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 3801b9645deSBram Moolenaar augroup END 38132c67ba7SBram Moolenaar 382b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 383b3307b5eSBram Moolenaar " window. 38432c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 38532c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 38632c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 38732c67ba7SBram Moolenaar endif 388c572da5fSBram Moolenaarendfunc 389c572da5fSBram Moolenaar 390b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 391b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 392b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 393b3307b5eSBram Moolenaar if s:way == 'prompt' 394b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 395b3307b5eSBram Moolenaar else 396b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 397b3307b5eSBram Moolenaar endif 398b3307b5eSBram Moolenaarendfunc 399b3307b5eSBram Moolenaar 400b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 401b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 402b3307b5eSBram Moolenaar if s:way == 'prompt' 403b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 404b3307b5eSBram Moolenaar else 405b3307b5eSBram Moolenaar let do_continue = 0 406b3307b5eSBram Moolenaar if !s:stopped 407b3307b5eSBram Moolenaar let do_continue = 1 408b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 409b3307b5eSBram Moolenaar sleep 10m 410b3307b5eSBram Moolenaar endif 411b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 412b3307b5eSBram Moolenaar if do_continue 413b3307b5eSBram Moolenaar Continue 414b3307b5eSBram Moolenaar endif 415b3307b5eSBram Moolenaar endif 416b3307b5eSBram Moolenaarendfunc 417b3307b5eSBram Moolenaar 418b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 419b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 420b3307b5eSBram Moolenaar call s:SendCommand(a:text) 421b3307b5eSBram Moolenaarendfunc 422b3307b5eSBram Moolenaar 4234551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4244551c0a9SBram Moolenaar" breakpoint. 425b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4262ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4272ed890f1SBram Moolenaar if has('win32') 4282ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4292ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4304551c0a9SBram Moolenaar if s:pid == 0 4314551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4324551c0a9SBram Moolenaar else 4334551c0a9SBram Moolenaar call debugbreak(s:pid) 4344551c0a9SBram Moolenaar endif 4352ed890f1SBram Moolenaar else 4362ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4372ed890f1SBram Moolenaar endif 438b3307b5eSBram Moolenaarendfunc 439b3307b5eSBram Moolenaar 440b3307b5eSBram Moolenaar" Function called when gdb outputs text. 441b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 442b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 443b3307b5eSBram Moolenaar 444b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 445b3307b5eSBram Moolenaar " Drop status and echo'd commands. 446a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 447b3307b5eSBram Moolenaar return 448b3307b5eSBram Moolenaar endif 449b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 450b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 451b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 452b3307b5eSBram Moolenaar " Silently drop evaluation errors. 453b3307b5eSBram Moolenaar unlet s:evalexpr 454b3307b5eSBram Moolenaar return 455b3307b5eSBram Moolenaar endif 456b3307b5eSBram Moolenaar elseif a:text[0] == '~' 457b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 458b3307b5eSBram Moolenaar else 459b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 460b3307b5eSBram Moolenaar return 461b3307b5eSBram Moolenaar endif 462b3307b5eSBram Moolenaar 463b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 464b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 465b3307b5eSBram Moolenaar 466b3307b5eSBram Moolenaar " Add the output above the current prompt. 467b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4684551c0a9SBram Moolenaar set modified 469b3307b5eSBram Moolenaar 470b3307b5eSBram Moolenaar call win_gotoid(curwinid) 471b3307b5eSBram Moolenaarendfunc 472b3307b5eSBram Moolenaar 473b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 474b3307b5eSBram Moolenaar" to the next ", unescaping characters. 475b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 476b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 477a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 478b3307b5eSBram Moolenaar return 479b3307b5eSBram Moolenaar endif 480b3307b5eSBram Moolenaar let result = '' 481b3307b5eSBram Moolenaar let i = 1 482b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 483b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 484b3307b5eSBram Moolenaar let i += 1 485b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 486b3307b5eSBram Moolenaar " drop \n 487b3307b5eSBram Moolenaar let i += 1 488b3307b5eSBram Moolenaar continue 489b3307b5eSBram Moolenaar endif 490b3307b5eSBram Moolenaar endif 491b3307b5eSBram Moolenaar let result .= a:quotedText[i] 492b3307b5eSBram Moolenaar let i += 1 493b3307b5eSBram Moolenaar endwhile 494b3307b5eSBram Moolenaar return result 495b3307b5eSBram Moolenaarendfunc 496b3307b5eSBram Moolenaar 497a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 498a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 4995378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5005378e1cfSBram Moolenaar return '' 5015378e1cfSBram Moolenaar endif 502a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 503a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 504a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 505a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 506a15b0a93SBram Moolenaar endif 507a15b0a93SBram Moolenaar return name 508a15b0a93SBram Moolenaarendfunc 509a15b0a93SBram Moolenaar 510b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 511fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 512b3623a38SBram Moolenaar unlet s:gdbwin 513e09ba7baSBram Moolenaar 514b3307b5eSBram Moolenaar call s:EndDebugCommon() 515b3307b5eSBram Moolenaarendfunc 516b3307b5eSBram Moolenaar 517b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 518e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 519e09ba7baSBram Moolenaar 520b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 521b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 522b3307b5eSBram Moolenaar endif 523b3307b5eSBram Moolenaar 524b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 525e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 526e09ba7baSBram Moolenaar call s:DeleteCommands() 527e09ba7baSBram Moolenaar 528e09ba7baSBram Moolenaar call win_gotoid(curwinid) 529b3307b5eSBram Moolenaar 53038baa3e6SBram Moolenaar if s:save_columns > 0 53138baa3e6SBram Moolenaar let &columns = s:save_columns 53238baa3e6SBram Moolenaar endif 5331b9645deSBram Moolenaar 534246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 535246fe03dSBram Moolenaar set balloonexpr= 53651b0f370SBram Moolenaar if has("balloon_eval") 53751b0f370SBram Moolenaar set noballooneval 538246fe03dSBram Moolenaar endif 53951b0f370SBram Moolenaar if has("balloon_eval_term") 54051b0f370SBram Moolenaar set noballoonevalterm 54151b0f370SBram Moolenaar endif 54251b0f370SBram Moolenaar endif 54351b0f370SBram Moolenaar 5441b9645deSBram Moolenaar au! TermDebug 545fe386641SBram Moolenaarendfunc 546fe386641SBram Moolenaar 547b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 548b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 549b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5504551c0a9SBram Moolenaar set nomodified 551b3307b5eSBram Moolenaar close 552b3307b5eSBram Moolenaar if curwinid != s:gdbwin 553b3307b5eSBram Moolenaar call win_gotoid(curwinid) 554b3307b5eSBram Moolenaar endif 555b3307b5eSBram Moolenaar 556b3307b5eSBram Moolenaar call s:EndDebugCommon() 557b3307b5eSBram Moolenaar unlet s:gdbwin 558b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 559b3307b5eSBram Moolenaarendfunc 560b3307b5eSBram Moolenaar 561fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 562fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 563fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 564fe386641SBram Moolenaar 565fe386641SBram Moolenaar for msg in msgs 566fe386641SBram Moolenaar " remove prefixed NL 567fe386641SBram Moolenaar if msg[0] == "\n" 568fe386641SBram Moolenaar let msg = msg[1:] 569fe386641SBram Moolenaar endif 570fe386641SBram Moolenaar if msg != '' 5711b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 572e09ba7baSBram Moolenaar call s:HandleCursor(msg) 57345d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 574e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 575e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 576e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 5774551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 5784551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 57945d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 58045d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 58145d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 58245d5f26dSBram Moolenaar call s:HandleError(msg) 583e09ba7baSBram Moolenaar endif 584e09ba7baSBram Moolenaar endif 585e09ba7baSBram Moolenaar endfor 586e09ba7baSBram Moolenaarendfunc 587e09ba7baSBram Moolenaar 588e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 589e09ba7baSBram Moolenaarfunc s:InstallCommands() 590963c1ad5SBram Moolenaar let save_cpo = &cpo 591963c1ad5SBram Moolenaar set cpo&vim 592963c1ad5SBram Moolenaar 593e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 59471137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 595e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 59645d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 597e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 59860e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 59960e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 60060e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 601b3307b5eSBram Moolenaar 602b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 603b3307b5eSBram Moolenaar if s:way == 'prompt' 604b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 605b3307b5eSBram Moolenaar else 606b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 607b3307b5eSBram Moolenaar endif 608b3307b5eSBram Moolenaar 60945d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 61045d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 61145d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 612b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 61371137fedSBram Moolenaar command Winbar call s:InstallWinbar() 61445d5f26dSBram Moolenaar 61545d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 61645d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 6171b9645deSBram Moolenaar 618f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 61971137fedSBram Moolenaar call s:InstallWinbar() 62071137fedSBram Moolenaar 62171137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 62271137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 62371137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 62471137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 62571137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 62671137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 62771137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 62871137fedSBram Moolenaar endif 62971137fedSBram Moolenaar endif 630963c1ad5SBram Moolenaar 631963c1ad5SBram Moolenaar let &cpo = save_cpo 63271137fedSBram Moolenaarendfunc 63371137fedSBram Moolenaar 63471137fedSBram Moolenaarlet s:winbar_winids = [] 63571137fedSBram Moolenaar 63671137fedSBram Moolenaar" Install the window toolbar in the current window. 63771137fedSBram Moolenaarfunc s:InstallWinbar() 638c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 63924a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 64024a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 64124a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 64224a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 64360e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 64424a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 64571137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 646c4b533e1SBram Moolenaar endif 647e09ba7baSBram Moolenaarendfunc 648e09ba7baSBram Moolenaar 649e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 650e09ba7baSBram Moolenaarfunc s:DeleteCommands() 651e09ba7baSBram Moolenaar delcommand Break 65271137fedSBram Moolenaar delcommand Clear 653e09ba7baSBram Moolenaar delcommand Step 65445d5f26dSBram Moolenaar delcommand Over 655e09ba7baSBram Moolenaar delcommand Finish 65660e73f2aSBram Moolenaar delcommand Run 65760e73f2aSBram Moolenaar delcommand Arguments 65860e73f2aSBram Moolenaar delcommand Stop 659e09ba7baSBram Moolenaar delcommand Continue 66045d5f26dSBram Moolenaar delcommand Evaluate 66145d5f26dSBram Moolenaar delcommand Gdb 66245d5f26dSBram Moolenaar delcommand Program 663b3623a38SBram Moolenaar delcommand Source 66471137fedSBram Moolenaar delcommand Winbar 66545d5f26dSBram Moolenaar 66645d5f26dSBram Moolenaar nunmap K 6671b9645deSBram Moolenaar 6681b9645deSBram Moolenaar if has('menu') 66971137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 67071137fedSBram Moolenaar let curwinid = win_getid(winnr()) 67171137fedSBram Moolenaar for winid in s:winbar_winids 67271137fedSBram Moolenaar if win_gotoid(winid) 6731b9645deSBram Moolenaar aunmenu WinBar.Step 6741b9645deSBram Moolenaar aunmenu WinBar.Next 6751b9645deSBram Moolenaar aunmenu WinBar.Finish 6761b9645deSBram Moolenaar aunmenu WinBar.Cont 67760e73f2aSBram Moolenaar aunmenu WinBar.Stop 6781b9645deSBram Moolenaar aunmenu WinBar.Eval 6791b9645deSBram Moolenaar endif 68071137fedSBram Moolenaar endfor 68171137fedSBram Moolenaar call win_gotoid(curwinid) 68271137fedSBram Moolenaar let s:winbar_winids = [] 68371137fedSBram Moolenaar 68471137fedSBram Moolenaar if exists('s:saved_mousemodel') 68571137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 68671137fedSBram Moolenaar unlet s:saved_mousemodel 68771137fedSBram Moolenaar aunmenu PopUp.-SEP3- 68871137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 68971137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 69071137fedSBram Moolenaar aunmenu PopUp.Evaluate 69171137fedSBram Moolenaar endif 69271137fedSBram Moolenaar endif 6931b9645deSBram Moolenaar 69445d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 695*37402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 696*37402ed5SBram Moolenaar for subid in keys(entries) 697*37402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 698*37402ed5SBram Moolenaar endfor 69945d5f26dSBram Moolenaar endfor 70045d5f26dSBram Moolenaar unlet s:breakpoints 701*37402ed5SBram Moolenaar unlet s:breakpoint_locations 702a15b0a93SBram Moolenaar 703a15b0a93SBram Moolenaar sign undefine debugPC 704a15b0a93SBram Moolenaar for val in s:BreakpointSigns 705a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 706a15b0a93SBram Moolenaar endfor 7074551c0a9SBram Moolenaar let s:BreakpointSigns = [] 708e09ba7baSBram Moolenaarendfunc 709e09ba7baSBram Moolenaar 710e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 711e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 71260e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 71360e73f2aSBram Moolenaar " Interrupt to make it work. 71460e73f2aSBram Moolenaar let do_continue = 0 71560e73f2aSBram Moolenaar if !s:stopped 71660e73f2aSBram Moolenaar let do_continue = 1 717b3307b5eSBram Moolenaar if s:way == 'prompt' 7184551c0a9SBram Moolenaar call s:PromptInterrupt() 719b3307b5eSBram Moolenaar else 72060e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 721b3307b5eSBram Moolenaar endif 72260e73f2aSBram Moolenaar sleep 10m 72360e73f2aSBram Moolenaar endif 724a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 725a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 726a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 72760e73f2aSBram Moolenaar if do_continue 72860e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 72960e73f2aSBram Moolenaar endif 730e09ba7baSBram Moolenaarendfunc 731e09ba7baSBram Moolenaar 73271137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 73371137fedSBram Moolenaarfunc s:ClearBreakpoint() 734e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 735e09ba7baSBram Moolenaar let lnum = line('.') 736*37402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 737*37402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 738*37402ed5SBram Moolenaar let idx = 0 739*37402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 740*37402ed5SBram Moolenaar if has_key(s:breakpoints, id) 741*37402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 742*37402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 743*37402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 744*37402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 745*37402ed5SBram Moolenaar endfor 746*37402ed5SBram Moolenaar unlet s:breakpoints[id] 747*37402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 748e09ba7baSBram Moolenaar break 749*37402ed5SBram Moolenaar else 750*37402ed5SBram Moolenaar let idx += 1 751e09ba7baSBram Moolenaar endif 752e09ba7baSBram Moolenaar endfor 753*37402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 754*37402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 755*37402ed5SBram Moolenaar endif 756*37402ed5SBram Moolenaar endif 757e09ba7baSBram Moolenaarendfunc 758e09ba7baSBram Moolenaar 75960e73f2aSBram Moolenaarfunc s:Run(args) 76060e73f2aSBram Moolenaar if a:args != '' 76160e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 76260e73f2aSBram Moolenaar endif 76360e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 76460e73f2aSBram Moolenaarendfunc 76560e73f2aSBram Moolenaar 76651b0f370SBram Moolenaarfunc s:SendEval(expr) 76751b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 76851b0f370SBram Moolenaar let s:evalexpr = a:expr 76951b0f370SBram Moolenaarendfunc 77051b0f370SBram Moolenaar 77145d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 77245d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 77345d5f26dSBram Moolenaar if a:arg != '' 77445d5f26dSBram Moolenaar let expr = a:arg 77545d5f26dSBram Moolenaar elseif a:range == 2 77645d5f26dSBram Moolenaar let pos = getcurpos() 77745d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 77845d5f26dSBram Moolenaar let regt = getregtype('v') 77945d5f26dSBram Moolenaar normal! gv"vy 78045d5f26dSBram Moolenaar let expr = @v 78145d5f26dSBram Moolenaar call setpos('.', pos) 78245d5f26dSBram Moolenaar call setreg('v', reg, regt) 78345d5f26dSBram Moolenaar else 78445d5f26dSBram Moolenaar let expr = expand('<cexpr>') 78545d5f26dSBram Moolenaar endif 78622f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 78751b0f370SBram Moolenaar call s:SendEval(expr) 78845d5f26dSBram Moolenaarendfunc 78945d5f26dSBram Moolenaar 79022f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 79151b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 79251b0f370SBram Moolenaar 79345d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 79445d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 7951b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 7961b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 79751b0f370SBram Moolenaar if s:evalFromBalloonExpr 79851b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 79951b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 80051b0f370SBram Moolenaar else 80151b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 80251b0f370SBram Moolenaar endif 80351b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 80451b0f370SBram Moolenaar else 8051b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 80651b0f370SBram Moolenaar endif 8071b9645deSBram Moolenaar 8087f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 8091b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 81022f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 81151b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 81251b0f370SBram Moolenaar else 81351b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 8141b9645deSBram Moolenaar endif 81545d5f26dSBram Moolenaarendfunc 81645d5f26dSBram Moolenaar 81751b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 81851b0f370SBram Moolenaar" if there is any. 81951b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 820b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 821b3307b5eSBram Moolenaar return 822b3307b5eSBram Moolenaar endif 823b3307b5eSBram Moolenaar if !s:stopped 824b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 825b3307b5eSBram Moolenaar " mouse triggers a balloon. 82651b0f370SBram Moolenaar return 82751b0f370SBram Moolenaar endif 82851b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 82951b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 83022f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 83122f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 83251b0f370SBram Moolenaar return '' 83351b0f370SBram Moolenaarendfunc 83451b0f370SBram Moolenaar 83545d5f26dSBram Moolenaar" Handle an error. 83645d5f26dSBram Moolenaarfunc s:HandleError(msg) 83722f1d0e3SBram Moolenaar if s:ignoreEvalError 83851b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 83922f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 84022f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 84151b0f370SBram Moolenaar return 84251b0f370SBram Moolenaar endif 84345d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 84445d5f26dSBram Moolenaarendfunc 84545d5f26dSBram Moolenaar 846b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 847b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 848c4b533e1SBram Moolenaar new 849b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 850c4b533e1SBram Moolenaar call s:InstallWinbar() 851c4b533e1SBram Moolenaar endif 852c4b533e1SBram Moolenaarendfunc 853c4b533e1SBram Moolenaar 854e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 855e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 856e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 857fe386641SBram Moolenaar let wid = win_getid(winnr()) 858fe386641SBram Moolenaar 85960e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8604551c0a9SBram Moolenaar call ch_log('program stopped') 86160e73f2aSBram Moolenaar let s:stopped = 1 86260e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8634551c0a9SBram Moolenaar call ch_log('program running') 86460e73f2aSBram Moolenaar let s:stopped = 0 86560e73f2aSBram Moolenaar endif 86660e73f2aSBram Moolenaar 867a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 868a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 869a15b0a93SBram Moolenaar else 870a15b0a93SBram Moolenaar let fname = '' 871a15b0a93SBram Moolenaar endif 8721b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 873e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 874fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8754551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 8761b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 877fe386641SBram Moolenaar if &modified 878fe386641SBram Moolenaar " TODO: find existing window 879fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 880b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 881c4b533e1SBram Moolenaar call s:InstallWinbar() 882fe386641SBram Moolenaar else 883fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 884fe386641SBram Moolenaar endif 885fe386641SBram Moolenaar endif 886fe386641SBram Moolenaar exe lnum 88701164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 8881b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 889fe386641SBram Moolenaar setlocal signcolumn=yes 890fe386641SBram Moolenaar endif 8914551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 892fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 893fe386641SBram Moolenaar endif 894fe386641SBram Moolenaar 895fe386641SBram Moolenaar call win_gotoid(wid) 896e09ba7baSBram Moolenaarendfunc 897e09ba7baSBram Moolenaar 898de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 899a15b0a93SBram Moolenaar 900*37402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 901*37402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 902*37402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 903*37402ed5SBram Moolenaar call add(s:BreakpointSigns, nr) 904*37402ed5SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 905de1a8314SBram Moolenaar endif 906de1a8314SBram Moolenaarendfunc 907de1a8314SBram Moolenaar 908*37402ed5SBram Moolenaarfunc! s:SplitMsg(s) 909*37402ed5SBram Moolenaar return split(a:s, '{.\{-}}\zs') 9105378e1cfSBram Moolenaarendfunction 9115378e1cfSBram Moolenaar 912e09ba7baSBram Moolenaar" Handle setting a breakpoint 913e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 914e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 9156dccc962SBram Moolenaar if a:msg !~ 'fullname=' 9166dccc962SBram Moolenaar " a watch does not have a file name 9176dccc962SBram Moolenaar return 9186dccc962SBram Moolenaar endif 9195378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 9205378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 9215378e1cfSBram Moolenaar if empty(fname) 9225378e1cfSBram Moolenaar continue 9235378e1cfSBram Moolenaar endif 9245378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 9255378e1cfSBram Moolenaar if empty(nr) 926e09ba7baSBram Moolenaar return 927fe386641SBram Moolenaar endif 928e09ba7baSBram Moolenaar 929*37402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 930*37402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 931*37402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 932*37402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 933*37402ed5SBram Moolenaar 934*37402ed5SBram Moolenaar if has_key(s:breakpoints, id) 935*37402ed5SBram Moolenaar let entries = s:breakpoints[id] 936*37402ed5SBram Moolenaar else 937*37402ed5SBram Moolenaar let entries = {} 938*37402ed5SBram Moolenaar let s:breakpoints[id] = entries 939*37402ed5SBram Moolenaar endif 940*37402ed5SBram Moolenaar if has_key(entries, subid) 941*37402ed5SBram Moolenaar let entry = entries[subid] 942e09ba7baSBram Moolenaar else 943e09ba7baSBram Moolenaar let entry = {} 944*37402ed5SBram Moolenaar let entries[subid] = entry 945fe386641SBram Moolenaar endif 946e09ba7baSBram Moolenaar 9475378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 948e09ba7baSBram Moolenaar let entry['fname'] = fname 949e09ba7baSBram Moolenaar let entry['lnum'] = lnum 9501b9645deSBram Moolenaar 951*37402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 952*37402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 953*37402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 954*37402ed5SBram Moolenaar endif 955*37402ed5SBram Moolenaar let s:breakpoint_locations[bploc] += [id] 956*37402ed5SBram Moolenaar 9571b9645deSBram Moolenaar if bufloaded(fname) 958*37402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 9591b9645deSBram Moolenaar endif 9605378e1cfSBram Moolenaar endfor 9611b9645deSBram Moolenaarendfunc 9621b9645deSBram Moolenaar 963*37402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry) 964*37402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 965*37402ed5SBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname'] 9661b9645deSBram Moolenaar let a:entry['placed'] = 1 967e09ba7baSBram Moolenaarendfunc 968e09ba7baSBram Moolenaar 969e09ba7baSBram Moolenaar" Handle deleting a breakpoint 970e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 971e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 972*37402ed5SBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 973*37402ed5SBram Moolenaar if empty(id) 974e09ba7baSBram Moolenaar return 975e09ba7baSBram Moolenaar endif 976*37402ed5SBram Moolenaar if has_key(s:breakpoints, id) 977*37402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 9781b9645deSBram Moolenaar if has_key(entry, 'placed') 979*37402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 9801b9645deSBram Moolenaar unlet entry['placed'] 9811b9645deSBram Moolenaar endif 9825378e1cfSBram Moolenaar endfor 983*37402ed5SBram Moolenaar unlet s:breakpoints[id] 984*37402ed5SBram Moolenaar endif 985c572da5fSBram Moolenaarendfunc 9861b9645deSBram Moolenaar 9874551c0a9SBram Moolenaar" Handle the debugged program starting to run. 9884551c0a9SBram Moolenaar" Will store the process ID in s:pid 9894551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 9904551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 9914551c0a9SBram Moolenaar if nr == 0 9924551c0a9SBram Moolenaar return 9934551c0a9SBram Moolenaar endif 9944551c0a9SBram Moolenaar let s:pid = nr 9954551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 9964551c0a9SBram Moolenaarendfunc 9974551c0a9SBram Moolenaar 9981b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 9991b9645deSBram Moolenaarfunc s:BufRead() 10001b9645deSBram Moolenaar let fname = expand('<afile>:p') 1001*37402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 1002*37402ed5SBram Moolenaar for [subid, entry] in items(entries) 10031b9645deSBram Moolenaar if entry['fname'] == fname 1004*37402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10051b9645deSBram Moolenaar endif 10061b9645deSBram Moolenaar endfor 1007*37402ed5SBram Moolenaar endfor 10081b9645deSBram Moolenaarendfunc 10091b9645deSBram Moolenaar 10101b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 10111b9645deSBram Moolenaarfunc s:BufUnloaded() 10121b9645deSBram Moolenaar let fname = expand('<afile>:p') 1013*37402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 1014*37402ed5SBram Moolenaar for [subid, entry] in items(entries) 10151b9645deSBram Moolenaar if entry['fname'] == fname 10161b9645deSBram Moolenaar let entry['placed'] = 0 10171b9645deSBram Moolenaar endif 10181b9645deSBram Moolenaar endfor 1019*37402ed5SBram Moolenaar endfor 10201b9645deSBram Moolenaarendfunc 1021ca4cc018SBram Moolenaar 1022ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 1023ca4cc018SBram Moolenaarunlet s:keepcpo 1024