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 76*5378e1cfSBram Moolenaar" Take a breakpoint number as used by GDB and turn it into an integer. 77*5378e1cfSBram Moolenaar" The breakpoint may contain a dot: 123.4 78*5378e1cfSBram Moolenaarfunc s:Breakpoint2SignNumber(nr) 79*5378e1cfSBram Moolenaar let t = split(a:nr, '\.') 80*5378e1cfSBram Moolenaar return t[0] * 1000 + (len(t) == 2 ? t[1] : 0) 81*5378e1cfSBram Moolenaarendfunction 82*5378e1cfSBram 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 363*5378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 364*5378e1cfSBram Moolenaar " breakpoint number. 365e09ba7baSBram Moolenaar let s:breakpoints = {} 3661b9645deSBram Moolenaar 3671b9645deSBram Moolenaar augroup TermDebug 3681b9645deSBram Moolenaar au BufRead * call s:BufRead() 3691b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 370f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 3711b9645deSBram Moolenaar augroup END 37232c67ba7SBram Moolenaar 373b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 374b3307b5eSBram Moolenaar " window. 37532c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 37632c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 37732c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 37832c67ba7SBram Moolenaar endif 379c572da5fSBram Moolenaarendfunc 380c572da5fSBram Moolenaar 381b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 382b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 383b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 384b3307b5eSBram Moolenaar if s:way == 'prompt' 385b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 386b3307b5eSBram Moolenaar else 387b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 388b3307b5eSBram Moolenaar endif 389b3307b5eSBram Moolenaarendfunc 390b3307b5eSBram Moolenaar 391b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 392b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 393b3307b5eSBram Moolenaar if s:way == 'prompt' 394b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 395b3307b5eSBram Moolenaar else 396b3307b5eSBram Moolenaar let do_continue = 0 397b3307b5eSBram Moolenaar if !s:stopped 398b3307b5eSBram Moolenaar let do_continue = 1 399b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 400b3307b5eSBram Moolenaar sleep 10m 401b3307b5eSBram Moolenaar endif 402b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 403b3307b5eSBram Moolenaar if do_continue 404b3307b5eSBram Moolenaar Continue 405b3307b5eSBram Moolenaar endif 406b3307b5eSBram Moolenaar endif 407b3307b5eSBram Moolenaarendfunc 408b3307b5eSBram Moolenaar 409b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 410b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 411b3307b5eSBram Moolenaar call s:SendCommand(a:text) 412b3307b5eSBram Moolenaarendfunc 413b3307b5eSBram Moolenaar 4144551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4154551c0a9SBram Moolenaar" breakpoint. 416b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4172ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4182ed890f1SBram Moolenaar if has('win32') 4192ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4202ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4214551c0a9SBram Moolenaar if s:pid == 0 4224551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4234551c0a9SBram Moolenaar else 4244551c0a9SBram Moolenaar call debugbreak(s:pid) 4254551c0a9SBram Moolenaar endif 4262ed890f1SBram Moolenaar else 4272ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4282ed890f1SBram Moolenaar endif 429b3307b5eSBram Moolenaarendfunc 430b3307b5eSBram Moolenaar 431b3307b5eSBram Moolenaar" Function called when gdb outputs text. 432b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 433b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 434b3307b5eSBram Moolenaar 435b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 436b3307b5eSBram Moolenaar " Drop status and echo'd commands. 437a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 438b3307b5eSBram Moolenaar return 439b3307b5eSBram Moolenaar endif 440b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 441b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 442b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 443b3307b5eSBram Moolenaar " Silently drop evaluation errors. 444b3307b5eSBram Moolenaar unlet s:evalexpr 445b3307b5eSBram Moolenaar return 446b3307b5eSBram Moolenaar endif 447b3307b5eSBram Moolenaar elseif a:text[0] == '~' 448b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 449b3307b5eSBram Moolenaar else 450b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 451b3307b5eSBram Moolenaar return 452b3307b5eSBram Moolenaar endif 453b3307b5eSBram Moolenaar 454b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 455b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 456b3307b5eSBram Moolenaar 457b3307b5eSBram Moolenaar " Add the output above the current prompt. 458b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4594551c0a9SBram Moolenaar set modified 460b3307b5eSBram Moolenaar 461b3307b5eSBram Moolenaar call win_gotoid(curwinid) 462b3307b5eSBram Moolenaarendfunc 463b3307b5eSBram Moolenaar 464b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 465b3307b5eSBram Moolenaar" to the next ", unescaping characters. 466b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 467b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 468a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 469b3307b5eSBram Moolenaar return 470b3307b5eSBram Moolenaar endif 471b3307b5eSBram Moolenaar let result = '' 472b3307b5eSBram Moolenaar let i = 1 473b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 474b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 475b3307b5eSBram Moolenaar let i += 1 476b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 477b3307b5eSBram Moolenaar " drop \n 478b3307b5eSBram Moolenaar let i += 1 479b3307b5eSBram Moolenaar continue 480b3307b5eSBram Moolenaar endif 481b3307b5eSBram Moolenaar endif 482b3307b5eSBram Moolenaar let result .= a:quotedText[i] 483b3307b5eSBram Moolenaar let i += 1 484b3307b5eSBram Moolenaar endwhile 485b3307b5eSBram Moolenaar return result 486b3307b5eSBram Moolenaarendfunc 487b3307b5eSBram Moolenaar 488a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 489a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 490*5378e1cfSBram Moolenaar if a:msg !~ 'fullname' 491*5378e1cfSBram Moolenaar return '' 492*5378e1cfSBram Moolenaar endif 493a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 494a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 495a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 496a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 497a15b0a93SBram Moolenaar endif 498a15b0a93SBram Moolenaar return name 499a15b0a93SBram Moolenaarendfunc 500a15b0a93SBram Moolenaar 501b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 502fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 503b3623a38SBram Moolenaar unlet s:gdbwin 504e09ba7baSBram Moolenaar 505b3307b5eSBram Moolenaar call s:EndDebugCommon() 506b3307b5eSBram Moolenaarendfunc 507b3307b5eSBram Moolenaar 508b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 509e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 510e09ba7baSBram Moolenaar 511b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 512b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 513b3307b5eSBram Moolenaar endif 514b3307b5eSBram Moolenaar 515b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 516e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 517e09ba7baSBram Moolenaar call s:DeleteCommands() 518e09ba7baSBram Moolenaar 519e09ba7baSBram Moolenaar call win_gotoid(curwinid) 520b3307b5eSBram Moolenaar 52138baa3e6SBram Moolenaar if s:save_columns > 0 52238baa3e6SBram Moolenaar let &columns = s:save_columns 52338baa3e6SBram Moolenaar endif 5241b9645deSBram Moolenaar 525246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 526246fe03dSBram Moolenaar set balloonexpr= 52751b0f370SBram Moolenaar if has("balloon_eval") 52851b0f370SBram Moolenaar set noballooneval 529246fe03dSBram Moolenaar endif 53051b0f370SBram Moolenaar if has("balloon_eval_term") 53151b0f370SBram Moolenaar set noballoonevalterm 53251b0f370SBram Moolenaar endif 53351b0f370SBram Moolenaar endif 53451b0f370SBram Moolenaar 5351b9645deSBram Moolenaar au! TermDebug 536fe386641SBram Moolenaarendfunc 537fe386641SBram Moolenaar 538b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 539b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 540b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5414551c0a9SBram Moolenaar set nomodified 542b3307b5eSBram Moolenaar close 543b3307b5eSBram Moolenaar if curwinid != s:gdbwin 544b3307b5eSBram Moolenaar call win_gotoid(curwinid) 545b3307b5eSBram Moolenaar endif 546b3307b5eSBram Moolenaar 547b3307b5eSBram Moolenaar call s:EndDebugCommon() 548b3307b5eSBram Moolenaar unlet s:gdbwin 549b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 550b3307b5eSBram Moolenaarendfunc 551b3307b5eSBram Moolenaar 552fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 553fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 554fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 555fe386641SBram Moolenaar 556fe386641SBram Moolenaar for msg in msgs 557fe386641SBram Moolenaar " remove prefixed NL 558fe386641SBram Moolenaar if msg[0] == "\n" 559fe386641SBram Moolenaar let msg = msg[1:] 560fe386641SBram Moolenaar endif 561fe386641SBram Moolenaar if msg != '' 5621b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 563e09ba7baSBram Moolenaar call s:HandleCursor(msg) 56445d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 565e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 566e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 567e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 5684551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 5694551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 57045d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 57145d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 57245d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 57345d5f26dSBram Moolenaar call s:HandleError(msg) 574e09ba7baSBram Moolenaar endif 575e09ba7baSBram Moolenaar endif 576e09ba7baSBram Moolenaar endfor 577e09ba7baSBram Moolenaarendfunc 578e09ba7baSBram Moolenaar 579e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 580e09ba7baSBram Moolenaarfunc s:InstallCommands() 581963c1ad5SBram Moolenaar let save_cpo = &cpo 582963c1ad5SBram Moolenaar set cpo&vim 583963c1ad5SBram Moolenaar 584e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 58571137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 586e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 58745d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 588e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 58960e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 59060e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 59160e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 592b3307b5eSBram Moolenaar 593b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 594b3307b5eSBram Moolenaar if s:way == 'prompt' 595b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 596b3307b5eSBram Moolenaar else 597b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 598b3307b5eSBram Moolenaar endif 599b3307b5eSBram Moolenaar 60045d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 60145d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 60245d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 603b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 60471137fedSBram Moolenaar command Winbar call s:InstallWinbar() 60545d5f26dSBram Moolenaar 60645d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 60745d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 6081b9645deSBram Moolenaar 609f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 61071137fedSBram Moolenaar call s:InstallWinbar() 61171137fedSBram Moolenaar 61271137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 61371137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 61471137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 61571137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 61671137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 61771137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 61871137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 61971137fedSBram Moolenaar endif 62071137fedSBram Moolenaar endif 621963c1ad5SBram Moolenaar 622963c1ad5SBram Moolenaar let &cpo = save_cpo 62371137fedSBram Moolenaarendfunc 62471137fedSBram Moolenaar 62571137fedSBram Moolenaarlet s:winbar_winids = [] 62671137fedSBram Moolenaar 62771137fedSBram Moolenaar" Install the window toolbar in the current window. 62871137fedSBram Moolenaarfunc s:InstallWinbar() 629c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 63024a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 63124a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 63224a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 63324a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 63460e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 63524a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 63671137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 637c4b533e1SBram Moolenaar endif 638e09ba7baSBram Moolenaarendfunc 639e09ba7baSBram Moolenaar 640e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 641e09ba7baSBram Moolenaarfunc s:DeleteCommands() 642e09ba7baSBram Moolenaar delcommand Break 64371137fedSBram Moolenaar delcommand Clear 644e09ba7baSBram Moolenaar delcommand Step 64545d5f26dSBram Moolenaar delcommand Over 646e09ba7baSBram Moolenaar delcommand Finish 64760e73f2aSBram Moolenaar delcommand Run 64860e73f2aSBram Moolenaar delcommand Arguments 64960e73f2aSBram Moolenaar delcommand Stop 650e09ba7baSBram Moolenaar delcommand Continue 65145d5f26dSBram Moolenaar delcommand Evaluate 65245d5f26dSBram Moolenaar delcommand Gdb 65345d5f26dSBram Moolenaar delcommand Program 654b3623a38SBram Moolenaar delcommand Source 65571137fedSBram Moolenaar delcommand Winbar 65645d5f26dSBram Moolenaar 65745d5f26dSBram Moolenaar nunmap K 6581b9645deSBram Moolenaar 6591b9645deSBram Moolenaar if has('menu') 66071137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 66171137fedSBram Moolenaar let curwinid = win_getid(winnr()) 66271137fedSBram Moolenaar for winid in s:winbar_winids 66371137fedSBram Moolenaar if win_gotoid(winid) 6641b9645deSBram Moolenaar aunmenu WinBar.Step 6651b9645deSBram Moolenaar aunmenu WinBar.Next 6661b9645deSBram Moolenaar aunmenu WinBar.Finish 6671b9645deSBram Moolenaar aunmenu WinBar.Cont 66860e73f2aSBram Moolenaar aunmenu WinBar.Stop 6691b9645deSBram Moolenaar aunmenu WinBar.Eval 6701b9645deSBram Moolenaar endif 67171137fedSBram Moolenaar endfor 67271137fedSBram Moolenaar call win_gotoid(curwinid) 67371137fedSBram Moolenaar let s:winbar_winids = [] 67471137fedSBram Moolenaar 67571137fedSBram Moolenaar if exists('s:saved_mousemodel') 67671137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 67771137fedSBram Moolenaar unlet s:saved_mousemodel 67871137fedSBram Moolenaar aunmenu PopUp.-SEP3- 67971137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 68071137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 68171137fedSBram Moolenaar aunmenu PopUp.Evaluate 68271137fedSBram Moolenaar endif 68371137fedSBram Moolenaar endif 6841b9645deSBram Moolenaar 68545d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 68645d5f26dSBram Moolenaar for key in keys(s:breakpoints) 687*5378e1cfSBram Moolenaar exe 'sign unplace ' . (s:break_id + s:Breakpoint2SignNumber(key)) 68845d5f26dSBram Moolenaar endfor 68945d5f26dSBram Moolenaar unlet s:breakpoints 690a15b0a93SBram Moolenaar 691a15b0a93SBram Moolenaar sign undefine debugPC 692a15b0a93SBram Moolenaar for val in s:BreakpointSigns 693a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 694a15b0a93SBram Moolenaar endfor 6954551c0a9SBram Moolenaar let s:BreakpointSigns = [] 696e09ba7baSBram Moolenaarendfunc 697e09ba7baSBram Moolenaar 698e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 699e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 70060e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 70160e73f2aSBram Moolenaar " Interrupt to make it work. 70260e73f2aSBram Moolenaar let do_continue = 0 70360e73f2aSBram Moolenaar if !s:stopped 70460e73f2aSBram Moolenaar let do_continue = 1 705b3307b5eSBram Moolenaar if s:way == 'prompt' 7064551c0a9SBram Moolenaar call s:PromptInterrupt() 707b3307b5eSBram Moolenaar else 70860e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 709b3307b5eSBram Moolenaar endif 71060e73f2aSBram Moolenaar sleep 10m 71160e73f2aSBram Moolenaar endif 712a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 713a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 714a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 71560e73f2aSBram Moolenaar if do_continue 71660e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 71760e73f2aSBram Moolenaar endif 718e09ba7baSBram Moolenaarendfunc 719e09ba7baSBram Moolenaar 72071137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 72171137fedSBram Moolenaarfunc s:ClearBreakpoint() 722e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 723e09ba7baSBram Moolenaar let lnum = line('.') 724e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 725e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 726b3307b5eSBram Moolenaar call s:SendCommand('-break-delete ' . key) 727e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 728*5378e1cfSBram Moolenaar exe 'sign unplace ' . (s:break_id + s:Breakpoint2SignNumber(key)) 729e09ba7baSBram Moolenaar unlet s:breakpoints[key] 730e09ba7baSBram Moolenaar break 731e09ba7baSBram Moolenaar endif 732e09ba7baSBram Moolenaar endfor 733e09ba7baSBram Moolenaarendfunc 734e09ba7baSBram Moolenaar 73560e73f2aSBram Moolenaarfunc s:Run(args) 73660e73f2aSBram Moolenaar if a:args != '' 73760e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 73860e73f2aSBram Moolenaar endif 73960e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 74060e73f2aSBram Moolenaarendfunc 74160e73f2aSBram Moolenaar 74251b0f370SBram Moolenaarfunc s:SendEval(expr) 74351b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 74451b0f370SBram Moolenaar let s:evalexpr = a:expr 74551b0f370SBram Moolenaarendfunc 74651b0f370SBram Moolenaar 74745d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 74845d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 74945d5f26dSBram Moolenaar if a:arg != '' 75045d5f26dSBram Moolenaar let expr = a:arg 75145d5f26dSBram Moolenaar elseif a:range == 2 75245d5f26dSBram Moolenaar let pos = getcurpos() 75345d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 75445d5f26dSBram Moolenaar let regt = getregtype('v') 75545d5f26dSBram Moolenaar normal! gv"vy 75645d5f26dSBram Moolenaar let expr = @v 75745d5f26dSBram Moolenaar call setpos('.', pos) 75845d5f26dSBram Moolenaar call setreg('v', reg, regt) 75945d5f26dSBram Moolenaar else 76045d5f26dSBram Moolenaar let expr = expand('<cexpr>') 76145d5f26dSBram Moolenaar endif 76222f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 76351b0f370SBram Moolenaar call s:SendEval(expr) 76445d5f26dSBram Moolenaarendfunc 76545d5f26dSBram Moolenaar 76622f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 76751b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 76851b0f370SBram Moolenaar 76945d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 77045d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 7711b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 7721b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 77351b0f370SBram Moolenaar if s:evalFromBalloonExpr 77451b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 77551b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 77651b0f370SBram Moolenaar else 77751b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 77851b0f370SBram Moolenaar endif 77951b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 78051b0f370SBram Moolenaar else 7811b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 78251b0f370SBram Moolenaar endif 7831b9645deSBram Moolenaar 7847f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 7851b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 78622f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 78751b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 78851b0f370SBram Moolenaar else 78951b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 7901b9645deSBram Moolenaar endif 79145d5f26dSBram Moolenaarendfunc 79245d5f26dSBram Moolenaar 79351b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 79451b0f370SBram Moolenaar" if there is any. 79551b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 796b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 797b3307b5eSBram Moolenaar return 798b3307b5eSBram Moolenaar endif 799b3307b5eSBram Moolenaar if !s:stopped 800b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 801b3307b5eSBram Moolenaar " mouse triggers a balloon. 80251b0f370SBram Moolenaar return 80351b0f370SBram Moolenaar endif 80451b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 80551b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 80622f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 80722f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 80851b0f370SBram Moolenaar return '' 80951b0f370SBram Moolenaarendfunc 81051b0f370SBram Moolenaar 81145d5f26dSBram Moolenaar" Handle an error. 81245d5f26dSBram Moolenaarfunc s:HandleError(msg) 81322f1d0e3SBram Moolenaar if s:ignoreEvalError 81451b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 81522f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 81622f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 81751b0f370SBram Moolenaar return 81851b0f370SBram Moolenaar endif 81945d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 82045d5f26dSBram Moolenaarendfunc 82145d5f26dSBram Moolenaar 822b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 823b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 824c4b533e1SBram Moolenaar new 825b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 826c4b533e1SBram Moolenaar call s:InstallWinbar() 827c4b533e1SBram Moolenaar endif 828c4b533e1SBram Moolenaarendfunc 829c4b533e1SBram Moolenaar 830e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 831e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 832e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 833fe386641SBram Moolenaar let wid = win_getid(winnr()) 834fe386641SBram Moolenaar 83560e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8364551c0a9SBram Moolenaar call ch_log('program stopped') 83760e73f2aSBram Moolenaar let s:stopped = 1 83860e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8394551c0a9SBram Moolenaar call ch_log('program running') 84060e73f2aSBram Moolenaar let s:stopped = 0 84160e73f2aSBram Moolenaar endif 84260e73f2aSBram Moolenaar 843a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 844a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 845a15b0a93SBram Moolenaar else 846a15b0a93SBram Moolenaar let fname = '' 847a15b0a93SBram Moolenaar endif 8481b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 849e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 850fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8514551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 8521b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 853fe386641SBram Moolenaar if &modified 854fe386641SBram Moolenaar " TODO: find existing window 855fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 856b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 857c4b533e1SBram Moolenaar call s:InstallWinbar() 858fe386641SBram Moolenaar else 859fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 860fe386641SBram Moolenaar endif 861fe386641SBram Moolenaar endif 862fe386641SBram Moolenaar exe lnum 86301164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 8641b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 865fe386641SBram Moolenaar setlocal signcolumn=yes 866fe386641SBram Moolenaar endif 8674551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 868fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 869fe386641SBram Moolenaar endif 870fe386641SBram Moolenaar 871fe386641SBram Moolenaar call win_gotoid(wid) 872e09ba7baSBram Moolenaarendfunc 873e09ba7baSBram Moolenaar 874de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 875a15b0a93SBram Moolenaar 876a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr) 877de1a8314SBram Moolenaar if index(s:BreakpointSigns, a:nr) == -1 878de1a8314SBram Moolenaar call add(s:BreakpointSigns, a:nr) 879*5378e1cfSBram Moolenaar exe "sign define debugBreakpoint" . a:nr . " text=" . substitute(a:nr, '\..*', '', '') . " texthl=debugBreakpoint" 880de1a8314SBram Moolenaar endif 881de1a8314SBram Moolenaarendfunc 882de1a8314SBram Moolenaar 883*5378e1cfSBram Moolenaarfunc s:SplitMsg(s) 884*5378e1cfSBram Moolenaar return split(a:s, '{\%([a-z-]\+=[^,]\+,*\)\+}\zs') 885*5378e1cfSBram Moolenaarendfunction 886*5378e1cfSBram Moolenaar 887e09ba7baSBram Moolenaar" Handle setting a breakpoint 888e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 889e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 8906dccc962SBram Moolenaar if a:msg !~ 'fullname=' 8916dccc962SBram Moolenaar " a watch does not have a file name 8926dccc962SBram Moolenaar return 8936dccc962SBram Moolenaar endif 894*5378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 895*5378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 896*5378e1cfSBram Moolenaar if empty(fname) 897*5378e1cfSBram Moolenaar continue 898*5378e1cfSBram Moolenaar endif 899*5378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 900*5378e1cfSBram Moolenaar if empty(nr) 901e09ba7baSBram Moolenaar return 902fe386641SBram Moolenaar endif 903de1a8314SBram Moolenaar call s:CreateBreakpoint(nr) 904e09ba7baSBram Moolenaar 905e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 906e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 907e09ba7baSBram Moolenaar else 908e09ba7baSBram Moolenaar let entry = {} 909e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 910fe386641SBram Moolenaar endif 911e09ba7baSBram Moolenaar 912*5378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 913e09ba7baSBram Moolenaar let entry['fname'] = fname 914e09ba7baSBram Moolenaar let entry['lnum'] = lnum 9151b9645deSBram Moolenaar 9161b9645deSBram Moolenaar if bufloaded(fname) 9171b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 9181b9645deSBram Moolenaar endif 919*5378e1cfSBram Moolenaar endfor 9201b9645deSBram Moolenaarendfunc 9211b9645deSBram Moolenaar 9221b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 923*5378e1cfSBram Moolenaar exe 'sign place ' . (s:break_id + s:Breakpoint2SignNumber(a:nr)) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname'] 9241b9645deSBram Moolenaar let a:entry['placed'] = 1 925e09ba7baSBram Moolenaarendfunc 926e09ba7baSBram Moolenaar 927e09ba7baSBram Moolenaar" Handle deleting a breakpoint 928e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 929e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 930*5378e1cfSBram Moolenaar let key = substitute(a:msg, '.*id="\([0-9.]*\)\".*', '\1', '') 931*5378e1cfSBram Moolenaar if empty(key) 932e09ba7baSBram Moolenaar return 933e09ba7baSBram Moolenaar endif 934*5378e1cfSBram Moolenaar for [nr, entry] in items(s:breakpoints) 935*5378e1cfSBram Moolenaar if stridx(nr, key) != 0 936*5378e1cfSBram Moolenaar continue 937*5378e1cfSBram Moolenaar endif 9381b9645deSBram Moolenaar let entry = s:breakpoints[nr] 9391b9645deSBram Moolenaar if has_key(entry, 'placed') 940*5378e1cfSBram Moolenaar exe 'sign unplace ' . (s:break_id + s:Breakpoint2SignNumber(nr)) 9411b9645deSBram Moolenaar unlet entry['placed'] 9421b9645deSBram Moolenaar endif 943e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 944*5378e1cfSBram Moolenaar endfor 945c572da5fSBram Moolenaarendfunc 9461b9645deSBram Moolenaar 9474551c0a9SBram Moolenaar" Handle the debugged program starting to run. 9484551c0a9SBram Moolenaar" Will store the process ID in s:pid 9494551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 9504551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 9514551c0a9SBram Moolenaar if nr == 0 9524551c0a9SBram Moolenaar return 9534551c0a9SBram Moolenaar endif 9544551c0a9SBram Moolenaar let s:pid = nr 9554551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 9564551c0a9SBram Moolenaarendfunc 9574551c0a9SBram Moolenaar 9581b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 9591b9645deSBram Moolenaarfunc s:BufRead() 9601b9645deSBram Moolenaar let fname = expand('<afile>:p') 9611b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9621b9645deSBram Moolenaar if entry['fname'] == fname 9631b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 9641b9645deSBram Moolenaar endif 9651b9645deSBram Moolenaar endfor 9661b9645deSBram Moolenaarendfunc 9671b9645deSBram Moolenaar 9681b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 9691b9645deSBram Moolenaarfunc s:BufUnloaded() 9701b9645deSBram Moolenaar let fname = expand('<afile>:p') 9711b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9721b9645deSBram Moolenaar if entry['fname'] == fname 9731b9645deSBram Moolenaar let entry['placed'] = 0 9741b9645deSBram Moolenaar endif 9751b9645deSBram Moolenaar endfor 9761b9645deSBram Moolenaarendfunc 977ca4cc018SBram Moolenaar 978ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 979ca4cc018SBram Moolenaarunlet s:keepcpo 980