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 ' 203*f63db65bSBram Moolenaar " response can be in the same line or the next line 204*f63db65bSBram Moolenaar let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1) 2053e4b84d0SBram Moolenaar if response =~ 'Undefined command' 206f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 2073e4b84d0SBram Moolenaar exe 'bwipe! ' . s:ptybuf 2083e4b84d0SBram Moolenaar exe 'bwipe! ' . s:commbuf 2093e4b84d0SBram Moolenaar return 2103e4b84d0SBram Moolenaar endif 2113e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2123e4b84d0SBram Moolenaar " Success! 2133e4b84d0SBram Moolenaar break 2143e4b84d0SBram Moolenaar endif 2153e4b84d0SBram Moolenaar endif 2163e4b84d0SBram Moolenaar endfor 2173e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2183e4b84d0SBram Moolenaar break 2193e4b84d0SBram Moolenaar endif 2203e4b84d0SBram Moolenaar let try_count += 1 2213e4b84d0SBram Moolenaar if try_count > 100 2223e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2233e4b84d0SBram Moolenaar break 2243e4b84d0SBram Moolenaar endif 2253e4b84d0SBram Moolenaar sleep 10m 2263e4b84d0SBram Moolenaar endwhile 2273e4b84d0SBram Moolenaar 22860e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 22960e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 23060e73f2aSBram Moolenaar " running. 23160e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 232b3307b5eSBram Moolenaar " Older gdb uses a different command. 233b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 234e09ba7baSBram Moolenaar 235f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 236f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 237b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 238f3ba14ffSBram Moolenaar 239b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 240b3307b5eSBram Moolenaarendfunc 241b3307b5eSBram Moolenaar 242b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 243b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 244b3307b5eSBram Moolenaar if s:vertical 245b3307b5eSBram Moolenaar vertical new 246b3307b5eSBram Moolenaar else 247b3307b5eSBram Moolenaar new 248b3307b5eSBram Moolenaar endif 249b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 250b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 251b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 252b3307b5eSBram Moolenaar set buftype=prompt 253b3307b5eSBram Moolenaar file gdb 254b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 255b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 256b3307b5eSBram Moolenaar 257b3307b5eSBram Moolenaar if s:vertical 258b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 259b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 260b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 261b3307b5eSBram Moolenaar endif 262b3307b5eSBram Moolenaar 263b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 264b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 265b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 266b3307b5eSBram Moolenaar 267b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 268b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 269b3307b5eSBram Moolenaar 270b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 271b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 272b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 273b3307b5eSBram Moolenaar \ }) 274b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 275b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 276b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 277b3307b5eSBram Moolenaar return 278b3307b5eSBram Moolenaar endif 2794551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 2804551c0a9SBram Moolenaar set modified 281b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 282b3307b5eSBram Moolenaar 283b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 284b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 285b3307b5eSBram Moolenaar " target is running. 286b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 287b3307b5eSBram Moolenaar " Older gdb uses a different command. 288b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 289b3307b5eSBram Moolenaar 290b3307b5eSBram Moolenaar let s:ptybuf = 0 291b3307b5eSBram Moolenaar if has('win32') 292b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 293b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 294b3307b5eSBram Moolenaar elseif has('terminal') 295b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 296b3307b5eSBram Moolenaar " gdb window. 297b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 298b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 299b3307b5eSBram Moolenaar \ }) 300b3307b5eSBram Moolenaar if s:ptybuf == 0 301b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 302b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 303b3307b5eSBram Moolenaar return 304b3307b5eSBram Moolenaar endif 305b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 306b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 307b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 308b3307b5eSBram Moolenaar 309b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 310b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 311b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 312b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 313b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 314b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 315b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 316b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 317b3307b5eSBram Moolenaar else 318b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 319b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 320b3307b5eSBram Moolenaar endif 321b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 322b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 323b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 324b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 325b3307b5eSBram Moolenaar 326b3307b5eSBram Moolenaar " Set arguments to be run 327b3307b5eSBram Moolenaar if len(proc_args) 328b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 329b3307b5eSBram Moolenaar endif 330b3307b5eSBram Moolenaar 331b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 332b3307b5eSBram Moolenaar startinsert 333b3307b5eSBram Moolenaarendfunc 334b3307b5eSBram Moolenaar 335b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 33638baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 33738baa3e6SBram Moolenaar " There can be only one. 33838baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 33938baa3e6SBram Moolenaar 34045d5f26dSBram Moolenaar " Install debugger commands in the text window. 341b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 342e09ba7baSBram Moolenaar call s:InstallCommands() 34345d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 344e09ba7baSBram Moolenaar 34551b0f370SBram Moolenaar " Enable showing a balloon with eval info 346246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 347246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 34851b0f370SBram Moolenaar if has("balloon_eval") 34951b0f370SBram Moolenaar set ballooneval 350246fe03dSBram Moolenaar endif 35151b0f370SBram Moolenaar if has("balloon_eval_term") 35251b0f370SBram Moolenaar set balloonevalterm 35351b0f370SBram Moolenaar endif 35451b0f370SBram Moolenaar endif 35551b0f370SBram Moolenaar 356de1a8314SBram Moolenaar " Contains breakpoints that have been placed, key is the number. 357e09ba7baSBram Moolenaar let s:breakpoints = {} 3581b9645deSBram Moolenaar 3591b9645deSBram Moolenaar augroup TermDebug 3601b9645deSBram Moolenaar au BufRead * call s:BufRead() 3611b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 362f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 3631b9645deSBram Moolenaar augroup END 36432c67ba7SBram Moolenaar 365b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 366b3307b5eSBram Moolenaar " window. 36732c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 36832c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 36932c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 37032c67ba7SBram Moolenaar endif 371c572da5fSBram Moolenaarendfunc 372c572da5fSBram Moolenaar 373b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 374b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 375b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 376b3307b5eSBram Moolenaar if s:way == 'prompt' 377b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 378b3307b5eSBram Moolenaar else 379b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 380b3307b5eSBram Moolenaar endif 381b3307b5eSBram Moolenaarendfunc 382b3307b5eSBram Moolenaar 383b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 384b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 385b3307b5eSBram Moolenaar if s:way == 'prompt' 386b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 387b3307b5eSBram Moolenaar else 388b3307b5eSBram Moolenaar let do_continue = 0 389b3307b5eSBram Moolenaar if !s:stopped 390b3307b5eSBram Moolenaar let do_continue = 1 391b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 392b3307b5eSBram Moolenaar sleep 10m 393b3307b5eSBram Moolenaar endif 394b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 395b3307b5eSBram Moolenaar if do_continue 396b3307b5eSBram Moolenaar Continue 397b3307b5eSBram Moolenaar endif 398b3307b5eSBram Moolenaar endif 399b3307b5eSBram Moolenaarendfunc 400b3307b5eSBram Moolenaar 401b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 402b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 403b3307b5eSBram Moolenaar call s:SendCommand(a:text) 404b3307b5eSBram Moolenaarendfunc 405b3307b5eSBram Moolenaar 4064551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4074551c0a9SBram Moolenaar" breakpoint. 408b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4092ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4102ed890f1SBram Moolenaar if has('win32') 4112ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4122ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4134551c0a9SBram Moolenaar if s:pid == 0 4144551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4154551c0a9SBram Moolenaar else 4164551c0a9SBram Moolenaar call debugbreak(s:pid) 4174551c0a9SBram Moolenaar endif 4182ed890f1SBram Moolenaar else 4192ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4202ed890f1SBram Moolenaar endif 421b3307b5eSBram Moolenaarendfunc 422b3307b5eSBram Moolenaar 423b3307b5eSBram Moolenaar" Function called when gdb outputs text. 424b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 425b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 426b3307b5eSBram Moolenaar 427b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 428b3307b5eSBram Moolenaar " Drop status and echo'd commands. 429a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 430b3307b5eSBram Moolenaar return 431b3307b5eSBram Moolenaar endif 432b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 433b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 434b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 435b3307b5eSBram Moolenaar " Silently drop evaluation errors. 436b3307b5eSBram Moolenaar unlet s:evalexpr 437b3307b5eSBram Moolenaar return 438b3307b5eSBram Moolenaar endif 439b3307b5eSBram Moolenaar elseif a:text[0] == '~' 440b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 441b3307b5eSBram Moolenaar else 442b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 443b3307b5eSBram Moolenaar return 444b3307b5eSBram Moolenaar endif 445b3307b5eSBram Moolenaar 446b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 447b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 448b3307b5eSBram Moolenaar 449b3307b5eSBram Moolenaar " Add the output above the current prompt. 450b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4514551c0a9SBram Moolenaar set modified 452b3307b5eSBram Moolenaar 453b3307b5eSBram Moolenaar call win_gotoid(curwinid) 454b3307b5eSBram Moolenaarendfunc 455b3307b5eSBram Moolenaar 456b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 457b3307b5eSBram Moolenaar" to the next ", unescaping characters. 458b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 459b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 460a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 461b3307b5eSBram Moolenaar return 462b3307b5eSBram Moolenaar endif 463b3307b5eSBram Moolenaar let result = '' 464b3307b5eSBram Moolenaar let i = 1 465b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 466b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 467b3307b5eSBram Moolenaar let i += 1 468b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 469b3307b5eSBram Moolenaar " drop \n 470b3307b5eSBram Moolenaar let i += 1 471b3307b5eSBram Moolenaar continue 472b3307b5eSBram Moolenaar endif 473b3307b5eSBram Moolenaar endif 474b3307b5eSBram Moolenaar let result .= a:quotedText[i] 475b3307b5eSBram Moolenaar let i += 1 476b3307b5eSBram Moolenaar endwhile 477b3307b5eSBram Moolenaar return result 478b3307b5eSBram Moolenaarendfunc 479b3307b5eSBram Moolenaar 480a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 481a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 482a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 483a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 484a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 485a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 486a15b0a93SBram Moolenaar endif 487a15b0a93SBram Moolenaar return name 488a15b0a93SBram Moolenaarendfunc 489a15b0a93SBram Moolenaar 490b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 491fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 492b3623a38SBram Moolenaar unlet s:gdbwin 493e09ba7baSBram Moolenaar 494b3307b5eSBram Moolenaar call s:EndDebugCommon() 495b3307b5eSBram Moolenaarendfunc 496b3307b5eSBram Moolenaar 497b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 498e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 499e09ba7baSBram Moolenaar 500b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 501b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 502b3307b5eSBram Moolenaar endif 503b3307b5eSBram Moolenaar 504b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 505e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 506e09ba7baSBram Moolenaar call s:DeleteCommands() 507e09ba7baSBram Moolenaar 508e09ba7baSBram Moolenaar call win_gotoid(curwinid) 509b3307b5eSBram Moolenaar 51038baa3e6SBram Moolenaar if s:save_columns > 0 51138baa3e6SBram Moolenaar let &columns = s:save_columns 51238baa3e6SBram Moolenaar endif 5131b9645deSBram Moolenaar 514246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 515246fe03dSBram Moolenaar set balloonexpr= 51651b0f370SBram Moolenaar if has("balloon_eval") 51751b0f370SBram Moolenaar set noballooneval 518246fe03dSBram Moolenaar endif 51951b0f370SBram Moolenaar if has("balloon_eval_term") 52051b0f370SBram Moolenaar set noballoonevalterm 52151b0f370SBram Moolenaar endif 52251b0f370SBram Moolenaar endif 52351b0f370SBram Moolenaar 5241b9645deSBram Moolenaar au! TermDebug 525fe386641SBram Moolenaarendfunc 526fe386641SBram Moolenaar 527b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 528b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 529b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5304551c0a9SBram Moolenaar set nomodified 531b3307b5eSBram Moolenaar close 532b3307b5eSBram Moolenaar if curwinid != s:gdbwin 533b3307b5eSBram Moolenaar call win_gotoid(curwinid) 534b3307b5eSBram Moolenaar endif 535b3307b5eSBram Moolenaar 536b3307b5eSBram Moolenaar call s:EndDebugCommon() 537b3307b5eSBram Moolenaar unlet s:gdbwin 538b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 539b3307b5eSBram Moolenaarendfunc 540b3307b5eSBram Moolenaar 541fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 542fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 543fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 544fe386641SBram Moolenaar 545fe386641SBram Moolenaar for msg in msgs 546fe386641SBram Moolenaar " remove prefixed NL 547fe386641SBram Moolenaar if msg[0] == "\n" 548fe386641SBram Moolenaar let msg = msg[1:] 549fe386641SBram Moolenaar endif 550fe386641SBram Moolenaar if msg != '' 5511b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 552e09ba7baSBram Moolenaar call s:HandleCursor(msg) 55345d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 554e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 555e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 556e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 5574551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 5584551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 55945d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 56045d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 56145d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 56245d5f26dSBram Moolenaar call s:HandleError(msg) 563e09ba7baSBram Moolenaar endif 564e09ba7baSBram Moolenaar endif 565e09ba7baSBram Moolenaar endfor 566e09ba7baSBram Moolenaarendfunc 567e09ba7baSBram Moolenaar 568e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 569e09ba7baSBram Moolenaarfunc s:InstallCommands() 570963c1ad5SBram Moolenaar let save_cpo = &cpo 571963c1ad5SBram Moolenaar set cpo&vim 572963c1ad5SBram Moolenaar 573e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 57471137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 575e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 57645d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 577e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 57860e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 57960e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 58060e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 581b3307b5eSBram Moolenaar 582b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 583b3307b5eSBram Moolenaar if s:way == 'prompt' 584b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 585b3307b5eSBram Moolenaar else 586b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 587b3307b5eSBram Moolenaar endif 588b3307b5eSBram Moolenaar 58945d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 59045d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 59145d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 592b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 59371137fedSBram Moolenaar command Winbar call s:InstallWinbar() 59445d5f26dSBram Moolenaar 59545d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 59645d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 5971b9645deSBram Moolenaar 598f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 59971137fedSBram Moolenaar call s:InstallWinbar() 60071137fedSBram Moolenaar 60171137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 60271137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 60371137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 60471137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 60571137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 60671137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 60771137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 60871137fedSBram Moolenaar endif 60971137fedSBram Moolenaar endif 610963c1ad5SBram Moolenaar 611963c1ad5SBram Moolenaar let &cpo = save_cpo 61271137fedSBram Moolenaarendfunc 61371137fedSBram Moolenaar 61471137fedSBram Moolenaarlet s:winbar_winids = [] 61571137fedSBram Moolenaar 61671137fedSBram Moolenaar" Install the window toolbar in the current window. 61771137fedSBram Moolenaarfunc s:InstallWinbar() 618c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 61924a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 62024a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 62124a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 62224a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 62360e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 62424a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 62571137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 626c4b533e1SBram Moolenaar endif 627e09ba7baSBram Moolenaarendfunc 628e09ba7baSBram Moolenaar 629e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 630e09ba7baSBram Moolenaarfunc s:DeleteCommands() 631e09ba7baSBram Moolenaar delcommand Break 63271137fedSBram Moolenaar delcommand Clear 633e09ba7baSBram Moolenaar delcommand Step 63445d5f26dSBram Moolenaar delcommand Over 635e09ba7baSBram Moolenaar delcommand Finish 63660e73f2aSBram Moolenaar delcommand Run 63760e73f2aSBram Moolenaar delcommand Arguments 63860e73f2aSBram Moolenaar delcommand Stop 639e09ba7baSBram Moolenaar delcommand Continue 64045d5f26dSBram Moolenaar delcommand Evaluate 64145d5f26dSBram Moolenaar delcommand Gdb 64245d5f26dSBram Moolenaar delcommand Program 643b3623a38SBram Moolenaar delcommand Source 64471137fedSBram Moolenaar delcommand Winbar 64545d5f26dSBram Moolenaar 64645d5f26dSBram Moolenaar nunmap K 6471b9645deSBram Moolenaar 6481b9645deSBram Moolenaar if has('menu') 64971137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 65071137fedSBram Moolenaar let curwinid = win_getid(winnr()) 65171137fedSBram Moolenaar for winid in s:winbar_winids 65271137fedSBram Moolenaar if win_gotoid(winid) 6531b9645deSBram Moolenaar aunmenu WinBar.Step 6541b9645deSBram Moolenaar aunmenu WinBar.Next 6551b9645deSBram Moolenaar aunmenu WinBar.Finish 6561b9645deSBram Moolenaar aunmenu WinBar.Cont 65760e73f2aSBram Moolenaar aunmenu WinBar.Stop 6581b9645deSBram Moolenaar aunmenu WinBar.Eval 6591b9645deSBram Moolenaar endif 66071137fedSBram Moolenaar endfor 66171137fedSBram Moolenaar call win_gotoid(curwinid) 66271137fedSBram Moolenaar let s:winbar_winids = [] 66371137fedSBram Moolenaar 66471137fedSBram Moolenaar if exists('s:saved_mousemodel') 66571137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 66671137fedSBram Moolenaar unlet s:saved_mousemodel 66771137fedSBram Moolenaar aunmenu PopUp.-SEP3- 66871137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 66971137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 67071137fedSBram Moolenaar aunmenu PopUp.Evaluate 67171137fedSBram Moolenaar endif 67271137fedSBram Moolenaar endif 6731b9645deSBram Moolenaar 67445d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 67545d5f26dSBram Moolenaar for key in keys(s:breakpoints) 67645d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 67745d5f26dSBram Moolenaar endfor 67845d5f26dSBram Moolenaar unlet s:breakpoints 679a15b0a93SBram Moolenaar 680a15b0a93SBram Moolenaar sign undefine debugPC 681a15b0a93SBram Moolenaar for val in s:BreakpointSigns 682a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 683a15b0a93SBram Moolenaar endfor 6844551c0a9SBram Moolenaar let s:BreakpointSigns = [] 685e09ba7baSBram Moolenaarendfunc 686e09ba7baSBram Moolenaar 687e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 688e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 68960e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 69060e73f2aSBram Moolenaar " Interrupt to make it work. 69160e73f2aSBram Moolenaar let do_continue = 0 69260e73f2aSBram Moolenaar if !s:stopped 69360e73f2aSBram Moolenaar let do_continue = 1 694b3307b5eSBram Moolenaar if s:way == 'prompt' 6954551c0a9SBram Moolenaar call s:PromptInterrupt() 696b3307b5eSBram Moolenaar else 69760e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 698b3307b5eSBram Moolenaar endif 69960e73f2aSBram Moolenaar sleep 10m 70060e73f2aSBram Moolenaar endif 701a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 702a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 703a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 70460e73f2aSBram Moolenaar if do_continue 70560e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 70660e73f2aSBram Moolenaar endif 707e09ba7baSBram Moolenaarendfunc 708e09ba7baSBram Moolenaar 70971137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 71071137fedSBram Moolenaarfunc s:ClearBreakpoint() 711e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 712e09ba7baSBram Moolenaar let lnum = line('.') 713e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 714e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 715b3307b5eSBram Moolenaar call s:SendCommand('-break-delete ' . key) 716e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 717e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 718e09ba7baSBram Moolenaar unlet s:breakpoints[key] 719e09ba7baSBram Moolenaar break 720e09ba7baSBram Moolenaar endif 721e09ba7baSBram Moolenaar endfor 722e09ba7baSBram Moolenaarendfunc 723e09ba7baSBram Moolenaar 72460e73f2aSBram Moolenaarfunc s:Run(args) 72560e73f2aSBram Moolenaar if a:args != '' 72660e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 72760e73f2aSBram Moolenaar endif 72860e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 72960e73f2aSBram Moolenaarendfunc 73060e73f2aSBram Moolenaar 73151b0f370SBram Moolenaarfunc s:SendEval(expr) 73251b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 73351b0f370SBram Moolenaar let s:evalexpr = a:expr 73451b0f370SBram Moolenaarendfunc 73551b0f370SBram Moolenaar 73645d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 73745d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 73845d5f26dSBram Moolenaar if a:arg != '' 73945d5f26dSBram Moolenaar let expr = a:arg 74045d5f26dSBram Moolenaar elseif a:range == 2 74145d5f26dSBram Moolenaar let pos = getcurpos() 74245d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 74345d5f26dSBram Moolenaar let regt = getregtype('v') 74445d5f26dSBram Moolenaar normal! gv"vy 74545d5f26dSBram Moolenaar let expr = @v 74645d5f26dSBram Moolenaar call setpos('.', pos) 74745d5f26dSBram Moolenaar call setreg('v', reg, regt) 74845d5f26dSBram Moolenaar else 74945d5f26dSBram Moolenaar let expr = expand('<cexpr>') 75045d5f26dSBram Moolenaar endif 75122f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 75251b0f370SBram Moolenaar call s:SendEval(expr) 75345d5f26dSBram Moolenaarendfunc 75445d5f26dSBram Moolenaar 75522f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 75651b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 75751b0f370SBram Moolenaar 75845d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 75945d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 7601b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 7611b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 76251b0f370SBram Moolenaar if s:evalFromBalloonExpr 76351b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 76451b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 76551b0f370SBram Moolenaar else 76651b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 76751b0f370SBram Moolenaar endif 76851b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 76951b0f370SBram Moolenaar else 7701b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 77151b0f370SBram Moolenaar endif 7721b9645deSBram Moolenaar 7737f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 7741b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 77522f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 77651b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 77751b0f370SBram Moolenaar else 77851b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 7791b9645deSBram Moolenaar endif 78045d5f26dSBram Moolenaarendfunc 78145d5f26dSBram Moolenaar 78251b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 78351b0f370SBram Moolenaar" if there is any. 78451b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 785b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 786b3307b5eSBram Moolenaar return 787b3307b5eSBram Moolenaar endif 788b3307b5eSBram Moolenaar if !s:stopped 789b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 790b3307b5eSBram Moolenaar " mouse triggers a balloon. 79151b0f370SBram Moolenaar return 79251b0f370SBram Moolenaar endif 79351b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 79451b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 79522f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 79622f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 79751b0f370SBram Moolenaar return '' 79851b0f370SBram Moolenaarendfunc 79951b0f370SBram Moolenaar 80045d5f26dSBram Moolenaar" Handle an error. 80145d5f26dSBram Moolenaarfunc s:HandleError(msg) 80222f1d0e3SBram Moolenaar if s:ignoreEvalError 80351b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 80422f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 80522f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 80651b0f370SBram Moolenaar return 80751b0f370SBram Moolenaar endif 80845d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 80945d5f26dSBram Moolenaarendfunc 81045d5f26dSBram Moolenaar 811b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 812b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 813c4b533e1SBram Moolenaar new 814b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 815c4b533e1SBram Moolenaar call s:InstallWinbar() 816c4b533e1SBram Moolenaar endif 817c4b533e1SBram Moolenaarendfunc 818c4b533e1SBram Moolenaar 819e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 820e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 821e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 822fe386641SBram Moolenaar let wid = win_getid(winnr()) 823fe386641SBram Moolenaar 82460e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8254551c0a9SBram Moolenaar call ch_log('program stopped') 82660e73f2aSBram Moolenaar let s:stopped = 1 82760e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8284551c0a9SBram Moolenaar call ch_log('program running') 82960e73f2aSBram Moolenaar let s:stopped = 0 83060e73f2aSBram Moolenaar endif 83160e73f2aSBram Moolenaar 832a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 833a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 834a15b0a93SBram Moolenaar else 835a15b0a93SBram Moolenaar let fname = '' 836a15b0a93SBram Moolenaar endif 8371b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 838e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 839fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8404551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 8411b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 842fe386641SBram Moolenaar if &modified 843fe386641SBram Moolenaar " TODO: find existing window 844fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 845b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 846c4b533e1SBram Moolenaar call s:InstallWinbar() 847fe386641SBram Moolenaar else 848fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 849fe386641SBram Moolenaar endif 850fe386641SBram Moolenaar endif 851fe386641SBram Moolenaar exe lnum 85201164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 8531b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 854fe386641SBram Moolenaar setlocal signcolumn=yes 855fe386641SBram Moolenaar endif 8564551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 857fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 858fe386641SBram Moolenaar endif 859fe386641SBram Moolenaar 860fe386641SBram Moolenaar call win_gotoid(wid) 861e09ba7baSBram Moolenaarendfunc 862e09ba7baSBram Moolenaar 863de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 864a15b0a93SBram Moolenaar 865a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr) 866de1a8314SBram Moolenaar if index(s:BreakpointSigns, a:nr) == -1 867de1a8314SBram Moolenaar call add(s:BreakpointSigns, a:nr) 868de1a8314SBram Moolenaar exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint" 869de1a8314SBram Moolenaar endif 870de1a8314SBram Moolenaarendfunc 871de1a8314SBram Moolenaar 872e09ba7baSBram Moolenaar" Handle setting a breakpoint 873e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 874e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 8756dccc962SBram Moolenaar if a:msg !~ 'fullname=' 8766dccc962SBram Moolenaar " a watch does not have a file name 8776dccc962SBram Moolenaar return 8786dccc962SBram Moolenaar endif 8796dccc962SBram Moolenaar 880e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 881e09ba7baSBram Moolenaar if nr == 0 882e09ba7baSBram Moolenaar return 883fe386641SBram Moolenaar endif 884de1a8314SBram Moolenaar call s:CreateBreakpoint(nr) 885e09ba7baSBram Moolenaar 886e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 887e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 888e09ba7baSBram Moolenaar else 889e09ba7baSBram Moolenaar let entry = {} 890e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 891fe386641SBram Moolenaar endif 892e09ba7baSBram Moolenaar 893a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 894e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 895e09ba7baSBram Moolenaar let entry['fname'] = fname 896e09ba7baSBram Moolenaar let entry['lnum'] = lnum 8971b9645deSBram Moolenaar 8981b9645deSBram Moolenaar if bufloaded(fname) 8991b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 9001b9645deSBram Moolenaar endif 9011b9645deSBram Moolenaarendfunc 9021b9645deSBram Moolenaar 9031b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 904de1a8314SBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname'] 9051b9645deSBram Moolenaar let a:entry['placed'] = 1 906e09ba7baSBram Moolenaarendfunc 907e09ba7baSBram Moolenaar 908e09ba7baSBram Moolenaar" Handle deleting a breakpoint 909e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 910e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 911e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 912e09ba7baSBram Moolenaar if nr == 0 913e09ba7baSBram Moolenaar return 914e09ba7baSBram Moolenaar endif 9151b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 9161b9645deSBram Moolenaar let entry = s:breakpoints[nr] 9171b9645deSBram Moolenaar if has_key(entry, 'placed') 918e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 9191b9645deSBram Moolenaar unlet entry['placed'] 9201b9645deSBram Moolenaar endif 921e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 9221b9645deSBram Moolenaar endif 923c572da5fSBram Moolenaarendfunc 9241b9645deSBram Moolenaar 9254551c0a9SBram Moolenaar" Handle the debugged program starting to run. 9264551c0a9SBram Moolenaar" Will store the process ID in s:pid 9274551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 9284551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 9294551c0a9SBram Moolenaar if nr == 0 9304551c0a9SBram Moolenaar return 9314551c0a9SBram Moolenaar endif 9324551c0a9SBram Moolenaar let s:pid = nr 9334551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 9344551c0a9SBram Moolenaarendfunc 9354551c0a9SBram Moolenaar 9361b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 9371b9645deSBram Moolenaarfunc s:BufRead() 9381b9645deSBram Moolenaar let fname = expand('<afile>:p') 9391b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9401b9645deSBram Moolenaar if entry['fname'] == fname 9411b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 9421b9645deSBram Moolenaar endif 9431b9645deSBram Moolenaar endfor 9441b9645deSBram Moolenaarendfunc 9451b9645deSBram Moolenaar 9461b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 9471b9645deSBram Moolenaarfunc s:BufUnloaded() 9481b9645deSBram Moolenaar let fname = expand('<afile>:p') 9491b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9501b9645deSBram Moolenaar if entry['fname'] == fname 9511b9645deSBram Moolenaar let entry['placed'] = 0 9521b9645deSBram Moolenaar endif 9531b9645deSBram Moolenaar endfor 9541b9645deSBram Moolenaarendfunc 955ca4cc018SBram Moolenaar 956ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 957ca4cc018SBram Moolenaarunlet s:keepcpo 958