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 59fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 60fe386641SBram Moolenaar" To end type "quit" in the gdb window. 6132c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>) 6232c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) 63c572da5fSBram Moolenaar 64fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 65e09ba7baSBram Moolenaarif !exists('termdebugger') 66e09ba7baSBram Moolenaar let termdebugger = 'gdb' 67c572da5fSBram Moolenaarendif 68c572da5fSBram Moolenaar 69fe386641SBram Moolenaarlet s:pc_id = 12 70de1a8314SBram Moolenaarlet s:break_id = 13 " breakpoint number is added to this 7160e73f2aSBram Moolenaarlet s:stopped = 1 72e09ba7baSBram Moolenaar 73f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new) 74f07f9e73SBram Moolenaar let default = a:init ? 'default ' : '' 75f07f9e73SBram Moolenaar if a:new ==# 'light' && a:old !=# 'light' 76f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" 77f07f9e73SBram Moolenaar elseif a:new ==# 'dark' && a:old !=# 'dark' 78f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue" 79e09ba7baSBram Moolenaar endif 80f07f9e73SBram Moolenaarendfunc 81f07f9e73SBram Moolenaar 82f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background) 83e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 84fe386641SBram Moolenaar 8532c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 8632c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 8732c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 8832c67ba7SBram Moolenaarendfunc 8932c67ba7SBram Moolenaar 9032c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 9132c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 9232c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 9332c67ba7SBram Moolenaarendfunc 9432c67ba7SBram Moolenaar 9532c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 96b3623a38SBram Moolenaar if exists('s:gdbwin') 97b3623a38SBram Moolenaar echoerr 'Terminal debugger already running' 98b3623a38SBram Moolenaar return 99b3623a38SBram Moolenaar endif 100b3307b5eSBram Moolenaar let s:ptywin = 0 1014551c0a9SBram Moolenaar let s:pid = 0 102b3623a38SBram Moolenaar 103b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 104b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 105b3307b5eSBram Moolenaar 106b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 107fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 108fe386641SBram Moolenaar 10924a98a0eSBram Moolenaar let s:save_columns = 0 11024a98a0eSBram Moolenaar if exists('g:termdebug_wide') 11124a98a0eSBram Moolenaar if &columns < g:termdebug_wide 11238baa3e6SBram Moolenaar let s:save_columns = &columns 11338baa3e6SBram Moolenaar let &columns = g:termdebug_wide 11424a98a0eSBram Moolenaar endif 115b3307b5eSBram Moolenaar let s:vertical = 1 11638baa3e6SBram Moolenaar else 117b3307b5eSBram Moolenaar let s:vertical = 0 11838baa3e6SBram Moolenaar endif 11938baa3e6SBram Moolenaar 120b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 121b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 122b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 123b3307b5eSBram Moolenaar let s:way = 'terminal' 124b3307b5eSBram Moolenaar else 125b3307b5eSBram Moolenaar let s:way = 'prompt' 126b3307b5eSBram Moolenaar endif 127b3307b5eSBram Moolenaar 128b3307b5eSBram Moolenaar if s:way == 'prompt' 129b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 130b3307b5eSBram Moolenaar else 131b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 132b3307b5eSBram Moolenaar endif 133b3307b5eSBram Moolenaarendfunc 134b3307b5eSBram Moolenaar 135b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 136b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 137fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 138b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 139b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 140fe386641SBram Moolenaar \ }) 141fe386641SBram Moolenaar if s:ptybuf == 0 142fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 143fe386641SBram Moolenaar return 144fe386641SBram Moolenaar endif 145fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 14645d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 147b3307b5eSBram Moolenaar if s:vertical 14851b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 14951b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 15051b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 15151b0f370SBram Moolenaar endif 152fe386641SBram Moolenaar 153fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 154fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 155fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 156fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 157fe386641SBram Moolenaar \ 'hidden': 1, 158fe386641SBram Moolenaar \ }) 159fe386641SBram Moolenaar if s:commbuf == 0 160fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 161fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 162fe386641SBram Moolenaar return 163fe386641SBram Moolenaar endif 164fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 165c572da5fSBram Moolenaar 166c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 167c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 16832c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 16932c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 17032c67ba7SBram Moolenaar 17132c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 172b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 17360e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 174b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndTermDebug'), 175fe386641SBram Moolenaar \ 'term_finish': 'close', 176c572da5fSBram Moolenaar \ }) 17760e73f2aSBram Moolenaar if s:gdbbuf == 0 178fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 179fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 180fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 181fe386641SBram Moolenaar return 182fe386641SBram Moolenaar endif 18345d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 184fe386641SBram Moolenaar 18532c67ba7SBram Moolenaar " Set arguments to be run 18632c67ba7SBram Moolenaar if len(proc_args) 18732c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 18832c67ba7SBram Moolenaar endif 18932c67ba7SBram Moolenaar 190fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 19160e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 19260e73f2aSBram Moolenaar 1933e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 1943e4b84d0SBram Moolenaar " why the debugger doesn't work. 1953e4b84d0SBram Moolenaar let try_count = 0 1963e4b84d0SBram Moolenaar while 1 1973e4b84d0SBram Moolenaar let response = '' 198b3623a38SBram Moolenaar for lnum in range(1,200) 1993e4b84d0SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 2003e4b84d0SBram Moolenaar let response = term_getline(s:gdbbuf, lnum + 1) 2013e4b84d0SBram Moolenaar if response =~ 'Undefined command' 202f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 2033e4b84d0SBram Moolenaar exe 'bwipe! ' . s:ptybuf 2043e4b84d0SBram Moolenaar exe 'bwipe! ' . s:commbuf 2053e4b84d0SBram Moolenaar return 2063e4b84d0SBram Moolenaar endif 2073e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2083e4b84d0SBram Moolenaar " Success! 2093e4b84d0SBram Moolenaar break 2103e4b84d0SBram Moolenaar endif 2113e4b84d0SBram Moolenaar endif 2123e4b84d0SBram Moolenaar endfor 2133e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2143e4b84d0SBram Moolenaar break 2153e4b84d0SBram Moolenaar endif 2163e4b84d0SBram Moolenaar let try_count += 1 2173e4b84d0SBram Moolenaar if try_count > 100 2183e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2193e4b84d0SBram Moolenaar break 2203e4b84d0SBram Moolenaar endif 2213e4b84d0SBram Moolenaar sleep 10m 2223e4b84d0SBram Moolenaar endwhile 2233e4b84d0SBram Moolenaar 22460e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 22560e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 22660e73f2aSBram Moolenaar " running. 22760e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 228b3307b5eSBram Moolenaar " Older gdb uses a different command. 229b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 230e09ba7baSBram Moolenaar 231f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 232f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 233b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 234f3ba14ffSBram Moolenaar 235b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 236b3307b5eSBram Moolenaarendfunc 237b3307b5eSBram Moolenaar 238b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 239b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 240b3307b5eSBram Moolenaar if s:vertical 241b3307b5eSBram Moolenaar vertical new 242b3307b5eSBram Moolenaar else 243b3307b5eSBram Moolenaar new 244b3307b5eSBram Moolenaar endif 245b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 246b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 247b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 248b3307b5eSBram Moolenaar set buftype=prompt 249b3307b5eSBram Moolenaar file gdb 250b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 251b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 252b3307b5eSBram Moolenaar 253b3307b5eSBram Moolenaar if s:vertical 254b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 255b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 256b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 257b3307b5eSBram Moolenaar endif 258b3307b5eSBram Moolenaar 259b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 260b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 261b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 262b3307b5eSBram Moolenaar 263b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 264b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 265b3307b5eSBram Moolenaar 266b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 267b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 268b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 269b3307b5eSBram Moolenaar \ }) 270b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 271b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 272b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 273b3307b5eSBram Moolenaar return 274b3307b5eSBram Moolenaar endif 2754551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 2764551c0a9SBram Moolenaar set modified 277b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 278b3307b5eSBram Moolenaar 279b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 280b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 281b3307b5eSBram Moolenaar " target is running. 282b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 283b3307b5eSBram Moolenaar " Older gdb uses a different command. 284b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 285b3307b5eSBram Moolenaar 286b3307b5eSBram Moolenaar let s:ptybuf = 0 287b3307b5eSBram Moolenaar if has('win32') 288b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 289b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 290b3307b5eSBram Moolenaar elseif has('terminal') 291b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 292b3307b5eSBram Moolenaar " gdb window. 293b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 294b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 295b3307b5eSBram Moolenaar \ }) 296b3307b5eSBram Moolenaar if s:ptybuf == 0 297b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 298b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 299b3307b5eSBram Moolenaar return 300b3307b5eSBram Moolenaar endif 301b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 302b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 303b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 304b3307b5eSBram Moolenaar 305b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 306b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 307b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 308b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 309b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 310b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 311b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 312b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 313b3307b5eSBram Moolenaar else 314b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 315b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 316b3307b5eSBram Moolenaar endif 317b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 318b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 319b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 320b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 321b3307b5eSBram Moolenaar 322b3307b5eSBram Moolenaar " Set arguments to be run 323b3307b5eSBram Moolenaar if len(proc_args) 324b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 325b3307b5eSBram Moolenaar endif 326b3307b5eSBram Moolenaar 327b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 328b3307b5eSBram Moolenaar startinsert 329b3307b5eSBram Moolenaarendfunc 330b3307b5eSBram Moolenaar 331b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 33238baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 33338baa3e6SBram Moolenaar " There can be only one. 33438baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 33538baa3e6SBram Moolenaar 33645d5f26dSBram Moolenaar " Install debugger commands in the text window. 337b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 338e09ba7baSBram Moolenaar call s:InstallCommands() 33945d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 340e09ba7baSBram Moolenaar 34151b0f370SBram Moolenaar " Enable showing a balloon with eval info 342246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 343246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 34451b0f370SBram Moolenaar if has("balloon_eval") 34551b0f370SBram Moolenaar set ballooneval 346246fe03dSBram Moolenaar endif 34751b0f370SBram Moolenaar if has("balloon_eval_term") 34851b0f370SBram Moolenaar set balloonevalterm 34951b0f370SBram Moolenaar endif 35051b0f370SBram Moolenaar endif 35151b0f370SBram Moolenaar 352de1a8314SBram Moolenaar " Contains breakpoints that have been placed, key is the number. 353e09ba7baSBram Moolenaar let s:breakpoints = {} 3541b9645deSBram Moolenaar 3551b9645deSBram Moolenaar augroup TermDebug 3561b9645deSBram Moolenaar au BufRead * call s:BufRead() 3571b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 358f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 3591b9645deSBram Moolenaar augroup END 36032c67ba7SBram Moolenaar 361b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 362b3307b5eSBram Moolenaar " window. 36332c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 36432c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 36532c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 36632c67ba7SBram Moolenaar endif 367c572da5fSBram Moolenaarendfunc 368c572da5fSBram Moolenaar 369b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 370b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 371b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 372b3307b5eSBram Moolenaar if s:way == 'prompt' 373b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 374b3307b5eSBram Moolenaar else 375b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 376b3307b5eSBram Moolenaar endif 377b3307b5eSBram Moolenaarendfunc 378b3307b5eSBram Moolenaar 379b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 380b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 381b3307b5eSBram Moolenaar if s:way == 'prompt' 382b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 383b3307b5eSBram Moolenaar else 384b3307b5eSBram Moolenaar let do_continue = 0 385b3307b5eSBram Moolenaar if !s:stopped 386b3307b5eSBram Moolenaar let do_continue = 1 387b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 388b3307b5eSBram Moolenaar sleep 10m 389b3307b5eSBram Moolenaar endif 390b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 391b3307b5eSBram Moolenaar if do_continue 392b3307b5eSBram Moolenaar Continue 393b3307b5eSBram Moolenaar endif 394b3307b5eSBram Moolenaar endif 395b3307b5eSBram Moolenaarendfunc 396b3307b5eSBram Moolenaar 397b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 398b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 399b3307b5eSBram Moolenaar call s:SendCommand(a:text) 400b3307b5eSBram Moolenaarendfunc 401b3307b5eSBram Moolenaar 4024551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4034551c0a9SBram Moolenaar" breakpoint. 404b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4052ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4062ed890f1SBram Moolenaar if has('win32') 4072ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4082ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4094551c0a9SBram Moolenaar if s:pid == 0 4104551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4114551c0a9SBram Moolenaar else 4124551c0a9SBram Moolenaar call debugbreak(s:pid) 4134551c0a9SBram Moolenaar endif 4142ed890f1SBram Moolenaar else 4152ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4162ed890f1SBram Moolenaar endif 417b3307b5eSBram Moolenaarendfunc 418b3307b5eSBram Moolenaar 419b3307b5eSBram Moolenaar" Function called when gdb outputs text. 420b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 421b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 422b3307b5eSBram Moolenaar 423b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 424b3307b5eSBram Moolenaar " Drop status and echo'd commands. 425a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 426b3307b5eSBram Moolenaar return 427b3307b5eSBram Moolenaar endif 428b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 429b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 430b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 431b3307b5eSBram Moolenaar " Silently drop evaluation errors. 432b3307b5eSBram Moolenaar unlet s:evalexpr 433b3307b5eSBram Moolenaar return 434b3307b5eSBram Moolenaar endif 435b3307b5eSBram Moolenaar elseif a:text[0] == '~' 436b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 437b3307b5eSBram Moolenaar else 438b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 439b3307b5eSBram Moolenaar return 440b3307b5eSBram Moolenaar endif 441b3307b5eSBram Moolenaar 442b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 443b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 444b3307b5eSBram Moolenaar 445b3307b5eSBram Moolenaar " Add the output above the current prompt. 446b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4474551c0a9SBram Moolenaar set modified 448b3307b5eSBram Moolenaar 449b3307b5eSBram Moolenaar call win_gotoid(curwinid) 450b3307b5eSBram Moolenaarendfunc 451b3307b5eSBram Moolenaar 452b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 453b3307b5eSBram Moolenaar" to the next ", unescaping characters. 454b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 455b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 456a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 457b3307b5eSBram Moolenaar return 458b3307b5eSBram Moolenaar endif 459b3307b5eSBram Moolenaar let result = '' 460b3307b5eSBram Moolenaar let i = 1 461b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 462b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 463b3307b5eSBram Moolenaar let i += 1 464b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 465b3307b5eSBram Moolenaar " drop \n 466b3307b5eSBram Moolenaar let i += 1 467b3307b5eSBram Moolenaar continue 468b3307b5eSBram Moolenaar endif 469b3307b5eSBram Moolenaar endif 470b3307b5eSBram Moolenaar let result .= a:quotedText[i] 471b3307b5eSBram Moolenaar let i += 1 472b3307b5eSBram Moolenaar endwhile 473b3307b5eSBram Moolenaar return result 474b3307b5eSBram Moolenaarendfunc 475b3307b5eSBram Moolenaar 476a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 477a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 478a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 479a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 480a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 481a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 482a15b0a93SBram Moolenaar endif 483a15b0a93SBram Moolenaar return name 484a15b0a93SBram Moolenaarendfunc 485a15b0a93SBram Moolenaar 486b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 487fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 488b3623a38SBram Moolenaar unlet s:gdbwin 489e09ba7baSBram Moolenaar 490b3307b5eSBram Moolenaar call s:EndDebugCommon() 491b3307b5eSBram Moolenaarendfunc 492b3307b5eSBram Moolenaar 493b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 494e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 495e09ba7baSBram Moolenaar 496b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 497b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 498b3307b5eSBram Moolenaar endif 499b3307b5eSBram Moolenaar 500b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 501e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 502e09ba7baSBram Moolenaar call s:DeleteCommands() 503e09ba7baSBram Moolenaar 504e09ba7baSBram Moolenaar call win_gotoid(curwinid) 505b3307b5eSBram Moolenaar 50638baa3e6SBram Moolenaar if s:save_columns > 0 50738baa3e6SBram Moolenaar let &columns = s:save_columns 50838baa3e6SBram Moolenaar endif 5091b9645deSBram Moolenaar 510246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 511246fe03dSBram Moolenaar set balloonexpr= 51251b0f370SBram Moolenaar if has("balloon_eval") 51351b0f370SBram Moolenaar set noballooneval 514246fe03dSBram Moolenaar endif 51551b0f370SBram Moolenaar if has("balloon_eval_term") 51651b0f370SBram Moolenaar set noballoonevalterm 51751b0f370SBram Moolenaar endif 51851b0f370SBram Moolenaar endif 51951b0f370SBram Moolenaar 5201b9645deSBram Moolenaar au! TermDebug 521fe386641SBram Moolenaarendfunc 522fe386641SBram Moolenaar 523b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 524b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 525b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5264551c0a9SBram Moolenaar set nomodified 527b3307b5eSBram Moolenaar close 528b3307b5eSBram Moolenaar if curwinid != s:gdbwin 529b3307b5eSBram Moolenaar call win_gotoid(curwinid) 530b3307b5eSBram Moolenaar endif 531b3307b5eSBram Moolenaar 532b3307b5eSBram Moolenaar call s:EndDebugCommon() 533b3307b5eSBram Moolenaar unlet s:gdbwin 534b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 535b3307b5eSBram Moolenaarendfunc 536b3307b5eSBram Moolenaar 537fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 538fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 539fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 540fe386641SBram Moolenaar 541fe386641SBram Moolenaar for msg in msgs 542fe386641SBram Moolenaar " remove prefixed NL 543fe386641SBram Moolenaar if msg[0] == "\n" 544fe386641SBram Moolenaar let msg = msg[1:] 545fe386641SBram Moolenaar endif 546fe386641SBram Moolenaar if msg != '' 5471b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 548e09ba7baSBram Moolenaar call s:HandleCursor(msg) 54945d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 550e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 551e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 552e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 5534551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 5544551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 55545d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 55645d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 55745d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 55845d5f26dSBram Moolenaar call s:HandleError(msg) 559e09ba7baSBram Moolenaar endif 560e09ba7baSBram Moolenaar endif 561e09ba7baSBram Moolenaar endfor 562e09ba7baSBram Moolenaarendfunc 563e09ba7baSBram Moolenaar 564e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 565e09ba7baSBram Moolenaarfunc s:InstallCommands() 566e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 56771137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 568e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 56945d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 570e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 57160e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 57260e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 57360e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 574b3307b5eSBram Moolenaar 575b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 576b3307b5eSBram Moolenaar if s:way == 'prompt' 577b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 578b3307b5eSBram Moolenaar else 579b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 580b3307b5eSBram Moolenaar endif 581b3307b5eSBram Moolenaar 58245d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 58345d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 58445d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 585b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 58671137fedSBram Moolenaar command Winbar call s:InstallWinbar() 58745d5f26dSBram Moolenaar 58845d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 58945d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 5901b9645deSBram Moolenaar 591f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 59271137fedSBram Moolenaar call s:InstallWinbar() 59371137fedSBram Moolenaar 59471137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 59571137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 59671137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 59771137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 59871137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 59971137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 60071137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 60171137fedSBram Moolenaar endif 60271137fedSBram Moolenaar endif 60371137fedSBram Moolenaarendfunc 60471137fedSBram Moolenaar 60571137fedSBram Moolenaarlet s:winbar_winids = [] 60671137fedSBram Moolenaar 60771137fedSBram Moolenaar" Install the window toolbar in the current window. 60871137fedSBram Moolenaarfunc s:InstallWinbar() 609c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 61024a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 61124a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 61224a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 61324a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 61460e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 61524a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 61671137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 617c4b533e1SBram Moolenaar endif 618e09ba7baSBram Moolenaarendfunc 619e09ba7baSBram Moolenaar 620e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 621e09ba7baSBram Moolenaarfunc s:DeleteCommands() 622e09ba7baSBram Moolenaar delcommand Break 62371137fedSBram Moolenaar delcommand Clear 624e09ba7baSBram Moolenaar delcommand Step 62545d5f26dSBram Moolenaar delcommand Over 626e09ba7baSBram Moolenaar delcommand Finish 62760e73f2aSBram Moolenaar delcommand Run 62860e73f2aSBram Moolenaar delcommand Arguments 62960e73f2aSBram Moolenaar delcommand Stop 630e09ba7baSBram Moolenaar delcommand Continue 63145d5f26dSBram Moolenaar delcommand Evaluate 63245d5f26dSBram Moolenaar delcommand Gdb 63345d5f26dSBram Moolenaar delcommand Program 634b3623a38SBram Moolenaar delcommand Source 63571137fedSBram Moolenaar delcommand Winbar 63645d5f26dSBram Moolenaar 63745d5f26dSBram Moolenaar nunmap K 6381b9645deSBram Moolenaar 6391b9645deSBram Moolenaar if has('menu') 64071137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 64171137fedSBram Moolenaar let curwinid = win_getid(winnr()) 64271137fedSBram Moolenaar for winid in s:winbar_winids 64371137fedSBram Moolenaar if win_gotoid(winid) 6441b9645deSBram Moolenaar aunmenu WinBar.Step 6451b9645deSBram Moolenaar aunmenu WinBar.Next 6461b9645deSBram Moolenaar aunmenu WinBar.Finish 6471b9645deSBram Moolenaar aunmenu WinBar.Cont 64860e73f2aSBram Moolenaar aunmenu WinBar.Stop 6491b9645deSBram Moolenaar aunmenu WinBar.Eval 6501b9645deSBram Moolenaar endif 65171137fedSBram Moolenaar endfor 65271137fedSBram Moolenaar call win_gotoid(curwinid) 65371137fedSBram Moolenaar let s:winbar_winids = [] 65471137fedSBram Moolenaar 65571137fedSBram Moolenaar if exists('s:saved_mousemodel') 65671137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 65771137fedSBram Moolenaar unlet s:saved_mousemodel 65871137fedSBram Moolenaar aunmenu PopUp.-SEP3- 65971137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 66071137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 66171137fedSBram Moolenaar aunmenu PopUp.Evaluate 66271137fedSBram Moolenaar endif 66371137fedSBram Moolenaar endif 6641b9645deSBram Moolenaar 66545d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 66645d5f26dSBram Moolenaar for key in keys(s:breakpoints) 66745d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 66845d5f26dSBram Moolenaar endfor 66945d5f26dSBram Moolenaar unlet s:breakpoints 670a15b0a93SBram Moolenaar 671a15b0a93SBram Moolenaar sign undefine debugPC 672a15b0a93SBram Moolenaar for val in s:BreakpointSigns 673a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 674a15b0a93SBram Moolenaar endfor 6754551c0a9SBram Moolenaar let s:BreakpointSigns = [] 676e09ba7baSBram Moolenaarendfunc 677e09ba7baSBram Moolenaar 678e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 679e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 68060e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 68160e73f2aSBram Moolenaar " Interrupt to make it work. 68260e73f2aSBram Moolenaar let do_continue = 0 68360e73f2aSBram Moolenaar if !s:stopped 68460e73f2aSBram Moolenaar let do_continue = 1 685b3307b5eSBram Moolenaar if s:way == 'prompt' 6864551c0a9SBram Moolenaar call s:PromptInterrupt() 687b3307b5eSBram Moolenaar else 68860e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 689b3307b5eSBram Moolenaar endif 69060e73f2aSBram Moolenaar sleep 10m 69160e73f2aSBram Moolenaar endif 692a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 693a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 694a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 69560e73f2aSBram Moolenaar if do_continue 69660e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 69760e73f2aSBram Moolenaar endif 698e09ba7baSBram Moolenaarendfunc 699e09ba7baSBram Moolenaar 70071137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 70171137fedSBram Moolenaarfunc s:ClearBreakpoint() 702e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 703e09ba7baSBram Moolenaar let lnum = line('.') 704e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 705e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 706b3307b5eSBram Moolenaar call s:SendCommand('-break-delete ' . key) 707e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 708e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 709e09ba7baSBram Moolenaar unlet s:breakpoints[key] 710e09ba7baSBram Moolenaar break 711e09ba7baSBram Moolenaar endif 712e09ba7baSBram Moolenaar endfor 713e09ba7baSBram Moolenaarendfunc 714e09ba7baSBram Moolenaar 71560e73f2aSBram Moolenaarfunc s:Run(args) 71660e73f2aSBram Moolenaar if a:args != '' 71760e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 71860e73f2aSBram Moolenaar endif 71960e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 72060e73f2aSBram Moolenaarendfunc 72160e73f2aSBram Moolenaar 72251b0f370SBram Moolenaarfunc s:SendEval(expr) 72351b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 72451b0f370SBram Moolenaar let s:evalexpr = a:expr 72551b0f370SBram Moolenaarendfunc 72651b0f370SBram Moolenaar 72745d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 72845d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 72945d5f26dSBram Moolenaar if a:arg != '' 73045d5f26dSBram Moolenaar let expr = a:arg 73145d5f26dSBram Moolenaar elseif a:range == 2 73245d5f26dSBram Moolenaar let pos = getcurpos() 73345d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 73445d5f26dSBram Moolenaar let regt = getregtype('v') 73545d5f26dSBram Moolenaar normal! gv"vy 73645d5f26dSBram Moolenaar let expr = @v 73745d5f26dSBram Moolenaar call setpos('.', pos) 73845d5f26dSBram Moolenaar call setreg('v', reg, regt) 73945d5f26dSBram Moolenaar else 74045d5f26dSBram Moolenaar let expr = expand('<cexpr>') 74145d5f26dSBram Moolenaar endif 74222f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 74351b0f370SBram Moolenaar call s:SendEval(expr) 74445d5f26dSBram Moolenaarendfunc 74545d5f26dSBram Moolenaar 74622f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 74751b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 74851b0f370SBram Moolenaar 74945d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 75045d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 7511b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 7521b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 75351b0f370SBram Moolenaar if s:evalFromBalloonExpr 75451b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 75551b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 75651b0f370SBram Moolenaar else 75751b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 75851b0f370SBram Moolenaar endif 75951b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 76051b0f370SBram Moolenaar else 7611b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 76251b0f370SBram Moolenaar endif 7631b9645deSBram Moolenaar 7647f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 7651b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 76622f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 76751b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 76851b0f370SBram Moolenaar else 76951b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 7701b9645deSBram Moolenaar endif 77145d5f26dSBram Moolenaarendfunc 77245d5f26dSBram Moolenaar 77351b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 77451b0f370SBram Moolenaar" if there is any. 77551b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 776b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 777b3307b5eSBram Moolenaar return 778b3307b5eSBram Moolenaar endif 779b3307b5eSBram Moolenaar if !s:stopped 780b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 781b3307b5eSBram Moolenaar " mouse triggers a balloon. 78251b0f370SBram Moolenaar return 78351b0f370SBram Moolenaar endif 78451b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 78551b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 78622f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 78722f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 78851b0f370SBram Moolenaar return '' 78951b0f370SBram Moolenaarendfunc 79051b0f370SBram Moolenaar 79145d5f26dSBram Moolenaar" Handle an error. 79245d5f26dSBram Moolenaarfunc s:HandleError(msg) 79322f1d0e3SBram Moolenaar if s:ignoreEvalError 79451b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 79522f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 79622f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 79751b0f370SBram Moolenaar return 79851b0f370SBram Moolenaar endif 79945d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 80045d5f26dSBram Moolenaarendfunc 80145d5f26dSBram Moolenaar 802b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 803b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 804c4b533e1SBram Moolenaar new 805b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 806c4b533e1SBram Moolenaar call s:InstallWinbar() 807c4b533e1SBram Moolenaar endif 808c4b533e1SBram Moolenaarendfunc 809c4b533e1SBram Moolenaar 810e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 811e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 812e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 813fe386641SBram Moolenaar let wid = win_getid(winnr()) 814fe386641SBram Moolenaar 81560e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8164551c0a9SBram Moolenaar call ch_log('program stopped') 81760e73f2aSBram Moolenaar let s:stopped = 1 81860e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8194551c0a9SBram Moolenaar call ch_log('program running') 82060e73f2aSBram Moolenaar let s:stopped = 0 82160e73f2aSBram Moolenaar endif 82260e73f2aSBram Moolenaar 823a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 824a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 825a15b0a93SBram Moolenaar else 826a15b0a93SBram Moolenaar let fname = '' 827a15b0a93SBram Moolenaar endif 8281b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 829e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 830fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8314551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 8321b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 833fe386641SBram Moolenaar if &modified 834fe386641SBram Moolenaar " TODO: find existing window 835fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 836b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 837c4b533e1SBram Moolenaar call s:InstallWinbar() 838fe386641SBram Moolenaar else 839fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 840fe386641SBram Moolenaar endif 841fe386641SBram Moolenaar endif 842fe386641SBram Moolenaar exe lnum 84301164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 8441b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 845fe386641SBram Moolenaar setlocal signcolumn=yes 846fe386641SBram Moolenaar endif 8474551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 848fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 849fe386641SBram Moolenaar endif 850fe386641SBram Moolenaar 851fe386641SBram Moolenaar call win_gotoid(wid) 852e09ba7baSBram Moolenaarendfunc 853e09ba7baSBram Moolenaar 854de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 855a15b0a93SBram Moolenaar 856a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr) 857de1a8314SBram Moolenaar if index(s:BreakpointSigns, a:nr) == -1 858de1a8314SBram Moolenaar call add(s:BreakpointSigns, a:nr) 859de1a8314SBram Moolenaar exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint" 860de1a8314SBram Moolenaar endif 861de1a8314SBram Moolenaarendfunc 862de1a8314SBram Moolenaar 863e09ba7baSBram Moolenaar" Handle setting a breakpoint 864e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 865e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 866*6dccc962SBram Moolenaar if a:msg !~ 'fullname=' 867*6dccc962SBram Moolenaar " a watch does not have a file name 868*6dccc962SBram Moolenaar return 869*6dccc962SBram Moolenaar endif 870*6dccc962SBram Moolenaar 871e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 872e09ba7baSBram Moolenaar if nr == 0 873e09ba7baSBram Moolenaar return 874fe386641SBram Moolenaar endif 875de1a8314SBram Moolenaar call s:CreateBreakpoint(nr) 876e09ba7baSBram Moolenaar 877e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 878e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 879e09ba7baSBram Moolenaar else 880e09ba7baSBram Moolenaar let entry = {} 881e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 882fe386641SBram Moolenaar endif 883e09ba7baSBram Moolenaar 884a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 885e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 886e09ba7baSBram Moolenaar let entry['fname'] = fname 887e09ba7baSBram Moolenaar let entry['lnum'] = lnum 8881b9645deSBram Moolenaar 8891b9645deSBram Moolenaar if bufloaded(fname) 8901b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 8911b9645deSBram Moolenaar endif 8921b9645deSBram Moolenaarendfunc 8931b9645deSBram Moolenaar 8941b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 895de1a8314SBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname'] 8961b9645deSBram Moolenaar let a:entry['placed'] = 1 897e09ba7baSBram Moolenaarendfunc 898e09ba7baSBram Moolenaar 899e09ba7baSBram Moolenaar" Handle deleting a breakpoint 900e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 901e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 902e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 903e09ba7baSBram Moolenaar if nr == 0 904e09ba7baSBram Moolenaar return 905e09ba7baSBram Moolenaar endif 9061b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 9071b9645deSBram Moolenaar let entry = s:breakpoints[nr] 9081b9645deSBram Moolenaar if has_key(entry, 'placed') 909e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 9101b9645deSBram Moolenaar unlet entry['placed'] 9111b9645deSBram Moolenaar endif 912e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 9131b9645deSBram Moolenaar endif 914c572da5fSBram Moolenaarendfunc 9151b9645deSBram Moolenaar 9164551c0a9SBram Moolenaar" Handle the debugged program starting to run. 9174551c0a9SBram Moolenaar" Will store the process ID in s:pid 9184551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 9194551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 9204551c0a9SBram Moolenaar if nr == 0 9214551c0a9SBram Moolenaar return 9224551c0a9SBram Moolenaar endif 9234551c0a9SBram Moolenaar let s:pid = nr 9244551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 9254551c0a9SBram Moolenaarendfunc 9264551c0a9SBram Moolenaar 9271b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 9281b9645deSBram Moolenaarfunc s:BufRead() 9291b9645deSBram Moolenaar let fname = expand('<afile>:p') 9301b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9311b9645deSBram Moolenaar if entry['fname'] == fname 9321b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 9331b9645deSBram Moolenaar endif 9341b9645deSBram Moolenaar endfor 9351b9645deSBram Moolenaarendfunc 9361b9645deSBram Moolenaar 9371b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 9381b9645deSBram Moolenaarfunc s:BufUnloaded() 9391b9645deSBram Moolenaar let fname = expand('<afile>:p') 9401b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9411b9645deSBram Moolenaar if entry['fname'] == fname 9421b9645deSBram Moolenaar let entry['placed'] = 0 9431b9645deSBram Moolenaar endif 9441b9645deSBram Moolenaar endfor 9451b9645deSBram Moolenaarendfunc 946