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 101*4551c0a9SBram 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 275*4551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 276*4551c0a9SBram 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 402*4551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 403*4551c0a9SBram Moolenaar" breakpoint. 404b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 405*4551c0a9SBram Moolenaar if s:pid == 0 406*4551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 407*4551c0a9SBram Moolenaar else 408b3307b5eSBram Moolenaar call ch_log('Interrupting gdb') 409*4551c0a9SBram Moolenaar " Using job_stop(s:gdbjob, 'int') does not work. 410*4551c0a9SBram Moolenaar call debugbreak(s:pid) 411*4551c0a9SBram Moolenaar endif 412b3307b5eSBram Moolenaarendfunc 413b3307b5eSBram Moolenaar 414b3307b5eSBram Moolenaar" Function called when gdb outputs text. 415b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 416b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 417b3307b5eSBram Moolenaar 418b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 419b3307b5eSBram Moolenaar " Drop status and echo'd commands. 420a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 421b3307b5eSBram Moolenaar return 422b3307b5eSBram Moolenaar endif 423b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 424b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 425b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 426b3307b5eSBram Moolenaar " Silently drop evaluation errors. 427b3307b5eSBram Moolenaar unlet s:evalexpr 428b3307b5eSBram Moolenaar return 429b3307b5eSBram Moolenaar endif 430b3307b5eSBram Moolenaar elseif a:text[0] == '~' 431b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 432b3307b5eSBram Moolenaar else 433b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 434b3307b5eSBram Moolenaar return 435b3307b5eSBram Moolenaar endif 436b3307b5eSBram Moolenaar 437b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 438b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 439b3307b5eSBram Moolenaar 440b3307b5eSBram Moolenaar " Add the output above the current prompt. 441b3307b5eSBram Moolenaar call append(line('$') - 1, text) 442*4551c0a9SBram Moolenaar set modified 443b3307b5eSBram Moolenaar 444b3307b5eSBram Moolenaar call win_gotoid(curwinid) 445b3307b5eSBram Moolenaarendfunc 446b3307b5eSBram Moolenaar 447b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 448b3307b5eSBram Moolenaar" to the next ", unescaping characters. 449b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 450b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 451a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 452b3307b5eSBram Moolenaar return 453b3307b5eSBram Moolenaar endif 454b3307b5eSBram Moolenaar let result = '' 455b3307b5eSBram Moolenaar let i = 1 456b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 457b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 458b3307b5eSBram Moolenaar let i += 1 459b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 460b3307b5eSBram Moolenaar " drop \n 461b3307b5eSBram Moolenaar let i += 1 462b3307b5eSBram Moolenaar continue 463b3307b5eSBram Moolenaar endif 464b3307b5eSBram Moolenaar endif 465b3307b5eSBram Moolenaar let result .= a:quotedText[i] 466b3307b5eSBram Moolenaar let i += 1 467b3307b5eSBram Moolenaar endwhile 468b3307b5eSBram Moolenaar return result 469b3307b5eSBram Moolenaarendfunc 470b3307b5eSBram Moolenaar 471a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 472a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 473a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 474a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 475a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 476a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 477a15b0a93SBram Moolenaar endif 478a15b0a93SBram Moolenaar return name 479a15b0a93SBram Moolenaarendfunc 480a15b0a93SBram Moolenaar 481b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 482fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 483b3623a38SBram Moolenaar unlet s:gdbwin 484e09ba7baSBram Moolenaar 485b3307b5eSBram Moolenaar call s:EndDebugCommon() 486b3307b5eSBram Moolenaarendfunc 487b3307b5eSBram Moolenaar 488b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 489e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 490e09ba7baSBram Moolenaar 491b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 492b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 493b3307b5eSBram Moolenaar endif 494b3307b5eSBram Moolenaar 495b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 496e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 497e09ba7baSBram Moolenaar call s:DeleteCommands() 498e09ba7baSBram Moolenaar 499e09ba7baSBram Moolenaar call win_gotoid(curwinid) 500b3307b5eSBram Moolenaar 50138baa3e6SBram Moolenaar if s:save_columns > 0 50238baa3e6SBram Moolenaar let &columns = s:save_columns 50338baa3e6SBram Moolenaar endif 5041b9645deSBram Moolenaar 505246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 506246fe03dSBram Moolenaar set balloonexpr= 50751b0f370SBram Moolenaar if has("balloon_eval") 50851b0f370SBram Moolenaar set noballooneval 509246fe03dSBram Moolenaar endif 51051b0f370SBram Moolenaar if has("balloon_eval_term") 51151b0f370SBram Moolenaar set noballoonevalterm 51251b0f370SBram Moolenaar endif 51351b0f370SBram Moolenaar endif 51451b0f370SBram Moolenaar 5151b9645deSBram Moolenaar au! TermDebug 516fe386641SBram Moolenaarendfunc 517fe386641SBram Moolenaar 518b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 519b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 520b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 521*4551c0a9SBram Moolenaar set nomodified 522b3307b5eSBram Moolenaar close 523b3307b5eSBram Moolenaar if curwinid != s:gdbwin 524b3307b5eSBram Moolenaar call win_gotoid(curwinid) 525b3307b5eSBram Moolenaar endif 526b3307b5eSBram Moolenaar 527b3307b5eSBram Moolenaar call s:EndDebugCommon() 528b3307b5eSBram Moolenaar unlet s:gdbwin 529b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 530b3307b5eSBram Moolenaarendfunc 531b3307b5eSBram Moolenaar 532fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 533fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 534fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 535fe386641SBram Moolenaar 536fe386641SBram Moolenaar for msg in msgs 537fe386641SBram Moolenaar " remove prefixed NL 538fe386641SBram Moolenaar if msg[0] == "\n" 539fe386641SBram Moolenaar let msg = msg[1:] 540fe386641SBram Moolenaar endif 541fe386641SBram Moolenaar if msg != '' 5421b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 543e09ba7baSBram Moolenaar call s:HandleCursor(msg) 54445d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 545e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 546e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 547e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 548*4551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 549*4551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 55045d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 55145d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 55245d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 55345d5f26dSBram Moolenaar call s:HandleError(msg) 554e09ba7baSBram Moolenaar endif 555e09ba7baSBram Moolenaar endif 556e09ba7baSBram Moolenaar endfor 557e09ba7baSBram Moolenaarendfunc 558e09ba7baSBram Moolenaar 559e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 560e09ba7baSBram Moolenaarfunc s:InstallCommands() 561e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 56271137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 563e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 56445d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 565e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 56660e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 56760e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 56860e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 569b3307b5eSBram Moolenaar 570b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 571b3307b5eSBram Moolenaar if s:way == 'prompt' 572b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 573b3307b5eSBram Moolenaar else 574b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 575b3307b5eSBram Moolenaar endif 576b3307b5eSBram Moolenaar 57745d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 57845d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 57945d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 580b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 58171137fedSBram Moolenaar command Winbar call s:InstallWinbar() 58245d5f26dSBram Moolenaar 58345d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 58445d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 5851b9645deSBram Moolenaar 586f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 58771137fedSBram Moolenaar call s:InstallWinbar() 58871137fedSBram Moolenaar 58971137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 59071137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 59171137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 59271137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 59371137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 59471137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 59571137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 59671137fedSBram Moolenaar endif 59771137fedSBram Moolenaar endif 59871137fedSBram Moolenaarendfunc 59971137fedSBram Moolenaar 60071137fedSBram Moolenaarlet s:winbar_winids = [] 60171137fedSBram Moolenaar 60271137fedSBram Moolenaar" Install the window toolbar in the current window. 60371137fedSBram Moolenaarfunc s:InstallWinbar() 604c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 60524a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 60624a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 60724a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 60824a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 60960e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 61024a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 61171137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 612c4b533e1SBram Moolenaar endif 613e09ba7baSBram Moolenaarendfunc 614e09ba7baSBram Moolenaar 615e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 616e09ba7baSBram Moolenaarfunc s:DeleteCommands() 617e09ba7baSBram Moolenaar delcommand Break 61871137fedSBram Moolenaar delcommand Clear 619e09ba7baSBram Moolenaar delcommand Step 62045d5f26dSBram Moolenaar delcommand Over 621e09ba7baSBram Moolenaar delcommand Finish 62260e73f2aSBram Moolenaar delcommand Run 62360e73f2aSBram Moolenaar delcommand Arguments 62460e73f2aSBram Moolenaar delcommand Stop 625e09ba7baSBram Moolenaar delcommand Continue 62645d5f26dSBram Moolenaar delcommand Evaluate 62745d5f26dSBram Moolenaar delcommand Gdb 62845d5f26dSBram Moolenaar delcommand Program 629b3623a38SBram Moolenaar delcommand Source 63071137fedSBram Moolenaar delcommand Winbar 63145d5f26dSBram Moolenaar 63245d5f26dSBram Moolenaar nunmap K 6331b9645deSBram Moolenaar 6341b9645deSBram Moolenaar if has('menu') 63571137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 63671137fedSBram Moolenaar let curwinid = win_getid(winnr()) 63771137fedSBram Moolenaar for winid in s:winbar_winids 63871137fedSBram Moolenaar if win_gotoid(winid) 6391b9645deSBram Moolenaar aunmenu WinBar.Step 6401b9645deSBram Moolenaar aunmenu WinBar.Next 6411b9645deSBram Moolenaar aunmenu WinBar.Finish 6421b9645deSBram Moolenaar aunmenu WinBar.Cont 64360e73f2aSBram Moolenaar aunmenu WinBar.Stop 6441b9645deSBram Moolenaar aunmenu WinBar.Eval 6451b9645deSBram Moolenaar endif 64671137fedSBram Moolenaar endfor 64771137fedSBram Moolenaar call win_gotoid(curwinid) 64871137fedSBram Moolenaar let s:winbar_winids = [] 64971137fedSBram Moolenaar 65071137fedSBram Moolenaar if exists('s:saved_mousemodel') 65171137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 65271137fedSBram Moolenaar unlet s:saved_mousemodel 65371137fedSBram Moolenaar aunmenu PopUp.-SEP3- 65471137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 65571137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 65671137fedSBram Moolenaar aunmenu PopUp.Evaluate 65771137fedSBram Moolenaar endif 65871137fedSBram Moolenaar endif 6591b9645deSBram Moolenaar 66045d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 66145d5f26dSBram Moolenaar for key in keys(s:breakpoints) 66245d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 66345d5f26dSBram Moolenaar endfor 66445d5f26dSBram Moolenaar unlet s:breakpoints 665a15b0a93SBram Moolenaar 666a15b0a93SBram Moolenaar sign undefine debugPC 667a15b0a93SBram Moolenaar for val in s:BreakpointSigns 668a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 669a15b0a93SBram Moolenaar endfor 670*4551c0a9SBram Moolenaar let s:BreakpointSigns = [] 671e09ba7baSBram Moolenaarendfunc 672e09ba7baSBram Moolenaar 673e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 674e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 67560e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 67660e73f2aSBram Moolenaar " Interrupt to make it work. 67760e73f2aSBram Moolenaar let do_continue = 0 67860e73f2aSBram Moolenaar if !s:stopped 67960e73f2aSBram Moolenaar let do_continue = 1 680b3307b5eSBram Moolenaar if s:way == 'prompt' 681*4551c0a9SBram Moolenaar call s:PromptInterrupt() 682b3307b5eSBram Moolenaar else 68360e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 684b3307b5eSBram Moolenaar endif 68560e73f2aSBram Moolenaar sleep 10m 68660e73f2aSBram Moolenaar endif 687a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 688a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 689a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 69060e73f2aSBram Moolenaar if do_continue 69160e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 69260e73f2aSBram Moolenaar endif 693e09ba7baSBram Moolenaarendfunc 694e09ba7baSBram Moolenaar 69571137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 69671137fedSBram Moolenaarfunc s:ClearBreakpoint() 697e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 698e09ba7baSBram Moolenaar let lnum = line('.') 699e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 700e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 701b3307b5eSBram Moolenaar call s:SendCommand('-break-delete ' . key) 702e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 703e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 704e09ba7baSBram Moolenaar unlet s:breakpoints[key] 705e09ba7baSBram Moolenaar break 706e09ba7baSBram Moolenaar endif 707e09ba7baSBram Moolenaar endfor 708e09ba7baSBram Moolenaarendfunc 709e09ba7baSBram Moolenaar 71060e73f2aSBram Moolenaarfunc s:Run(args) 71160e73f2aSBram Moolenaar if a:args != '' 71260e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 71360e73f2aSBram Moolenaar endif 71460e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 71560e73f2aSBram Moolenaarendfunc 71660e73f2aSBram Moolenaar 71751b0f370SBram Moolenaarfunc s:SendEval(expr) 71851b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 71951b0f370SBram Moolenaar let s:evalexpr = a:expr 72051b0f370SBram Moolenaarendfunc 72151b0f370SBram Moolenaar 72245d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 72345d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 72445d5f26dSBram Moolenaar if a:arg != '' 72545d5f26dSBram Moolenaar let expr = a:arg 72645d5f26dSBram Moolenaar elseif a:range == 2 72745d5f26dSBram Moolenaar let pos = getcurpos() 72845d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 72945d5f26dSBram Moolenaar let regt = getregtype('v') 73045d5f26dSBram Moolenaar normal! gv"vy 73145d5f26dSBram Moolenaar let expr = @v 73245d5f26dSBram Moolenaar call setpos('.', pos) 73345d5f26dSBram Moolenaar call setreg('v', reg, regt) 73445d5f26dSBram Moolenaar else 73545d5f26dSBram Moolenaar let expr = expand('<cexpr>') 73645d5f26dSBram Moolenaar endif 73722f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 73851b0f370SBram Moolenaar call s:SendEval(expr) 73945d5f26dSBram Moolenaarendfunc 74045d5f26dSBram Moolenaar 74122f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 74251b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 74351b0f370SBram Moolenaar 74445d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 74545d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 7461b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 7471b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 74851b0f370SBram Moolenaar if s:evalFromBalloonExpr 74951b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 75051b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 75151b0f370SBram Moolenaar else 75251b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 75351b0f370SBram Moolenaar endif 75451b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 75551b0f370SBram Moolenaar else 7561b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 75751b0f370SBram Moolenaar endif 7581b9645deSBram Moolenaar 7597f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 7601b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 76122f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 76251b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 76351b0f370SBram Moolenaar else 76451b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 7651b9645deSBram Moolenaar endif 76645d5f26dSBram Moolenaarendfunc 76745d5f26dSBram Moolenaar 76851b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 76951b0f370SBram Moolenaar" if there is any. 77051b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 771b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 772b3307b5eSBram Moolenaar return 773b3307b5eSBram Moolenaar endif 774b3307b5eSBram Moolenaar if !s:stopped 775b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 776b3307b5eSBram Moolenaar " mouse triggers a balloon. 77751b0f370SBram Moolenaar return 77851b0f370SBram Moolenaar endif 77951b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 78051b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 78122f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 78222f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 78351b0f370SBram Moolenaar return '' 78451b0f370SBram Moolenaarendfunc 78551b0f370SBram Moolenaar 78645d5f26dSBram Moolenaar" Handle an error. 78745d5f26dSBram Moolenaarfunc s:HandleError(msg) 78822f1d0e3SBram Moolenaar if s:ignoreEvalError 78951b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 79022f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 79122f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 79251b0f370SBram Moolenaar return 79351b0f370SBram Moolenaar endif 79445d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 79545d5f26dSBram Moolenaarendfunc 79645d5f26dSBram Moolenaar 797b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 798b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 799c4b533e1SBram Moolenaar new 800b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 801c4b533e1SBram Moolenaar call s:InstallWinbar() 802c4b533e1SBram Moolenaar endif 803c4b533e1SBram Moolenaarendfunc 804c4b533e1SBram Moolenaar 805e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 806e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 807e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 808fe386641SBram Moolenaar let wid = win_getid(winnr()) 809fe386641SBram Moolenaar 81060e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 811*4551c0a9SBram Moolenaar call ch_log('program stopped') 81260e73f2aSBram Moolenaar let s:stopped = 1 81360e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 814*4551c0a9SBram Moolenaar call ch_log('program running') 81560e73f2aSBram Moolenaar let s:stopped = 0 81660e73f2aSBram Moolenaar endif 81760e73f2aSBram Moolenaar 818a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 819a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 820a15b0a93SBram Moolenaar else 821a15b0a93SBram Moolenaar let fname = '' 822a15b0a93SBram Moolenaar endif 8231b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 824e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 825fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 826*4551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 8271b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 828fe386641SBram Moolenaar if &modified 829fe386641SBram Moolenaar " TODO: find existing window 830fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 831b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 832c4b533e1SBram Moolenaar call s:InstallWinbar() 833fe386641SBram Moolenaar else 834fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 835fe386641SBram Moolenaar endif 836fe386641SBram Moolenaar endif 837fe386641SBram Moolenaar exe lnum 83801164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 8391b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 840fe386641SBram Moolenaar setlocal signcolumn=yes 841fe386641SBram Moolenaar endif 842*4551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 843fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 844fe386641SBram Moolenaar endif 845fe386641SBram Moolenaar 846fe386641SBram Moolenaar call win_gotoid(wid) 847e09ba7baSBram Moolenaarendfunc 848e09ba7baSBram Moolenaar 849de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 850a15b0a93SBram Moolenaar 851a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr) 852de1a8314SBram Moolenaar if index(s:BreakpointSigns, a:nr) == -1 853de1a8314SBram Moolenaar call add(s:BreakpointSigns, a:nr) 854de1a8314SBram Moolenaar exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint" 855de1a8314SBram Moolenaar endif 856de1a8314SBram Moolenaarendfunc 857de1a8314SBram Moolenaar 858e09ba7baSBram Moolenaar" Handle setting a breakpoint 859e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 860e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 861e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 862e09ba7baSBram Moolenaar if nr == 0 863e09ba7baSBram Moolenaar return 864fe386641SBram Moolenaar endif 865de1a8314SBram Moolenaar call s:CreateBreakpoint(nr) 866e09ba7baSBram Moolenaar 867e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 868e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 869e09ba7baSBram Moolenaar else 870e09ba7baSBram Moolenaar let entry = {} 871e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 872fe386641SBram Moolenaar endif 873e09ba7baSBram Moolenaar 874a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 875e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 876e09ba7baSBram Moolenaar let entry['fname'] = fname 877e09ba7baSBram Moolenaar let entry['lnum'] = lnum 8781b9645deSBram Moolenaar 8791b9645deSBram Moolenaar if bufloaded(fname) 8801b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 8811b9645deSBram Moolenaar endif 8821b9645deSBram Moolenaarendfunc 8831b9645deSBram Moolenaar 8841b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 885de1a8314SBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname'] 8861b9645deSBram Moolenaar let a:entry['placed'] = 1 887e09ba7baSBram Moolenaarendfunc 888e09ba7baSBram Moolenaar 889e09ba7baSBram Moolenaar" Handle deleting a breakpoint 890e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 891e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 892e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 893e09ba7baSBram Moolenaar if nr == 0 894e09ba7baSBram Moolenaar return 895e09ba7baSBram Moolenaar endif 8961b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 8971b9645deSBram Moolenaar let entry = s:breakpoints[nr] 8981b9645deSBram Moolenaar if has_key(entry, 'placed') 899e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 9001b9645deSBram Moolenaar unlet entry['placed'] 9011b9645deSBram Moolenaar endif 902e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 9031b9645deSBram Moolenaar endif 904c572da5fSBram Moolenaarendfunc 9051b9645deSBram Moolenaar 906*4551c0a9SBram Moolenaar" Handle the debugged program starting to run. 907*4551c0a9SBram Moolenaar" Will store the process ID in s:pid 908*4551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 909*4551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 910*4551c0a9SBram Moolenaar if nr == 0 911*4551c0a9SBram Moolenaar return 912*4551c0a9SBram Moolenaar endif 913*4551c0a9SBram Moolenaar let s:pid = nr 914*4551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 915*4551c0a9SBram Moolenaarendfunc 916*4551c0a9SBram Moolenaar 9171b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 9181b9645deSBram Moolenaarfunc s:BufRead() 9191b9645deSBram Moolenaar let fname = expand('<afile>:p') 9201b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9211b9645deSBram Moolenaar if entry['fname'] == fname 9221b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 9231b9645deSBram Moolenaar endif 9241b9645deSBram Moolenaar endfor 9251b9645deSBram Moolenaarendfunc 9261b9645deSBram Moolenaar 9271b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 9281b9645deSBram Moolenaarfunc s:BufUnloaded() 9291b9645deSBram Moolenaar let fname = expand('<afile>:p') 9301b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9311b9645deSBram Moolenaar if entry['fname'] == fname 9321b9645deSBram Moolenaar let entry['placed'] = 0 9331b9645deSBram Moolenaar endif 9341b9645deSBram Moolenaar endfor 9351b9645deSBram Moolenaarendfunc 936