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 76f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new) 77f07f9e73SBram Moolenaar let default = a:init ? 'default ' : '' 78f07f9e73SBram Moolenaar if a:new ==# 'light' && a:old !=# 'light' 79f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" 80f07f9e73SBram Moolenaar elseif a:new ==# 'dark' && a:old !=# 'dark' 81f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue" 82e09ba7baSBram Moolenaar endif 83f07f9e73SBram Moolenaarendfunc 84f07f9e73SBram Moolenaar 85f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background) 86e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 87fe386641SBram Moolenaar 8832c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 8932c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 9032c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 9132c67ba7SBram Moolenaarendfunc 9232c67ba7SBram Moolenaar 9332c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 9432c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 9532c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 9632c67ba7SBram Moolenaarendfunc 9732c67ba7SBram Moolenaar 9832c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 99b3623a38SBram Moolenaar if exists('s:gdbwin') 100b3623a38SBram Moolenaar echoerr 'Terminal debugger already running' 101b3623a38SBram Moolenaar return 102b3623a38SBram Moolenaar endif 103b3307b5eSBram Moolenaar let s:ptywin = 0 1044551c0a9SBram Moolenaar let s:pid = 0 105b3623a38SBram Moolenaar 106b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 107b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 108b3307b5eSBram Moolenaar 109b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 110fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 111fe386641SBram Moolenaar 11224a98a0eSBram Moolenaar let s:save_columns = 0 11324a98a0eSBram Moolenaar if exists('g:termdebug_wide') 11424a98a0eSBram Moolenaar if &columns < g:termdebug_wide 11538baa3e6SBram Moolenaar let s:save_columns = &columns 11638baa3e6SBram Moolenaar let &columns = g:termdebug_wide 11724a98a0eSBram Moolenaar endif 118b3307b5eSBram Moolenaar let s:vertical = 1 11938baa3e6SBram Moolenaar else 120b3307b5eSBram Moolenaar let s:vertical = 0 12138baa3e6SBram Moolenaar endif 12238baa3e6SBram Moolenaar 123b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 124b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 125b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 126b3307b5eSBram Moolenaar let s:way = 'terminal' 127b3307b5eSBram Moolenaar else 128b3307b5eSBram Moolenaar let s:way = 'prompt' 129b3307b5eSBram Moolenaar endif 130b3307b5eSBram Moolenaar 131b3307b5eSBram Moolenaar if s:way == 'prompt' 132b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 133b3307b5eSBram Moolenaar else 134b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 135b3307b5eSBram Moolenaar endif 136b3307b5eSBram Moolenaarendfunc 137b3307b5eSBram Moolenaar 138b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 139b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 140fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 141b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 142b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 143fe386641SBram Moolenaar \ }) 144fe386641SBram Moolenaar if s:ptybuf == 0 145fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 146fe386641SBram Moolenaar return 147fe386641SBram Moolenaar endif 148fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 14945d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 150b3307b5eSBram Moolenaar if s:vertical 15151b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 15251b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 15351b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 15451b0f370SBram Moolenaar endif 155fe386641SBram Moolenaar 156fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 157fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 158fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 159fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 160fe386641SBram Moolenaar \ 'hidden': 1, 161fe386641SBram Moolenaar \ }) 162fe386641SBram Moolenaar if s:commbuf == 0 163fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 164fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 165fe386641SBram Moolenaar return 166fe386641SBram Moolenaar endif 167fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 168c572da5fSBram Moolenaar 169c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 170c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 17132c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 17232c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 17332c67ba7SBram Moolenaar 17432c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 175b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 17660e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 177b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndTermDebug'), 178fe386641SBram Moolenaar \ 'term_finish': 'close', 179c572da5fSBram Moolenaar \ }) 18060e73f2aSBram Moolenaar if s:gdbbuf == 0 181fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 182fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 183fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 184fe386641SBram Moolenaar return 185fe386641SBram Moolenaar endif 18645d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 187fe386641SBram Moolenaar 18832c67ba7SBram Moolenaar " Set arguments to be run 18932c67ba7SBram Moolenaar if len(proc_args) 19032c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 19132c67ba7SBram Moolenaar endif 19232c67ba7SBram Moolenaar 193fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 19460e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 19560e73f2aSBram Moolenaar 1963e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 1973e4b84d0SBram Moolenaar " why the debugger doesn't work. 1983e4b84d0SBram Moolenaar let try_count = 0 1993e4b84d0SBram Moolenaar while 1 2003e4b84d0SBram Moolenaar let response = '' 201b3623a38SBram Moolenaar for lnum in range(1,200) 2023e4b84d0SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 2033e4b84d0SBram Moolenaar let response = term_getline(s:gdbbuf, lnum + 1) 2043e4b84d0SBram Moolenaar if response =~ 'Undefined command' 205f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 2063e4b84d0SBram Moolenaar exe 'bwipe! ' . s:ptybuf 2073e4b84d0SBram Moolenaar exe 'bwipe! ' . s:commbuf 2083e4b84d0SBram Moolenaar return 2093e4b84d0SBram Moolenaar endif 2103e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2113e4b84d0SBram Moolenaar " Success! 2123e4b84d0SBram Moolenaar break 2133e4b84d0SBram Moolenaar endif 2143e4b84d0SBram Moolenaar endif 2153e4b84d0SBram Moolenaar endfor 2163e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2173e4b84d0SBram Moolenaar break 2183e4b84d0SBram Moolenaar endif 2193e4b84d0SBram Moolenaar let try_count += 1 2203e4b84d0SBram Moolenaar if try_count > 100 2213e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2223e4b84d0SBram Moolenaar break 2233e4b84d0SBram Moolenaar endif 2243e4b84d0SBram Moolenaar sleep 10m 2253e4b84d0SBram Moolenaar endwhile 2263e4b84d0SBram Moolenaar 22760e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 22860e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 22960e73f2aSBram Moolenaar " running. 23060e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 231b3307b5eSBram Moolenaar " Older gdb uses a different command. 232b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 233e09ba7baSBram Moolenaar 234f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 235f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 236b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 237f3ba14ffSBram Moolenaar 238b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 239b3307b5eSBram Moolenaarendfunc 240b3307b5eSBram Moolenaar 241b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 242b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 243b3307b5eSBram Moolenaar if s:vertical 244b3307b5eSBram Moolenaar vertical new 245b3307b5eSBram Moolenaar else 246b3307b5eSBram Moolenaar new 247b3307b5eSBram Moolenaar endif 248b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 249b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 250b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 251b3307b5eSBram Moolenaar set buftype=prompt 252b3307b5eSBram Moolenaar file gdb 253b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 254b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 255b3307b5eSBram Moolenaar 256b3307b5eSBram Moolenaar if s:vertical 257b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 258b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 259b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 260b3307b5eSBram Moolenaar endif 261b3307b5eSBram Moolenaar 262b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 263b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 264b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 265b3307b5eSBram Moolenaar 266b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 267b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 268b3307b5eSBram Moolenaar 269b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 270b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 271b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 272b3307b5eSBram Moolenaar \ }) 273b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 274b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 275b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 276b3307b5eSBram Moolenaar return 277b3307b5eSBram Moolenaar endif 2784551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 2794551c0a9SBram Moolenaar set modified 280b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 281b3307b5eSBram Moolenaar 282b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 283b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 284b3307b5eSBram Moolenaar " target is running. 285b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 286b3307b5eSBram Moolenaar " Older gdb uses a different command. 287b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 288b3307b5eSBram Moolenaar 289b3307b5eSBram Moolenaar let s:ptybuf = 0 290b3307b5eSBram Moolenaar if has('win32') 291b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 292b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 293b3307b5eSBram Moolenaar elseif has('terminal') 294b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 295b3307b5eSBram Moolenaar " gdb window. 296b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 297b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 298b3307b5eSBram Moolenaar \ }) 299b3307b5eSBram Moolenaar if s:ptybuf == 0 300b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 301b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 302b3307b5eSBram Moolenaar return 303b3307b5eSBram Moolenaar endif 304b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 305b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 306b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 307b3307b5eSBram Moolenaar 308b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 309b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 310b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 311b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 312b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 313b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 314b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 315b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 316b3307b5eSBram Moolenaar else 317b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 318b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 319b3307b5eSBram Moolenaar endif 320b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 321b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 322b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 323b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 324b3307b5eSBram Moolenaar 325b3307b5eSBram Moolenaar " Set arguments to be run 326b3307b5eSBram Moolenaar if len(proc_args) 327b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 328b3307b5eSBram Moolenaar endif 329b3307b5eSBram Moolenaar 330b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 331b3307b5eSBram Moolenaar startinsert 332b3307b5eSBram Moolenaarendfunc 333b3307b5eSBram Moolenaar 334b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 33538baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 33638baa3e6SBram Moolenaar " There can be only one. 33738baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 33838baa3e6SBram Moolenaar 33945d5f26dSBram Moolenaar " Install debugger commands in the text window. 340b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 341e09ba7baSBram Moolenaar call s:InstallCommands() 34245d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 343e09ba7baSBram Moolenaar 34451b0f370SBram Moolenaar " Enable showing a balloon with eval info 345246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 346246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 34751b0f370SBram Moolenaar if has("balloon_eval") 34851b0f370SBram Moolenaar set ballooneval 349246fe03dSBram Moolenaar endif 35051b0f370SBram Moolenaar if has("balloon_eval_term") 35151b0f370SBram Moolenaar set balloonevalterm 35251b0f370SBram Moolenaar endif 35351b0f370SBram Moolenaar endif 35451b0f370SBram Moolenaar 355de1a8314SBram Moolenaar " Contains breakpoints that have been placed, key is the number. 356e09ba7baSBram Moolenaar let s:breakpoints = {} 3571b9645deSBram Moolenaar 3581b9645deSBram Moolenaar augroup TermDebug 3591b9645deSBram Moolenaar au BufRead * call s:BufRead() 3601b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 361f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 3621b9645deSBram Moolenaar augroup END 36332c67ba7SBram Moolenaar 364b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 365b3307b5eSBram Moolenaar " window. 36632c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 36732c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 36832c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 36932c67ba7SBram Moolenaar endif 370c572da5fSBram Moolenaarendfunc 371c572da5fSBram Moolenaar 372b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 373b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 374b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 375b3307b5eSBram Moolenaar if s:way == 'prompt' 376b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 377b3307b5eSBram Moolenaar else 378b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 379b3307b5eSBram Moolenaar endif 380b3307b5eSBram Moolenaarendfunc 381b3307b5eSBram Moolenaar 382b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 383b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 384b3307b5eSBram Moolenaar if s:way == 'prompt' 385b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 386b3307b5eSBram Moolenaar else 387b3307b5eSBram Moolenaar let do_continue = 0 388b3307b5eSBram Moolenaar if !s:stopped 389b3307b5eSBram Moolenaar let do_continue = 1 390b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 391b3307b5eSBram Moolenaar sleep 10m 392b3307b5eSBram Moolenaar endif 393b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 394b3307b5eSBram Moolenaar if do_continue 395b3307b5eSBram Moolenaar Continue 396b3307b5eSBram Moolenaar endif 397b3307b5eSBram Moolenaar endif 398b3307b5eSBram Moolenaarendfunc 399b3307b5eSBram Moolenaar 400b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 401b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 402b3307b5eSBram Moolenaar call s:SendCommand(a:text) 403b3307b5eSBram Moolenaarendfunc 404b3307b5eSBram Moolenaar 4054551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4064551c0a9SBram Moolenaar" breakpoint. 407b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4082ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4092ed890f1SBram Moolenaar if has('win32') 4102ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4112ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4124551c0a9SBram Moolenaar if s:pid == 0 4134551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4144551c0a9SBram Moolenaar else 4154551c0a9SBram Moolenaar call debugbreak(s:pid) 4164551c0a9SBram Moolenaar endif 4172ed890f1SBram Moolenaar else 4182ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4192ed890f1SBram Moolenaar endif 420b3307b5eSBram Moolenaarendfunc 421b3307b5eSBram Moolenaar 422b3307b5eSBram Moolenaar" Function called when gdb outputs text. 423b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 424b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 425b3307b5eSBram Moolenaar 426b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 427b3307b5eSBram Moolenaar " Drop status and echo'd commands. 428a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 429b3307b5eSBram Moolenaar return 430b3307b5eSBram Moolenaar endif 431b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 432b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 433b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 434b3307b5eSBram Moolenaar " Silently drop evaluation errors. 435b3307b5eSBram Moolenaar unlet s:evalexpr 436b3307b5eSBram Moolenaar return 437b3307b5eSBram Moolenaar endif 438b3307b5eSBram Moolenaar elseif a:text[0] == '~' 439b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 440b3307b5eSBram Moolenaar else 441b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 442b3307b5eSBram Moolenaar return 443b3307b5eSBram Moolenaar endif 444b3307b5eSBram Moolenaar 445b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 446b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 447b3307b5eSBram Moolenaar 448b3307b5eSBram Moolenaar " Add the output above the current prompt. 449b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4504551c0a9SBram Moolenaar set modified 451b3307b5eSBram Moolenaar 452b3307b5eSBram Moolenaar call win_gotoid(curwinid) 453b3307b5eSBram Moolenaarendfunc 454b3307b5eSBram Moolenaar 455b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 456b3307b5eSBram Moolenaar" to the next ", unescaping characters. 457b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 458b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 459a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 460b3307b5eSBram Moolenaar return 461b3307b5eSBram Moolenaar endif 462b3307b5eSBram Moolenaar let result = '' 463b3307b5eSBram Moolenaar let i = 1 464b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 465b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 466b3307b5eSBram Moolenaar let i += 1 467b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 468b3307b5eSBram Moolenaar " drop \n 469b3307b5eSBram Moolenaar let i += 1 470b3307b5eSBram Moolenaar continue 471b3307b5eSBram Moolenaar endif 472b3307b5eSBram Moolenaar endif 473b3307b5eSBram Moolenaar let result .= a:quotedText[i] 474b3307b5eSBram Moolenaar let i += 1 475b3307b5eSBram Moolenaar endwhile 476b3307b5eSBram Moolenaar return result 477b3307b5eSBram Moolenaarendfunc 478b3307b5eSBram Moolenaar 479a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 480a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 481a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 482a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 483a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 484a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 485a15b0a93SBram Moolenaar endif 486a15b0a93SBram Moolenaar return name 487a15b0a93SBram Moolenaarendfunc 488a15b0a93SBram Moolenaar 489b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 490fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 491b3623a38SBram Moolenaar unlet s:gdbwin 492e09ba7baSBram Moolenaar 493b3307b5eSBram Moolenaar call s:EndDebugCommon() 494b3307b5eSBram Moolenaarendfunc 495b3307b5eSBram Moolenaar 496b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 497e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 498e09ba7baSBram Moolenaar 499b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 500b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 501b3307b5eSBram Moolenaar endif 502b3307b5eSBram Moolenaar 503b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 504e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 505e09ba7baSBram Moolenaar call s:DeleteCommands() 506e09ba7baSBram Moolenaar 507e09ba7baSBram Moolenaar call win_gotoid(curwinid) 508b3307b5eSBram Moolenaar 50938baa3e6SBram Moolenaar if s:save_columns > 0 51038baa3e6SBram Moolenaar let &columns = s:save_columns 51138baa3e6SBram Moolenaar endif 5121b9645deSBram Moolenaar 513246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 514246fe03dSBram Moolenaar set balloonexpr= 51551b0f370SBram Moolenaar if has("balloon_eval") 51651b0f370SBram Moolenaar set noballooneval 517246fe03dSBram Moolenaar endif 51851b0f370SBram Moolenaar if has("balloon_eval_term") 51951b0f370SBram Moolenaar set noballoonevalterm 52051b0f370SBram Moolenaar endif 52151b0f370SBram Moolenaar endif 52251b0f370SBram Moolenaar 5231b9645deSBram Moolenaar au! TermDebug 524fe386641SBram Moolenaarendfunc 525fe386641SBram Moolenaar 526b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 527b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 528b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5294551c0a9SBram Moolenaar set nomodified 530b3307b5eSBram Moolenaar close 531b3307b5eSBram Moolenaar if curwinid != s:gdbwin 532b3307b5eSBram Moolenaar call win_gotoid(curwinid) 533b3307b5eSBram Moolenaar endif 534b3307b5eSBram Moolenaar 535b3307b5eSBram Moolenaar call s:EndDebugCommon() 536b3307b5eSBram Moolenaar unlet s:gdbwin 537b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 538b3307b5eSBram Moolenaarendfunc 539b3307b5eSBram Moolenaar 540fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 541fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 542fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 543fe386641SBram Moolenaar 544fe386641SBram Moolenaar for msg in msgs 545fe386641SBram Moolenaar " remove prefixed NL 546fe386641SBram Moolenaar if msg[0] == "\n" 547fe386641SBram Moolenaar let msg = msg[1:] 548fe386641SBram Moolenaar endif 549fe386641SBram Moolenaar if msg != '' 5501b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 551e09ba7baSBram Moolenaar call s:HandleCursor(msg) 55245d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 553e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 554e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 555e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 5564551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 5574551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 55845d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 55945d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 56045d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 56145d5f26dSBram Moolenaar call s:HandleError(msg) 562e09ba7baSBram Moolenaar endif 563e09ba7baSBram Moolenaar endif 564e09ba7baSBram Moolenaar endfor 565e09ba7baSBram Moolenaarendfunc 566e09ba7baSBram Moolenaar 567e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 568e09ba7baSBram Moolenaarfunc s:InstallCommands() 569*963c1ad5SBram Moolenaar let save_cpo = &cpo 570*963c1ad5SBram Moolenaar set cpo&vim 571*963c1ad5SBram Moolenaar 572e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 57371137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 574e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 57545d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 576e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 57760e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 57860e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 57960e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 580b3307b5eSBram Moolenaar 581b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 582b3307b5eSBram Moolenaar if s:way == 'prompt' 583b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 584b3307b5eSBram Moolenaar else 585b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 586b3307b5eSBram Moolenaar endif 587b3307b5eSBram Moolenaar 58845d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 58945d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 59045d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 591b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 59271137fedSBram Moolenaar command Winbar call s:InstallWinbar() 59345d5f26dSBram Moolenaar 59445d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 59545d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 5961b9645deSBram Moolenaar 597f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 59871137fedSBram Moolenaar call s:InstallWinbar() 59971137fedSBram Moolenaar 60071137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 60171137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 60271137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 60371137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 60471137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 60571137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 60671137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 60771137fedSBram Moolenaar endif 60871137fedSBram Moolenaar endif 609*963c1ad5SBram Moolenaar 610*963c1ad5SBram Moolenaar let &cpo = save_cpo 61171137fedSBram Moolenaarendfunc 61271137fedSBram Moolenaar 61371137fedSBram Moolenaarlet s:winbar_winids = [] 61471137fedSBram Moolenaar 61571137fedSBram Moolenaar" Install the window toolbar in the current window. 61671137fedSBram Moolenaarfunc s:InstallWinbar() 617c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 61824a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 61924a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 62024a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 62124a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 62260e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 62324a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 62471137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 625c4b533e1SBram Moolenaar endif 626e09ba7baSBram Moolenaarendfunc 627e09ba7baSBram Moolenaar 628e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 629e09ba7baSBram Moolenaarfunc s:DeleteCommands() 630e09ba7baSBram Moolenaar delcommand Break 63171137fedSBram Moolenaar delcommand Clear 632e09ba7baSBram Moolenaar delcommand Step 63345d5f26dSBram Moolenaar delcommand Over 634e09ba7baSBram Moolenaar delcommand Finish 63560e73f2aSBram Moolenaar delcommand Run 63660e73f2aSBram Moolenaar delcommand Arguments 63760e73f2aSBram Moolenaar delcommand Stop 638e09ba7baSBram Moolenaar delcommand Continue 63945d5f26dSBram Moolenaar delcommand Evaluate 64045d5f26dSBram Moolenaar delcommand Gdb 64145d5f26dSBram Moolenaar delcommand Program 642b3623a38SBram Moolenaar delcommand Source 64371137fedSBram Moolenaar delcommand Winbar 64445d5f26dSBram Moolenaar 64545d5f26dSBram Moolenaar nunmap K 6461b9645deSBram Moolenaar 6471b9645deSBram Moolenaar if has('menu') 64871137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 64971137fedSBram Moolenaar let curwinid = win_getid(winnr()) 65071137fedSBram Moolenaar for winid in s:winbar_winids 65171137fedSBram Moolenaar if win_gotoid(winid) 6521b9645deSBram Moolenaar aunmenu WinBar.Step 6531b9645deSBram Moolenaar aunmenu WinBar.Next 6541b9645deSBram Moolenaar aunmenu WinBar.Finish 6551b9645deSBram Moolenaar aunmenu WinBar.Cont 65660e73f2aSBram Moolenaar aunmenu WinBar.Stop 6571b9645deSBram Moolenaar aunmenu WinBar.Eval 6581b9645deSBram Moolenaar endif 65971137fedSBram Moolenaar endfor 66071137fedSBram Moolenaar call win_gotoid(curwinid) 66171137fedSBram Moolenaar let s:winbar_winids = [] 66271137fedSBram Moolenaar 66371137fedSBram Moolenaar if exists('s:saved_mousemodel') 66471137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 66571137fedSBram Moolenaar unlet s:saved_mousemodel 66671137fedSBram Moolenaar aunmenu PopUp.-SEP3- 66771137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 66871137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 66971137fedSBram Moolenaar aunmenu PopUp.Evaluate 67071137fedSBram Moolenaar endif 67171137fedSBram Moolenaar endif 6721b9645deSBram Moolenaar 67345d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 67445d5f26dSBram Moolenaar for key in keys(s:breakpoints) 67545d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 67645d5f26dSBram Moolenaar endfor 67745d5f26dSBram Moolenaar unlet s:breakpoints 678a15b0a93SBram Moolenaar 679a15b0a93SBram Moolenaar sign undefine debugPC 680a15b0a93SBram Moolenaar for val in s:BreakpointSigns 681a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 682a15b0a93SBram Moolenaar endfor 6834551c0a9SBram Moolenaar let s:BreakpointSigns = [] 684e09ba7baSBram Moolenaarendfunc 685e09ba7baSBram Moolenaar 686e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 687e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 68860e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 68960e73f2aSBram Moolenaar " Interrupt to make it work. 69060e73f2aSBram Moolenaar let do_continue = 0 69160e73f2aSBram Moolenaar if !s:stopped 69260e73f2aSBram Moolenaar let do_continue = 1 693b3307b5eSBram Moolenaar if s:way == 'prompt' 6944551c0a9SBram Moolenaar call s:PromptInterrupt() 695b3307b5eSBram Moolenaar else 69660e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 697b3307b5eSBram Moolenaar endif 69860e73f2aSBram Moolenaar sleep 10m 69960e73f2aSBram Moolenaar endif 700a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 701a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 702a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 70360e73f2aSBram Moolenaar if do_continue 70460e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 70560e73f2aSBram Moolenaar endif 706e09ba7baSBram Moolenaarendfunc 707e09ba7baSBram Moolenaar 70871137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 70971137fedSBram Moolenaarfunc s:ClearBreakpoint() 710e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 711e09ba7baSBram Moolenaar let lnum = line('.') 712e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 713e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 714b3307b5eSBram Moolenaar call s:SendCommand('-break-delete ' . key) 715e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 716e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 717e09ba7baSBram Moolenaar unlet s:breakpoints[key] 718e09ba7baSBram Moolenaar break 719e09ba7baSBram Moolenaar endif 720e09ba7baSBram Moolenaar endfor 721e09ba7baSBram Moolenaarendfunc 722e09ba7baSBram Moolenaar 72360e73f2aSBram Moolenaarfunc s:Run(args) 72460e73f2aSBram Moolenaar if a:args != '' 72560e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 72660e73f2aSBram Moolenaar endif 72760e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 72860e73f2aSBram Moolenaarendfunc 72960e73f2aSBram Moolenaar 73051b0f370SBram Moolenaarfunc s:SendEval(expr) 73151b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 73251b0f370SBram Moolenaar let s:evalexpr = a:expr 73351b0f370SBram Moolenaarendfunc 73451b0f370SBram Moolenaar 73545d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 73645d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 73745d5f26dSBram Moolenaar if a:arg != '' 73845d5f26dSBram Moolenaar let expr = a:arg 73945d5f26dSBram Moolenaar elseif a:range == 2 74045d5f26dSBram Moolenaar let pos = getcurpos() 74145d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 74245d5f26dSBram Moolenaar let regt = getregtype('v') 74345d5f26dSBram Moolenaar normal! gv"vy 74445d5f26dSBram Moolenaar let expr = @v 74545d5f26dSBram Moolenaar call setpos('.', pos) 74645d5f26dSBram Moolenaar call setreg('v', reg, regt) 74745d5f26dSBram Moolenaar else 74845d5f26dSBram Moolenaar let expr = expand('<cexpr>') 74945d5f26dSBram Moolenaar endif 75022f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 75151b0f370SBram Moolenaar call s:SendEval(expr) 75245d5f26dSBram Moolenaarendfunc 75345d5f26dSBram Moolenaar 75422f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 75551b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 75651b0f370SBram Moolenaar 75745d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 75845d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 7591b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 7601b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 76151b0f370SBram Moolenaar if s:evalFromBalloonExpr 76251b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 76351b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 76451b0f370SBram Moolenaar else 76551b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 76651b0f370SBram Moolenaar endif 76751b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 76851b0f370SBram Moolenaar else 7691b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 77051b0f370SBram Moolenaar endif 7711b9645deSBram Moolenaar 7727f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 7731b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 77422f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 77551b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 77651b0f370SBram Moolenaar else 77751b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 7781b9645deSBram Moolenaar endif 77945d5f26dSBram Moolenaarendfunc 78045d5f26dSBram Moolenaar 78151b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 78251b0f370SBram Moolenaar" if there is any. 78351b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 784b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 785b3307b5eSBram Moolenaar return 786b3307b5eSBram Moolenaar endif 787b3307b5eSBram Moolenaar if !s:stopped 788b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 789b3307b5eSBram Moolenaar " mouse triggers a balloon. 79051b0f370SBram Moolenaar return 79151b0f370SBram Moolenaar endif 79251b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 79351b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 79422f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 79522f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 79651b0f370SBram Moolenaar return '' 79751b0f370SBram Moolenaarendfunc 79851b0f370SBram Moolenaar 79945d5f26dSBram Moolenaar" Handle an error. 80045d5f26dSBram Moolenaarfunc s:HandleError(msg) 80122f1d0e3SBram Moolenaar if s:ignoreEvalError 80251b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 80322f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 80422f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 80551b0f370SBram Moolenaar return 80651b0f370SBram Moolenaar endif 80745d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 80845d5f26dSBram Moolenaarendfunc 80945d5f26dSBram Moolenaar 810b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 811b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 812c4b533e1SBram Moolenaar new 813b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 814c4b533e1SBram Moolenaar call s:InstallWinbar() 815c4b533e1SBram Moolenaar endif 816c4b533e1SBram Moolenaarendfunc 817c4b533e1SBram Moolenaar 818e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 819e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 820e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 821fe386641SBram Moolenaar let wid = win_getid(winnr()) 822fe386641SBram Moolenaar 82360e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8244551c0a9SBram Moolenaar call ch_log('program stopped') 82560e73f2aSBram Moolenaar let s:stopped = 1 82660e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8274551c0a9SBram Moolenaar call ch_log('program running') 82860e73f2aSBram Moolenaar let s:stopped = 0 82960e73f2aSBram Moolenaar endif 83060e73f2aSBram Moolenaar 831a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 832a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 833a15b0a93SBram Moolenaar else 834a15b0a93SBram Moolenaar let fname = '' 835a15b0a93SBram Moolenaar endif 8361b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 837e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 838fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8394551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 8401b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 841fe386641SBram Moolenaar if &modified 842fe386641SBram Moolenaar " TODO: find existing window 843fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 844b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 845c4b533e1SBram Moolenaar call s:InstallWinbar() 846fe386641SBram Moolenaar else 847fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 848fe386641SBram Moolenaar endif 849fe386641SBram Moolenaar endif 850fe386641SBram Moolenaar exe lnum 85101164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 8521b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 853fe386641SBram Moolenaar setlocal signcolumn=yes 854fe386641SBram Moolenaar endif 8554551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 856fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 857fe386641SBram Moolenaar endif 858fe386641SBram Moolenaar 859fe386641SBram Moolenaar call win_gotoid(wid) 860e09ba7baSBram Moolenaarendfunc 861e09ba7baSBram Moolenaar 862de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 863a15b0a93SBram Moolenaar 864a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr) 865de1a8314SBram Moolenaar if index(s:BreakpointSigns, a:nr) == -1 866de1a8314SBram Moolenaar call add(s:BreakpointSigns, a:nr) 867de1a8314SBram Moolenaar exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint" 868de1a8314SBram Moolenaar endif 869de1a8314SBram Moolenaarendfunc 870de1a8314SBram Moolenaar 871e09ba7baSBram Moolenaar" Handle setting a breakpoint 872e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 873e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 8746dccc962SBram Moolenaar if a:msg !~ 'fullname=' 8756dccc962SBram Moolenaar " a watch does not have a file name 8766dccc962SBram Moolenaar return 8776dccc962SBram Moolenaar endif 8786dccc962SBram Moolenaar 879e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 880e09ba7baSBram Moolenaar if nr == 0 881e09ba7baSBram Moolenaar return 882fe386641SBram Moolenaar endif 883de1a8314SBram Moolenaar call s:CreateBreakpoint(nr) 884e09ba7baSBram Moolenaar 885e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 886e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 887e09ba7baSBram Moolenaar else 888e09ba7baSBram Moolenaar let entry = {} 889e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 890fe386641SBram Moolenaar endif 891e09ba7baSBram Moolenaar 892a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 893e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 894e09ba7baSBram Moolenaar let entry['fname'] = fname 895e09ba7baSBram Moolenaar let entry['lnum'] = lnum 8961b9645deSBram Moolenaar 8971b9645deSBram Moolenaar if bufloaded(fname) 8981b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 8991b9645deSBram Moolenaar endif 9001b9645deSBram Moolenaarendfunc 9011b9645deSBram Moolenaar 9021b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 903de1a8314SBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname'] 9041b9645deSBram Moolenaar let a:entry['placed'] = 1 905e09ba7baSBram Moolenaarendfunc 906e09ba7baSBram Moolenaar 907e09ba7baSBram Moolenaar" Handle deleting a breakpoint 908e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 909e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 910e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 911e09ba7baSBram Moolenaar if nr == 0 912e09ba7baSBram Moolenaar return 913e09ba7baSBram Moolenaar endif 9141b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 9151b9645deSBram Moolenaar let entry = s:breakpoints[nr] 9161b9645deSBram Moolenaar if has_key(entry, 'placed') 917e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 9181b9645deSBram Moolenaar unlet entry['placed'] 9191b9645deSBram Moolenaar endif 920e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 9211b9645deSBram Moolenaar endif 922c572da5fSBram Moolenaarendfunc 9231b9645deSBram Moolenaar 9244551c0a9SBram Moolenaar" Handle the debugged program starting to run. 9254551c0a9SBram Moolenaar" Will store the process ID in s:pid 9264551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 9274551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 9284551c0a9SBram Moolenaar if nr == 0 9294551c0a9SBram Moolenaar return 9304551c0a9SBram Moolenaar endif 9314551c0a9SBram Moolenaar let s:pid = nr 9324551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 9334551c0a9SBram Moolenaarendfunc 9344551c0a9SBram Moolenaar 9351b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 9361b9645deSBram Moolenaarfunc s:BufRead() 9371b9645deSBram Moolenaar let fname = expand('<afile>:p') 9381b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9391b9645deSBram Moolenaar if entry['fname'] == fname 9401b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 9411b9645deSBram Moolenaar endif 9421b9645deSBram Moolenaar endfor 9431b9645deSBram Moolenaarendfunc 9441b9645deSBram Moolenaar 9451b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 9461b9645deSBram Moolenaarfunc s:BufUnloaded() 9471b9645deSBram Moolenaar let fname = expand('<afile>:p') 9481b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9491b9645deSBram Moolenaar if entry['fname'] == fname 9501b9645deSBram Moolenaar let entry['placed'] = 0 9511b9645deSBram Moolenaar endif 9521b9645deSBram Moolenaar endfor 9531b9645deSBram Moolenaarendfunc 954ca4cc018SBram Moolenaar 955ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 956ca4cc018SBram Moolenaarunlet s:keepcpo 957