1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3b3307b5eSBram Moolenaar" Author: Bram Moolenaar 4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license" 5*469bdbdeSBram Moolenaar" Last Change: 2019 Dec 11 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". 6818223a59SBram Moolenaarif !exists('g:termdebugger') 6918223a59SBram Moolenaar let g:termdebugger = 'gdb' 70c572da5fSBram Moolenaarendif 71c572da5fSBram Moolenaar 72fe386641SBram Moolenaarlet s:pc_id = 12 73de1a8314SBram Moolenaarlet s:break_id = 13 " breakpoint number is added to this 7460e73f2aSBram Moolenaarlet s:stopped = 1 75e09ba7baSBram Moolenaar 765378e1cfSBram Moolenaar" Take a breakpoint number as used by GDB and turn it into an integer. 7737402ed5SBram Moolenaar" The breakpoint may contain a dot: 123.4 -> 123004 7837402ed5SBram Moolenaar" The main breakpoint has a zero subid. 7937402ed5SBram Moolenaarfunc s:Breakpoint2SignNumber(id, subid) 8037402ed5SBram Moolenaar return s:break_id + a:id * 1000 + a:subid 815378e1cfSBram Moolenaarendfunction 825378e1cfSBram Moolenaar 83f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new) 84f07f9e73SBram Moolenaar let default = a:init ? 'default ' : '' 85f07f9e73SBram Moolenaar if a:new ==# 'light' && a:old !=# 'light' 86f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" 87f07f9e73SBram Moolenaar elseif a:new ==# 'dark' && a:old !=# 'dark' 88f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue" 89e09ba7baSBram Moolenaar endif 90f07f9e73SBram Moolenaarendfunc 91f07f9e73SBram Moolenaar 92f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background) 93e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 94fe386641SBram Moolenaar 9532c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 9632c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 9732c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 9832c67ba7SBram Moolenaarendfunc 9932c67ba7SBram Moolenaar 10032c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 10132c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 10232c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 10332c67ba7SBram Moolenaarendfunc 10432c67ba7SBram Moolenaar 10532c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 106b3623a38SBram Moolenaar if exists('s:gdbwin') 10718223a59SBram Moolenaar echoerr 'Terminal debugger already running, cannot run two' 108b3623a38SBram Moolenaar return 109b3623a38SBram Moolenaar endif 11018223a59SBram Moolenaar if !executable(g:termdebugger) 11118223a59SBram Moolenaar echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"' 11218223a59SBram Moolenaar return 11318223a59SBram Moolenaar endif 11418223a59SBram Moolenaar 115b3307b5eSBram Moolenaar let s:ptywin = 0 1164551c0a9SBram Moolenaar let s:pid = 0 117b3623a38SBram Moolenaar 118b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 119b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 120b3307b5eSBram Moolenaar 121b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 122fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 123fe386641SBram Moolenaar 12424a98a0eSBram Moolenaar let s:save_columns = 0 12568e6560bSBram Moolenaar let s:allleft = 0 12624a98a0eSBram Moolenaar if exists('g:termdebug_wide') 12724a98a0eSBram Moolenaar if &columns < g:termdebug_wide 12838baa3e6SBram Moolenaar let s:save_columns = &columns 12938baa3e6SBram Moolenaar let &columns = g:termdebug_wide 13068e6560bSBram Moolenaar " If we make the Vim window wider, use the whole left halve for the debug 13168e6560bSBram Moolenaar " windows. 13268e6560bSBram Moolenaar let s:allleft = 1 13324a98a0eSBram Moolenaar endif 134b3307b5eSBram Moolenaar let s:vertical = 1 13538baa3e6SBram Moolenaar else 136b3307b5eSBram Moolenaar let s:vertical = 0 13738baa3e6SBram Moolenaar endif 13838baa3e6SBram Moolenaar 139b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 140b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 141b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 142b3307b5eSBram Moolenaar let s:way = 'terminal' 143b3307b5eSBram Moolenaar else 144b3307b5eSBram Moolenaar let s:way = 'prompt' 145b3307b5eSBram Moolenaar endif 146b3307b5eSBram Moolenaar 147b3307b5eSBram Moolenaar if s:way == 'prompt' 148b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 149b3307b5eSBram Moolenaar else 150b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 151b3307b5eSBram Moolenaar endif 152b3307b5eSBram Moolenaarendfunc 153b3307b5eSBram Moolenaar 154ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended. 155ef3c6a5bSBram Moolenaarfunc s:CloseBuffers() 156ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:ptybuf 157ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:commbuf 158ef3c6a5bSBram Moolenaar unlet! s:gdbwin 159ef3c6a5bSBram Moolenaarendfunc 160ef3c6a5bSBram Moolenaar 161b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 162b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 163fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 164b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 165b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 166fe386641SBram Moolenaar \ }) 167fe386641SBram Moolenaar if s:ptybuf == 0 168fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 169fe386641SBram Moolenaar return 170fe386641SBram Moolenaar endif 171fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 17245d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 173b3307b5eSBram Moolenaar if s:vertical 17451b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 17551b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 17651b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 17768e6560bSBram Moolenaar if s:allleft 17868e6560bSBram Moolenaar " use the whole left column 17968e6560bSBram Moolenaar wincmd H 18068e6560bSBram Moolenaar endif 18151b0f370SBram Moolenaar endif 182fe386641SBram Moolenaar 183fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 184fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 185fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 186fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 187fe386641SBram Moolenaar \ 'hidden': 1, 188fe386641SBram Moolenaar \ }) 189fe386641SBram Moolenaar if s:commbuf == 0 190fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 191fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 192fe386641SBram Moolenaar return 193fe386641SBram Moolenaar endif 194fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 195c572da5fSBram Moolenaar 196c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 197c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 19832c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 19932c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 20032c67ba7SBram Moolenaar 20132c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 202b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 20360e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 204fe386641SBram Moolenaar \ 'term_finish': 'close', 205c572da5fSBram Moolenaar \ }) 20660e73f2aSBram Moolenaar if s:gdbbuf == 0 207fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 208ef3c6a5bSBram Moolenaar call s:CloseBuffers() 209fe386641SBram Moolenaar return 210fe386641SBram Moolenaar endif 21145d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 212fe386641SBram Moolenaar 21332c67ba7SBram Moolenaar " Set arguments to be run 21432c67ba7SBram Moolenaar if len(proc_args) 21532c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 21632c67ba7SBram Moolenaar endif 21732c67ba7SBram Moolenaar 218fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 21960e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 22060e73f2aSBram Moolenaar 2213e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 2223e4b84d0SBram Moolenaar " why the debugger doesn't work. 2233e4b84d0SBram Moolenaar let try_count = 0 2243e4b84d0SBram Moolenaar while 1 225ef3c6a5bSBram Moolenaar let gdbproc = term_getjob(s:gdbbuf) 226ef3c6a5bSBram Moolenaar if gdbproc == v:null || job_status(gdbproc) !=# 'run' 227ef3c6a5bSBram Moolenaar echoerr string(g:termdebugger) . ' exited unexpectedly' 228ef3c6a5bSBram Moolenaar call s:CloseBuffers() 229ef3c6a5bSBram Moolenaar return 230ef3c6a5bSBram Moolenaar endif 231ef3c6a5bSBram Moolenaar 2323e4b84d0SBram Moolenaar let response = '' 233b3623a38SBram Moolenaar for lnum in range(1, 200) 23419c8fe19SBram Moolenaar let line1 = term_getline(s:gdbbuf, lnum) 23519c8fe19SBram Moolenaar let line2 = term_getline(s:gdbbuf, lnum + 1) 23619c8fe19SBram Moolenaar if line1 =~ 'new-ui mi ' 237f63db65bSBram Moolenaar " response can be in the same line or the next line 23819c8fe19SBram Moolenaar let response = line1 . line2 2393e4b84d0SBram Moolenaar if response =~ 'Undefined command' 240f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 241ef3c6a5bSBram Moolenaar call s:CloseBuffers() 2423e4b84d0SBram Moolenaar return 2433e4b84d0SBram Moolenaar endif 2443e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2453e4b84d0SBram Moolenaar " Success! 2463e4b84d0SBram Moolenaar break 2473e4b84d0SBram Moolenaar endif 24819c8fe19SBram Moolenaar elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' 24919c8fe19SBram Moolenaar " Reading symbols might take a while, try more times 25006fe74aeSBram Moolenaar let try_count -= 1 25106fe74aeSBram Moolenaar endif 2523e4b84d0SBram Moolenaar endfor 2533e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2543e4b84d0SBram Moolenaar break 2553e4b84d0SBram Moolenaar endif 2563e4b84d0SBram Moolenaar let try_count += 1 2573e4b84d0SBram Moolenaar if try_count > 100 2583e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2593e4b84d0SBram Moolenaar break 2603e4b84d0SBram Moolenaar endif 2613e4b84d0SBram Moolenaar sleep 10m 2623e4b84d0SBram Moolenaar endwhile 2633e4b84d0SBram Moolenaar 26491359014SBram Moolenaar " Interpret commands while the target is running. This should usually only be 26560e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 26660e73f2aSBram Moolenaar " running. 26760e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 268b3307b5eSBram Moolenaar " Older gdb uses a different command. 269b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 270e09ba7baSBram Moolenaar 271f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 272f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 273b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 274f3ba14ffSBram Moolenaar 275ef3c6a5bSBram Moolenaar call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')}) 276b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 277b3307b5eSBram Moolenaarendfunc 278b3307b5eSBram Moolenaar 279b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 280b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 281b3307b5eSBram Moolenaar if s:vertical 282b3307b5eSBram Moolenaar vertical new 283b3307b5eSBram Moolenaar else 284b3307b5eSBram Moolenaar new 285b3307b5eSBram Moolenaar endif 286b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 287b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 288b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 289b3307b5eSBram Moolenaar set buftype=prompt 290b3307b5eSBram Moolenaar file gdb 291b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 292b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 293b3307b5eSBram Moolenaar 294b3307b5eSBram Moolenaar if s:vertical 295b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 296b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 297b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 298b3307b5eSBram Moolenaar endif 299b3307b5eSBram Moolenaar 300b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 301b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 302b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 303b3307b5eSBram Moolenaar 304b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 305b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 306b3307b5eSBram Moolenaar 307b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 308b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 309b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 310b3307b5eSBram Moolenaar \ }) 311b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 312b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 313b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 314b3307b5eSBram Moolenaar return 315b3307b5eSBram Moolenaar endif 3164551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 3174551c0a9SBram Moolenaar set modified 318b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 319b3307b5eSBram Moolenaar 32091359014SBram Moolenaar " Interpret commands while the target is running. This should usually only 321b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 322b3307b5eSBram Moolenaar " target is running. 323b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 324b3307b5eSBram Moolenaar " Older gdb uses a different command. 325b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 326b3307b5eSBram Moolenaar 327b3307b5eSBram Moolenaar let s:ptybuf = 0 328b3307b5eSBram Moolenaar if has('win32') 329b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 330b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 331b3307b5eSBram Moolenaar elseif has('terminal') 332b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 333b3307b5eSBram Moolenaar " gdb window. 334b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 335b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 336b3307b5eSBram Moolenaar \ }) 337b3307b5eSBram Moolenaar if s:ptybuf == 0 338b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 339b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 340b3307b5eSBram Moolenaar return 341b3307b5eSBram Moolenaar endif 342b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 343b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 344b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 345b3307b5eSBram Moolenaar 346b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 347b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 348b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 349b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 350b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 351b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 352b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 353b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 354b3307b5eSBram Moolenaar else 355b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 356b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 357b3307b5eSBram Moolenaar endif 358b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 359b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 360b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 361b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 362b3307b5eSBram Moolenaar 363b3307b5eSBram Moolenaar " Set arguments to be run 364b3307b5eSBram Moolenaar if len(proc_args) 365b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 366b3307b5eSBram Moolenaar endif 367b3307b5eSBram Moolenaar 368b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 369b3307b5eSBram Moolenaar startinsert 370b3307b5eSBram Moolenaarendfunc 371b3307b5eSBram Moolenaar 372b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 37338baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 37438baa3e6SBram Moolenaar " There can be only one. 37538baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 37638baa3e6SBram Moolenaar 37745d5f26dSBram Moolenaar " Install debugger commands in the text window. 378b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 379e09ba7baSBram Moolenaar call s:InstallCommands() 38045d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 381e09ba7baSBram Moolenaar 38251b0f370SBram Moolenaar " Enable showing a balloon with eval info 383246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 384246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 38551b0f370SBram Moolenaar if has("balloon_eval") 38651b0f370SBram Moolenaar set ballooneval 387246fe03dSBram Moolenaar endif 38851b0f370SBram Moolenaar if has("balloon_eval_term") 38951b0f370SBram Moolenaar set balloonevalterm 39051b0f370SBram Moolenaar endif 39151b0f370SBram Moolenaar endif 39251b0f370SBram Moolenaar 3935378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 3945378e1cfSBram Moolenaar " breakpoint number. 39537402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 39637402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 39737402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 39837402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 39937402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 400e09ba7baSBram Moolenaar let s:breakpoints = {} 4011b9645deSBram Moolenaar 40237402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 40337402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 40437402ed5SBram Moolenaar let s:breakpoint_locations = {} 40537402ed5SBram Moolenaar 4061b9645deSBram Moolenaar augroup TermDebug 4071b9645deSBram Moolenaar au BufRead * call s:BufRead() 4081b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 409f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 4101b9645deSBram Moolenaar augroup END 41132c67ba7SBram Moolenaar 412b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 413b3307b5eSBram Moolenaar " window. 41432c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 41532c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 41632c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 41732c67ba7SBram Moolenaar endif 418c572da5fSBram Moolenaarendfunc 419c572da5fSBram Moolenaar 420b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 421b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 422b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 423b3307b5eSBram Moolenaar if s:way == 'prompt' 424b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 425b3307b5eSBram Moolenaar else 426b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 427b3307b5eSBram Moolenaar endif 428b3307b5eSBram Moolenaarendfunc 429b3307b5eSBram Moolenaar 430b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 431b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 432b3307b5eSBram Moolenaar if s:way == 'prompt' 433b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 434b3307b5eSBram Moolenaar else 435b3307b5eSBram Moolenaar let do_continue = 0 436b3307b5eSBram Moolenaar if !s:stopped 437b3307b5eSBram Moolenaar let do_continue = 1 438b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 439b3307b5eSBram Moolenaar sleep 10m 440b3307b5eSBram Moolenaar endif 441b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 442b3307b5eSBram Moolenaar if do_continue 443b3307b5eSBram Moolenaar Continue 444b3307b5eSBram Moolenaar endif 445b3307b5eSBram Moolenaar endif 446b3307b5eSBram Moolenaarendfunc 447b3307b5eSBram Moolenaar 448b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 449b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 450b3307b5eSBram Moolenaar call s:SendCommand(a:text) 451b3307b5eSBram Moolenaarendfunc 452b3307b5eSBram Moolenaar 4534551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4544551c0a9SBram Moolenaar" breakpoint. 455b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4562ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4572ed890f1SBram Moolenaar if has('win32') 4582ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4592ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4604551c0a9SBram Moolenaar if s:pid == 0 4614551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4624551c0a9SBram Moolenaar else 4634551c0a9SBram Moolenaar call debugbreak(s:pid) 4644551c0a9SBram Moolenaar endif 4652ed890f1SBram Moolenaar else 4662ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4672ed890f1SBram Moolenaar endif 468b3307b5eSBram Moolenaarendfunc 469b3307b5eSBram Moolenaar 470b3307b5eSBram Moolenaar" Function called when gdb outputs text. 471b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 472b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 473b3307b5eSBram Moolenaar 474b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 475b3307b5eSBram Moolenaar " Drop status and echo'd commands. 476a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 477b3307b5eSBram Moolenaar return 478b3307b5eSBram Moolenaar endif 479b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 480b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 481b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 482b3307b5eSBram Moolenaar " Silently drop evaluation errors. 483b3307b5eSBram Moolenaar unlet s:evalexpr 484b3307b5eSBram Moolenaar return 485b3307b5eSBram Moolenaar endif 486b3307b5eSBram Moolenaar elseif a:text[0] == '~' 487b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 488b3307b5eSBram Moolenaar else 489b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 490b3307b5eSBram Moolenaar return 491b3307b5eSBram Moolenaar endif 492b3307b5eSBram Moolenaar 493b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 494b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 495b3307b5eSBram Moolenaar 496b3307b5eSBram Moolenaar " Add the output above the current prompt. 497b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4984551c0a9SBram Moolenaar set modified 499b3307b5eSBram Moolenaar 500b3307b5eSBram Moolenaar call win_gotoid(curwinid) 501b3307b5eSBram Moolenaarendfunc 502b3307b5eSBram Moolenaar 503b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 504b3307b5eSBram Moolenaar" to the next ", unescaping characters. 505b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 506b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 507a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 508b3307b5eSBram Moolenaar return 509b3307b5eSBram Moolenaar endif 510b3307b5eSBram Moolenaar let result = '' 511b3307b5eSBram Moolenaar let i = 1 512b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 513b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 514b3307b5eSBram Moolenaar let i += 1 515b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 516b3307b5eSBram Moolenaar " drop \n 517b3307b5eSBram Moolenaar let i += 1 518b3307b5eSBram Moolenaar continue 519589edb34SBram Moolenaar elseif a:quotedText[i] == 't' 520589edb34SBram Moolenaar " append \t 521589edb34SBram Moolenaar let i += 1 522589edb34SBram Moolenaar let result .= "\t" 523589edb34SBram Moolenaar continue 524b3307b5eSBram Moolenaar endif 525b3307b5eSBram Moolenaar endif 526b3307b5eSBram Moolenaar let result .= a:quotedText[i] 527b3307b5eSBram Moolenaar let i += 1 528b3307b5eSBram Moolenaar endwhile 529b3307b5eSBram Moolenaar return result 530b3307b5eSBram Moolenaarendfunc 531b3307b5eSBram Moolenaar 532a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 533a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5345378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5355378e1cfSBram Moolenaar return '' 5365378e1cfSBram Moolenaar endif 537a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 538a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 539a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 540a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 541a15b0a93SBram Moolenaar endif 542a15b0a93SBram Moolenaar return name 543a15b0a93SBram Moolenaarendfunc 544a15b0a93SBram Moolenaar 545b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 546fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 547b3623a38SBram Moolenaar unlet s:gdbwin 548e09ba7baSBram Moolenaar 549b3307b5eSBram Moolenaar call s:EndDebugCommon() 550b3307b5eSBram Moolenaarendfunc 551b3307b5eSBram Moolenaar 552b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 553e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 554e09ba7baSBram Moolenaar 555b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 556b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 557b3307b5eSBram Moolenaar endif 558b3307b5eSBram Moolenaar 559b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 560e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 561e09ba7baSBram Moolenaar call s:DeleteCommands() 562e09ba7baSBram Moolenaar 563e09ba7baSBram Moolenaar call win_gotoid(curwinid) 564b3307b5eSBram Moolenaar 56538baa3e6SBram Moolenaar if s:save_columns > 0 56638baa3e6SBram Moolenaar let &columns = s:save_columns 56738baa3e6SBram Moolenaar endif 5681b9645deSBram Moolenaar 569246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 570246fe03dSBram Moolenaar set balloonexpr= 57151b0f370SBram Moolenaar if has("balloon_eval") 57251b0f370SBram Moolenaar set noballooneval 573246fe03dSBram Moolenaar endif 57451b0f370SBram Moolenaar if has("balloon_eval_term") 57551b0f370SBram Moolenaar set noballoonevalterm 57651b0f370SBram Moolenaar endif 57751b0f370SBram Moolenaar endif 57851b0f370SBram Moolenaar 5791b9645deSBram Moolenaar au! TermDebug 580fe386641SBram Moolenaarendfunc 581fe386641SBram Moolenaar 582b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 583b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 584b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5854551c0a9SBram Moolenaar set nomodified 586b3307b5eSBram Moolenaar close 587b3307b5eSBram Moolenaar if curwinid != s:gdbwin 588b3307b5eSBram Moolenaar call win_gotoid(curwinid) 589b3307b5eSBram Moolenaar endif 590b3307b5eSBram Moolenaar 591b3307b5eSBram Moolenaar call s:EndDebugCommon() 592b3307b5eSBram Moolenaar unlet s:gdbwin 593b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 594b3307b5eSBram Moolenaarendfunc 595b3307b5eSBram Moolenaar 596fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 597fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 598fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 599fe386641SBram Moolenaar 600fe386641SBram Moolenaar for msg in msgs 601fe386641SBram Moolenaar " remove prefixed NL 602fe386641SBram Moolenaar if msg[0] == "\n" 603fe386641SBram Moolenaar let msg = msg[1:] 604fe386641SBram Moolenaar endif 605fe386641SBram Moolenaar if msg != '' 6061b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 607e09ba7baSBram Moolenaar call s:HandleCursor(msg) 60845d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 609e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 610e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 611e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 6124551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 6134551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 61445d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 61545d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 61645d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 61745d5f26dSBram Moolenaar call s:HandleError(msg) 618e09ba7baSBram Moolenaar endif 619e09ba7baSBram Moolenaar endif 620e09ba7baSBram Moolenaar endfor 621e09ba7baSBram Moolenaarendfunc 622e09ba7baSBram Moolenaar 623589edb34SBram Moolenaarfunc s:GotoProgram() 624589edb34SBram Moolenaar if has('win32') 625589edb34SBram Moolenaar if executable('powershell') 626589edb34SBram Moolenaar call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid)) 627589edb34SBram Moolenaar endif 628589edb34SBram Moolenaar else 629*469bdbdeSBram Moolenaar call win_gotoid(s:ptywin) 630589edb34SBram Moolenaar endif 631589edb34SBram Moolenaarendfunc 632589edb34SBram Moolenaar 633e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 634e09ba7baSBram Moolenaarfunc s:InstallCommands() 635963c1ad5SBram Moolenaar let save_cpo = &cpo 636963c1ad5SBram Moolenaar set cpo&vim 637963c1ad5SBram Moolenaar 638589edb34SBram Moolenaar command -nargs=? Break call s:SetBreakpoint(<q-args>) 63971137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 640e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 64145d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 642e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 64360e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 64460e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 64560e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 646b3307b5eSBram Moolenaar 647b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 648b3307b5eSBram Moolenaar if s:way == 'prompt' 649b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 650b3307b5eSBram Moolenaar else 651b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 652b3307b5eSBram Moolenaar endif 653b3307b5eSBram Moolenaar 65445d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 65545d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 656589edb34SBram Moolenaar command Program call s:GotoProgram() 657b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 65871137fedSBram Moolenaar command Winbar call s:InstallWinbar() 65945d5f26dSBram Moolenaar 66045d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 66145d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 6621b9645deSBram Moolenaar 663f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 66471137fedSBram Moolenaar call s:InstallWinbar() 66571137fedSBram Moolenaar 66671137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 66771137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 66871137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 66971137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 67071137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 67171137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 67271137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 67371137fedSBram Moolenaar endif 67471137fedSBram Moolenaar endif 675963c1ad5SBram Moolenaar 676963c1ad5SBram Moolenaar let &cpo = save_cpo 67771137fedSBram Moolenaarendfunc 67871137fedSBram Moolenaar 67971137fedSBram Moolenaarlet s:winbar_winids = [] 68071137fedSBram Moolenaar 68171137fedSBram Moolenaar" Install the window toolbar in the current window. 68271137fedSBram Moolenaarfunc s:InstallWinbar() 683c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 68424a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 68524a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 68624a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 68724a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 68860e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 68924a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 69071137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 691c4b533e1SBram Moolenaar endif 692e09ba7baSBram Moolenaarendfunc 693e09ba7baSBram Moolenaar 694e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 695e09ba7baSBram Moolenaarfunc s:DeleteCommands() 696e09ba7baSBram Moolenaar delcommand Break 69771137fedSBram Moolenaar delcommand Clear 698e09ba7baSBram Moolenaar delcommand Step 69945d5f26dSBram Moolenaar delcommand Over 700e09ba7baSBram Moolenaar delcommand Finish 70160e73f2aSBram Moolenaar delcommand Run 70260e73f2aSBram Moolenaar delcommand Arguments 70360e73f2aSBram Moolenaar delcommand Stop 704e09ba7baSBram Moolenaar delcommand Continue 70545d5f26dSBram Moolenaar delcommand Evaluate 70645d5f26dSBram Moolenaar delcommand Gdb 70745d5f26dSBram Moolenaar delcommand Program 708b3623a38SBram Moolenaar delcommand Source 70971137fedSBram Moolenaar delcommand Winbar 71045d5f26dSBram Moolenaar 71145d5f26dSBram Moolenaar nunmap K 7121b9645deSBram Moolenaar 7131b9645deSBram Moolenaar if has('menu') 71471137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 71571137fedSBram Moolenaar let curwinid = win_getid(winnr()) 71671137fedSBram Moolenaar for winid in s:winbar_winids 71771137fedSBram Moolenaar if win_gotoid(winid) 7181b9645deSBram Moolenaar aunmenu WinBar.Step 7191b9645deSBram Moolenaar aunmenu WinBar.Next 7201b9645deSBram Moolenaar aunmenu WinBar.Finish 7211b9645deSBram Moolenaar aunmenu WinBar.Cont 72260e73f2aSBram Moolenaar aunmenu WinBar.Stop 7231b9645deSBram Moolenaar aunmenu WinBar.Eval 7241b9645deSBram Moolenaar endif 72571137fedSBram Moolenaar endfor 72671137fedSBram Moolenaar call win_gotoid(curwinid) 72771137fedSBram Moolenaar let s:winbar_winids = [] 72871137fedSBram Moolenaar 72971137fedSBram Moolenaar if exists('s:saved_mousemodel') 73071137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 73171137fedSBram Moolenaar unlet s:saved_mousemodel 73271137fedSBram Moolenaar aunmenu PopUp.-SEP3- 73371137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 73471137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 73571137fedSBram Moolenaar aunmenu PopUp.Evaluate 73671137fedSBram Moolenaar endif 73771137fedSBram Moolenaar endif 7381b9645deSBram Moolenaar 73945d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 74037402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 74137402ed5SBram Moolenaar for subid in keys(entries) 74237402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 74337402ed5SBram Moolenaar endfor 74445d5f26dSBram Moolenaar endfor 74545d5f26dSBram Moolenaar unlet s:breakpoints 74637402ed5SBram Moolenaar unlet s:breakpoint_locations 747a15b0a93SBram Moolenaar 748a15b0a93SBram Moolenaar sign undefine debugPC 749a15b0a93SBram Moolenaar for val in s:BreakpointSigns 750a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 751a15b0a93SBram Moolenaar endfor 7524551c0a9SBram Moolenaar let s:BreakpointSigns = [] 753e09ba7baSBram Moolenaarendfunc 754e09ba7baSBram Moolenaar 755e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 756589edb34SBram Moolenaarfunc s:SetBreakpoint(at) 75760e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 75860e73f2aSBram Moolenaar " Interrupt to make it work. 75960e73f2aSBram Moolenaar let do_continue = 0 76060e73f2aSBram Moolenaar if !s:stopped 76160e73f2aSBram Moolenaar let do_continue = 1 762b3307b5eSBram Moolenaar if s:way == 'prompt' 7634551c0a9SBram Moolenaar call s:PromptInterrupt() 764b3307b5eSBram Moolenaar else 76560e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 766b3307b5eSBram Moolenaar endif 76760e73f2aSBram Moolenaar sleep 10m 76860e73f2aSBram Moolenaar endif 769589edb34SBram Moolenaar 770a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 771589edb34SBram Moolenaar let at = empty(a:at) ? 772589edb34SBram Moolenaar \ fnameescape(expand('%:p')) . ':' . line('.') : a:at 773589edb34SBram Moolenaar call s:SendCommand('-break-insert ' . at) 77460e73f2aSBram Moolenaar if do_continue 77560e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 77660e73f2aSBram Moolenaar endif 777e09ba7baSBram Moolenaarendfunc 778e09ba7baSBram Moolenaar 77971137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 78071137fedSBram Moolenaarfunc s:ClearBreakpoint() 781e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 782e09ba7baSBram Moolenaar let lnum = line('.') 78337402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 78437402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 78537402ed5SBram Moolenaar let idx = 0 78637402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 78737402ed5SBram Moolenaar if has_key(s:breakpoints, id) 78837402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 78937402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 79037402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 79137402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 79237402ed5SBram Moolenaar endfor 79337402ed5SBram Moolenaar unlet s:breakpoints[id] 79437402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 795e09ba7baSBram Moolenaar break 79637402ed5SBram Moolenaar else 79737402ed5SBram Moolenaar let idx += 1 798e09ba7baSBram Moolenaar endif 799e09ba7baSBram Moolenaar endfor 80037402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 80137402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 80237402ed5SBram Moolenaar endif 80337402ed5SBram Moolenaar endif 804e09ba7baSBram Moolenaarendfunc 805e09ba7baSBram Moolenaar 80660e73f2aSBram Moolenaarfunc s:Run(args) 80760e73f2aSBram Moolenaar if a:args != '' 80860e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 80960e73f2aSBram Moolenaar endif 81060e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 81160e73f2aSBram Moolenaarendfunc 81260e73f2aSBram Moolenaar 81351b0f370SBram Moolenaarfunc s:SendEval(expr) 81451b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 81551b0f370SBram Moolenaar let s:evalexpr = a:expr 81651b0f370SBram Moolenaarendfunc 81751b0f370SBram Moolenaar 81845d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 81945d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 82045d5f26dSBram Moolenaar if a:arg != '' 82145d5f26dSBram Moolenaar let expr = a:arg 82245d5f26dSBram Moolenaar elseif a:range == 2 82345d5f26dSBram Moolenaar let pos = getcurpos() 82445d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 82545d5f26dSBram Moolenaar let regt = getregtype('v') 82645d5f26dSBram Moolenaar normal! gv"vy 82745d5f26dSBram Moolenaar let expr = @v 82845d5f26dSBram Moolenaar call setpos('.', pos) 82945d5f26dSBram Moolenaar call setreg('v', reg, regt) 83045d5f26dSBram Moolenaar else 83145d5f26dSBram Moolenaar let expr = expand('<cexpr>') 83245d5f26dSBram Moolenaar endif 83322f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 83451b0f370SBram Moolenaar call s:SendEval(expr) 83545d5f26dSBram Moolenaarendfunc 83645d5f26dSBram Moolenaar 83722f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 83851b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 83951b0f370SBram Moolenaar 84045d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 84145d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 8421b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 8431b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 84451b0f370SBram Moolenaar if s:evalFromBalloonExpr 84551b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 84651b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 84751b0f370SBram Moolenaar else 84851b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 84951b0f370SBram Moolenaar endif 85051b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 85151b0f370SBram Moolenaar else 8521b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 85351b0f370SBram Moolenaar endif 8541b9645deSBram Moolenaar 8557f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 8561b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 85722f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 85851b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 85951b0f370SBram Moolenaar else 86051b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 8611b9645deSBram Moolenaar endif 86245d5f26dSBram Moolenaarendfunc 86345d5f26dSBram Moolenaar 86451b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 86551b0f370SBram Moolenaar" if there is any. 86651b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 867b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 868396e829fSBram Moolenaar return '' 869b3307b5eSBram Moolenaar endif 870b3307b5eSBram Moolenaar if !s:stopped 871b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 872b3307b5eSBram Moolenaar " mouse triggers a balloon. 873396e829fSBram Moolenaar return '' 87451b0f370SBram Moolenaar endif 87551b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 87651b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 87722f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 87822f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 87951b0f370SBram Moolenaar return '' 88051b0f370SBram Moolenaarendfunc 88151b0f370SBram Moolenaar 88245d5f26dSBram Moolenaar" Handle an error. 88345d5f26dSBram Moolenaarfunc s:HandleError(msg) 88422f1d0e3SBram Moolenaar if s:ignoreEvalError 88551b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 88622f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 88722f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 88851b0f370SBram Moolenaar return 88951b0f370SBram Moolenaar endif 89045d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 89145d5f26dSBram Moolenaarendfunc 89245d5f26dSBram Moolenaar 893b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 894b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 895c4b533e1SBram Moolenaar new 896b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 897c4b533e1SBram Moolenaar call s:InstallWinbar() 898c4b533e1SBram Moolenaar endif 899c4b533e1SBram Moolenaarendfunc 900c4b533e1SBram Moolenaar 901e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 902e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 903e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 904fe386641SBram Moolenaar let wid = win_getid(winnr()) 905fe386641SBram Moolenaar 90660e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 9074551c0a9SBram Moolenaar call ch_log('program stopped') 90860e73f2aSBram Moolenaar let s:stopped = 1 90960e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 9104551c0a9SBram Moolenaar call ch_log('program running') 91160e73f2aSBram Moolenaar let s:stopped = 0 91260e73f2aSBram Moolenaar endif 91360e73f2aSBram Moolenaar 914a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 915a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 916a15b0a93SBram Moolenaar else 917a15b0a93SBram Moolenaar let fname = '' 918a15b0a93SBram Moolenaar endif 9191b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 920e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 921fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 9224551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 9231b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 924fe386641SBram Moolenaar if &modified 925fe386641SBram Moolenaar " TODO: find existing window 926fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 927b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 928c4b533e1SBram Moolenaar call s:InstallWinbar() 929fe386641SBram Moolenaar else 930fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 931fe386641SBram Moolenaar endif 932fe386641SBram Moolenaar endif 933fe386641SBram Moolenaar exe lnum 93401164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 9351b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 936fe386641SBram Moolenaar setlocal signcolumn=yes 937fe386641SBram Moolenaar endif 9384551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 939fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 940fe386641SBram Moolenaar endif 941fe386641SBram Moolenaar 942fe386641SBram Moolenaar call win_gotoid(wid) 943e09ba7baSBram Moolenaarendfunc 944e09ba7baSBram Moolenaar 945de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 946a15b0a93SBram Moolenaar 94737402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 94837402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 94937402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 95037402ed5SBram Moolenaar call add(s:BreakpointSigns, nr) 95137402ed5SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 952de1a8314SBram Moolenaar endif 953de1a8314SBram Moolenaarendfunc 954de1a8314SBram Moolenaar 95537402ed5SBram Moolenaarfunc! s:SplitMsg(s) 95637402ed5SBram Moolenaar return split(a:s, '{.\{-}}\zs') 9575378e1cfSBram Moolenaarendfunction 9585378e1cfSBram Moolenaar 959e09ba7baSBram Moolenaar" Handle setting a breakpoint 960e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 961e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 9626dccc962SBram Moolenaar if a:msg !~ 'fullname=' 9636dccc962SBram Moolenaar " a watch does not have a file name 9646dccc962SBram Moolenaar return 9656dccc962SBram Moolenaar endif 9665378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 9675378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 9685378e1cfSBram Moolenaar if empty(fname) 9695378e1cfSBram Moolenaar continue 9705378e1cfSBram Moolenaar endif 9715378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 9725378e1cfSBram Moolenaar if empty(nr) 973e09ba7baSBram Moolenaar return 974fe386641SBram Moolenaar endif 975e09ba7baSBram Moolenaar 97637402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 97737402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 97837402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 97937402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 98037402ed5SBram Moolenaar 98137402ed5SBram Moolenaar if has_key(s:breakpoints, id) 98237402ed5SBram Moolenaar let entries = s:breakpoints[id] 98337402ed5SBram Moolenaar else 98437402ed5SBram Moolenaar let entries = {} 98537402ed5SBram Moolenaar let s:breakpoints[id] = entries 98637402ed5SBram Moolenaar endif 98737402ed5SBram Moolenaar if has_key(entries, subid) 98837402ed5SBram Moolenaar let entry = entries[subid] 989e09ba7baSBram Moolenaar else 990e09ba7baSBram Moolenaar let entry = {} 99137402ed5SBram Moolenaar let entries[subid] = entry 992fe386641SBram Moolenaar endif 993e09ba7baSBram Moolenaar 9945378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 995e09ba7baSBram Moolenaar let entry['fname'] = fname 996e09ba7baSBram Moolenaar let entry['lnum'] = lnum 9971b9645deSBram Moolenaar 99837402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 99937402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 100037402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 100137402ed5SBram Moolenaar endif 100237402ed5SBram Moolenaar let s:breakpoint_locations[bploc] += [id] 100337402ed5SBram Moolenaar 10041b9645deSBram Moolenaar if bufloaded(fname) 100537402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10061b9645deSBram Moolenaar endif 10075378e1cfSBram Moolenaar endfor 10081b9645deSBram Moolenaarendfunc 10091b9645deSBram Moolenaar 101037402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry) 101137402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 101237402ed5SBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' file=' . a:entry['fname'] 10131b9645deSBram Moolenaar let a:entry['placed'] = 1 1014e09ba7baSBram Moolenaarendfunc 1015e09ba7baSBram Moolenaar 1016e09ba7baSBram Moolenaar" Handle deleting a breakpoint 1017e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 1018e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 101937402ed5SBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 102037402ed5SBram Moolenaar if empty(id) 1021e09ba7baSBram Moolenaar return 1022e09ba7baSBram Moolenaar endif 102337402ed5SBram Moolenaar if has_key(s:breakpoints, id) 102437402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 10251b9645deSBram Moolenaar if has_key(entry, 'placed') 102637402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 10271b9645deSBram Moolenaar unlet entry['placed'] 10281b9645deSBram Moolenaar endif 10295378e1cfSBram Moolenaar endfor 103037402ed5SBram Moolenaar unlet s:breakpoints[id] 103137402ed5SBram Moolenaar endif 1032c572da5fSBram Moolenaarendfunc 10331b9645deSBram Moolenaar 10344551c0a9SBram Moolenaar" Handle the debugged program starting to run. 10354551c0a9SBram Moolenaar" Will store the process ID in s:pid 10364551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 10374551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 10384551c0a9SBram Moolenaar if nr == 0 10394551c0a9SBram Moolenaar return 10404551c0a9SBram Moolenaar endif 10414551c0a9SBram Moolenaar let s:pid = nr 10424551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 10434551c0a9SBram Moolenaarendfunc 10444551c0a9SBram Moolenaar 10451b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 10461b9645deSBram Moolenaarfunc s:BufRead() 10471b9645deSBram Moolenaar let fname = expand('<afile>:p') 104837402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 104937402ed5SBram Moolenaar for [subid, entry] in items(entries) 10501b9645deSBram Moolenaar if entry['fname'] == fname 105137402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 10521b9645deSBram Moolenaar endif 10531b9645deSBram Moolenaar endfor 105437402ed5SBram Moolenaar endfor 10551b9645deSBram Moolenaarendfunc 10561b9645deSBram Moolenaar 10571b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 10581b9645deSBram Moolenaarfunc s:BufUnloaded() 10591b9645deSBram Moolenaar let fname = expand('<afile>:p') 106037402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 106137402ed5SBram Moolenaar for [subid, entry] in items(entries) 10621b9645deSBram Moolenaar if entry['fname'] == fname 10631b9645deSBram Moolenaar let entry['placed'] = 0 10641b9645deSBram Moolenaar endif 10651b9645deSBram Moolenaar endfor 106637402ed5SBram Moolenaar endfor 10671b9645deSBram Moolenaarendfunc 1068ca4cc018SBram Moolenaar 1069ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 1070ca4cc018SBram Moolenaarunlet s:keepcpo 1071