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 59*ca4cc018SBram Moolenaarlet s:keepcpo = &cpo 60*ca4cc018SBram Moolenaarset cpo&vim 61*ca4cc018SBram Moolenaar 62fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 63fe386641SBram Moolenaar" To end type "quit" in the gdb window. 6432c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>) 6532c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) 66c572da5fSBram Moolenaar 67fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 68e09ba7baSBram Moolenaarif !exists('termdebugger') 69e09ba7baSBram Moolenaar let termdebugger = 'gdb' 70c572da5fSBram Moolenaarendif 71c572da5fSBram Moolenaar 72fe386641SBram Moolenaarlet s:pc_id = 12 73de1a8314SBram Moolenaarlet s:break_id = 13 " breakpoint number is added to this 7460e73f2aSBram Moolenaarlet s:stopped = 1 75e09ba7baSBram Moolenaar 76f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new) 77f07f9e73SBram Moolenaar let default = a:init ? 'default ' : '' 78f07f9e73SBram Moolenaar if a:new ==# 'light' && a:old !=# 'light' 79f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" 80f07f9e73SBram Moolenaar elseif a:new ==# 'dark' && a:old !=# 'dark' 81f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue" 82e09ba7baSBram Moolenaar endif 83f07f9e73SBram Moolenaarendfunc 84f07f9e73SBram Moolenaar 85f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background) 86e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 87fe386641SBram Moolenaar 8832c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 8932c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 9032c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 9132c67ba7SBram Moolenaarendfunc 9232c67ba7SBram Moolenaar 9332c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 9432c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 9532c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 9632c67ba7SBram Moolenaarendfunc 9732c67ba7SBram Moolenaar 9832c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 99b3623a38SBram Moolenaar if exists('s:gdbwin') 100b3623a38SBram Moolenaar echoerr 'Terminal debugger already running' 101b3623a38SBram Moolenaar return 102b3623a38SBram Moolenaar endif 103b3307b5eSBram Moolenaar let s:ptywin = 0 1044551c0a9SBram Moolenaar let s:pid = 0 105b3623a38SBram Moolenaar 106b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 107b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 108b3307b5eSBram Moolenaar 109b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 110fe386641SBram Moolenaar let s:startsigncolumn = &signcolumn 111fe386641SBram Moolenaar 11224a98a0eSBram Moolenaar let s:save_columns = 0 11324a98a0eSBram Moolenaar if exists('g:termdebug_wide') 11424a98a0eSBram Moolenaar if &columns < g:termdebug_wide 11538baa3e6SBram Moolenaar let s:save_columns = &columns 11638baa3e6SBram Moolenaar let &columns = g:termdebug_wide 11724a98a0eSBram Moolenaar endif 118b3307b5eSBram Moolenaar let s:vertical = 1 11938baa3e6SBram Moolenaar else 120b3307b5eSBram Moolenaar let s:vertical = 0 12138baa3e6SBram Moolenaar endif 12238baa3e6SBram Moolenaar 123b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 124b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 125b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 126b3307b5eSBram Moolenaar let s:way = 'terminal' 127b3307b5eSBram Moolenaar else 128b3307b5eSBram Moolenaar let s:way = 'prompt' 129b3307b5eSBram Moolenaar endif 130b3307b5eSBram Moolenaar 131b3307b5eSBram Moolenaar if s:way == 'prompt' 132b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 133b3307b5eSBram Moolenaar else 134b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 135b3307b5eSBram Moolenaar endif 136b3307b5eSBram Moolenaarendfunc 137b3307b5eSBram Moolenaar 138b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 139b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 140fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 141b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 142b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 143fe386641SBram Moolenaar \ }) 144fe386641SBram Moolenaar if s:ptybuf == 0 145fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 146fe386641SBram Moolenaar return 147fe386641SBram Moolenaar endif 148fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 14945d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 150b3307b5eSBram Moolenaar if s:vertical 15151b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 15251b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 15351b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 15451b0f370SBram Moolenaar endif 155fe386641SBram Moolenaar 156fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 157fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 158fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 159fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 160fe386641SBram Moolenaar \ 'hidden': 1, 161fe386641SBram Moolenaar \ }) 162fe386641SBram Moolenaar if s:commbuf == 0 163fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 164fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 165fe386641SBram Moolenaar return 166fe386641SBram Moolenaar endif 167fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 168c572da5fSBram Moolenaar 169c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 170c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 17132c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 17232c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 17332c67ba7SBram Moolenaar 17432c67ba7SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 175b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 17660e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 177b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndTermDebug'), 178fe386641SBram Moolenaar \ 'term_finish': 'close', 179c572da5fSBram Moolenaar \ }) 18060e73f2aSBram Moolenaar if s:gdbbuf == 0 181fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 182fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 183fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 184fe386641SBram Moolenaar return 185fe386641SBram Moolenaar endif 18645d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 187fe386641SBram Moolenaar 18832c67ba7SBram Moolenaar " Set arguments to be run 18932c67ba7SBram Moolenaar if len(proc_args) 19032c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 19132c67ba7SBram Moolenaar endif 19232c67ba7SBram Moolenaar 193fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 19460e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 19560e73f2aSBram Moolenaar 1963e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 1973e4b84d0SBram Moolenaar " why the debugger doesn't work. 1983e4b84d0SBram Moolenaar let try_count = 0 1993e4b84d0SBram Moolenaar while 1 2003e4b84d0SBram Moolenaar let response = '' 201b3623a38SBram Moolenaar for lnum in range(1,200) 2023e4b84d0SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 2033e4b84d0SBram Moolenaar let response = term_getline(s:gdbbuf, lnum + 1) 2043e4b84d0SBram Moolenaar if response =~ 'Undefined command' 205f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 2063e4b84d0SBram Moolenaar exe 'bwipe! ' . s:ptybuf 2073e4b84d0SBram Moolenaar exe 'bwipe! ' . s:commbuf 2083e4b84d0SBram Moolenaar return 2093e4b84d0SBram Moolenaar endif 2103e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2113e4b84d0SBram Moolenaar " Success! 2123e4b84d0SBram Moolenaar break 2133e4b84d0SBram Moolenaar endif 2143e4b84d0SBram Moolenaar endif 2153e4b84d0SBram Moolenaar endfor 2163e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2173e4b84d0SBram Moolenaar break 2183e4b84d0SBram Moolenaar endif 2193e4b84d0SBram Moolenaar let try_count += 1 2203e4b84d0SBram Moolenaar if try_count > 100 2213e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 2223e4b84d0SBram Moolenaar break 2233e4b84d0SBram Moolenaar endif 2243e4b84d0SBram Moolenaar sleep 10m 2253e4b84d0SBram Moolenaar endwhile 2263e4b84d0SBram Moolenaar 22760e73f2aSBram Moolenaar " Interpret commands while the target is running. This should usualy only be 22860e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 22960e73f2aSBram Moolenaar " running. 23060e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 231b3307b5eSBram Moolenaar " Older gdb uses a different command. 232b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 233e09ba7baSBram Moolenaar 234f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 235f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 236b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 237f3ba14ffSBram Moolenaar 238b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 239b3307b5eSBram Moolenaarendfunc 240b3307b5eSBram Moolenaar 241b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 242b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 243b3307b5eSBram Moolenaar if s:vertical 244b3307b5eSBram Moolenaar vertical new 245b3307b5eSBram Moolenaar else 246b3307b5eSBram Moolenaar new 247b3307b5eSBram Moolenaar endif 248b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 249b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 250b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 251b3307b5eSBram Moolenaar set buftype=prompt 252b3307b5eSBram Moolenaar file gdb 253b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 254b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 255b3307b5eSBram Moolenaar 256b3307b5eSBram Moolenaar if s:vertical 257b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 258b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 259b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 260b3307b5eSBram Moolenaar endif 261b3307b5eSBram Moolenaar 262b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 263b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 264b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 265b3307b5eSBram Moolenaar 266b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 267b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 268b3307b5eSBram Moolenaar 269b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 270b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 271b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 272b3307b5eSBram Moolenaar \ }) 273b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 274b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 275b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 276b3307b5eSBram Moolenaar return 277b3307b5eSBram Moolenaar endif 2784551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 2794551c0a9SBram Moolenaar set modified 280b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 281b3307b5eSBram Moolenaar 282b3307b5eSBram Moolenaar " Interpret commands while the target is running. This should usualy only 283b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 284b3307b5eSBram Moolenaar " target is running. 285b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 286b3307b5eSBram Moolenaar " Older gdb uses a different command. 287b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 288b3307b5eSBram Moolenaar 289b3307b5eSBram Moolenaar let s:ptybuf = 0 290b3307b5eSBram Moolenaar if has('win32') 291b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 292b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 293b3307b5eSBram Moolenaar elseif has('terminal') 294b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 295b3307b5eSBram Moolenaar " gdb window. 296b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 297b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 298b3307b5eSBram Moolenaar \ }) 299b3307b5eSBram Moolenaar if s:ptybuf == 0 300b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 301b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 302b3307b5eSBram Moolenaar return 303b3307b5eSBram Moolenaar endif 304b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 305b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 306b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 307b3307b5eSBram Moolenaar 308b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 309b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 310b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 311b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 312b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 313b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 314b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 315b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 316b3307b5eSBram Moolenaar else 317b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 318b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 319b3307b5eSBram Moolenaar endif 320b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 321b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 322b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 323b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 324b3307b5eSBram Moolenaar 325b3307b5eSBram Moolenaar " Set arguments to be run 326b3307b5eSBram Moolenaar if len(proc_args) 327b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 328b3307b5eSBram Moolenaar endif 329b3307b5eSBram Moolenaar 330b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 331b3307b5eSBram Moolenaar startinsert 332b3307b5eSBram Moolenaarendfunc 333b3307b5eSBram Moolenaar 334b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 33538baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 33638baa3e6SBram Moolenaar " There can be only one. 33738baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 33838baa3e6SBram Moolenaar 33945d5f26dSBram Moolenaar " Install debugger commands in the text window. 340b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 341e09ba7baSBram Moolenaar call s:InstallCommands() 34245d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 343e09ba7baSBram Moolenaar 34451b0f370SBram Moolenaar " Enable showing a balloon with eval info 345246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 346246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 34751b0f370SBram Moolenaar if has("balloon_eval") 34851b0f370SBram Moolenaar set ballooneval 349246fe03dSBram Moolenaar endif 35051b0f370SBram Moolenaar if has("balloon_eval_term") 35151b0f370SBram Moolenaar set balloonevalterm 35251b0f370SBram Moolenaar endif 35351b0f370SBram Moolenaar endif 35451b0f370SBram Moolenaar 355de1a8314SBram Moolenaar " Contains breakpoints that have been placed, key is the number. 356e09ba7baSBram Moolenaar let s:breakpoints = {} 3571b9645deSBram Moolenaar 3581b9645deSBram Moolenaar augroup TermDebug 3591b9645deSBram Moolenaar au BufRead * call s:BufRead() 3601b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 361f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 3621b9645deSBram Moolenaar augroup END 36332c67ba7SBram Moolenaar 364b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 365b3307b5eSBram Moolenaar " window. 36632c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 36732c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 36832c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 36932c67ba7SBram Moolenaar endif 370c572da5fSBram Moolenaarendfunc 371c572da5fSBram Moolenaar 372b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 373b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 374b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 375b3307b5eSBram Moolenaar if s:way == 'prompt' 376b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 377b3307b5eSBram Moolenaar else 378b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 379b3307b5eSBram Moolenaar endif 380b3307b5eSBram Moolenaarendfunc 381b3307b5eSBram Moolenaar 382b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 383b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 384b3307b5eSBram Moolenaar if s:way == 'prompt' 385b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 386b3307b5eSBram Moolenaar else 387b3307b5eSBram Moolenaar let do_continue = 0 388b3307b5eSBram Moolenaar if !s:stopped 389b3307b5eSBram Moolenaar let do_continue = 1 390b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 391b3307b5eSBram Moolenaar sleep 10m 392b3307b5eSBram Moolenaar endif 393b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 394b3307b5eSBram Moolenaar if do_continue 395b3307b5eSBram Moolenaar Continue 396b3307b5eSBram Moolenaar endif 397b3307b5eSBram Moolenaar endif 398b3307b5eSBram Moolenaarendfunc 399b3307b5eSBram Moolenaar 400b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 401b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 402b3307b5eSBram Moolenaar call s:SendCommand(a:text) 403b3307b5eSBram Moolenaarendfunc 404b3307b5eSBram Moolenaar 4054551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 4064551c0a9SBram Moolenaar" breakpoint. 407b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 4082ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 4092ed890f1SBram Moolenaar if has('win32') 4102ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 4112ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 4124551c0a9SBram Moolenaar if s:pid == 0 4134551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 4144551c0a9SBram Moolenaar else 4154551c0a9SBram Moolenaar call debugbreak(s:pid) 4164551c0a9SBram Moolenaar endif 4172ed890f1SBram Moolenaar else 4182ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 4192ed890f1SBram Moolenaar endif 420b3307b5eSBram Moolenaarendfunc 421b3307b5eSBram Moolenaar 422b3307b5eSBram Moolenaar" Function called when gdb outputs text. 423b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 424b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 425b3307b5eSBram Moolenaar 426b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 427b3307b5eSBram Moolenaar " Drop status and echo'd commands. 428a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 429b3307b5eSBram Moolenaar return 430b3307b5eSBram Moolenaar endif 431b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 432b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 433b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 434b3307b5eSBram Moolenaar " Silently drop evaluation errors. 435b3307b5eSBram Moolenaar unlet s:evalexpr 436b3307b5eSBram Moolenaar return 437b3307b5eSBram Moolenaar endif 438b3307b5eSBram Moolenaar elseif a:text[0] == '~' 439b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 440b3307b5eSBram Moolenaar else 441b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 442b3307b5eSBram Moolenaar return 443b3307b5eSBram Moolenaar endif 444b3307b5eSBram Moolenaar 445b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 446b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 447b3307b5eSBram Moolenaar 448b3307b5eSBram Moolenaar " Add the output above the current prompt. 449b3307b5eSBram Moolenaar call append(line('$') - 1, text) 4504551c0a9SBram Moolenaar set modified 451b3307b5eSBram Moolenaar 452b3307b5eSBram Moolenaar call win_gotoid(curwinid) 453b3307b5eSBram Moolenaarendfunc 454b3307b5eSBram Moolenaar 455b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 456b3307b5eSBram Moolenaar" to the next ", unescaping characters. 457b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 458b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 459a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 460b3307b5eSBram Moolenaar return 461b3307b5eSBram Moolenaar endif 462b3307b5eSBram Moolenaar let result = '' 463b3307b5eSBram Moolenaar let i = 1 464b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 465b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 466b3307b5eSBram Moolenaar let i += 1 467b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 468b3307b5eSBram Moolenaar " drop \n 469b3307b5eSBram Moolenaar let i += 1 470b3307b5eSBram Moolenaar continue 471b3307b5eSBram Moolenaar endif 472b3307b5eSBram Moolenaar endif 473b3307b5eSBram Moolenaar let result .= a:quotedText[i] 474b3307b5eSBram Moolenaar let i += 1 475b3307b5eSBram Moolenaar endwhile 476b3307b5eSBram Moolenaar return result 477b3307b5eSBram Moolenaarendfunc 478b3307b5eSBram Moolenaar 479a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 480a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 481a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 482a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 483a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 484a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 485a15b0a93SBram Moolenaar endif 486a15b0a93SBram Moolenaar return name 487a15b0a93SBram Moolenaarendfunc 488a15b0a93SBram Moolenaar 489b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 490fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 491b3623a38SBram Moolenaar unlet s:gdbwin 492e09ba7baSBram Moolenaar 493b3307b5eSBram Moolenaar call s:EndDebugCommon() 494b3307b5eSBram Moolenaarendfunc 495b3307b5eSBram Moolenaar 496b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 497e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 498e09ba7baSBram Moolenaar 499b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 500b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 501b3307b5eSBram Moolenaar endif 502b3307b5eSBram Moolenaar 503b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 504e09ba7baSBram Moolenaar let &signcolumn = s:startsigncolumn 505e09ba7baSBram Moolenaar call s:DeleteCommands() 506e09ba7baSBram Moolenaar 507e09ba7baSBram Moolenaar call win_gotoid(curwinid) 508b3307b5eSBram Moolenaar 50938baa3e6SBram Moolenaar if s:save_columns > 0 51038baa3e6SBram Moolenaar let &columns = s:save_columns 51138baa3e6SBram Moolenaar endif 5121b9645deSBram Moolenaar 513246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 514246fe03dSBram Moolenaar set balloonexpr= 51551b0f370SBram Moolenaar if has("balloon_eval") 51651b0f370SBram Moolenaar set noballooneval 517246fe03dSBram Moolenaar endif 51851b0f370SBram Moolenaar if has("balloon_eval_term") 51951b0f370SBram Moolenaar set noballoonevalterm 52051b0f370SBram Moolenaar endif 52151b0f370SBram Moolenaar endif 52251b0f370SBram Moolenaar 5231b9645deSBram Moolenaar au! TermDebug 524fe386641SBram Moolenaarendfunc 525fe386641SBram Moolenaar 526b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 527b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 528b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 5294551c0a9SBram Moolenaar set nomodified 530b3307b5eSBram Moolenaar close 531b3307b5eSBram Moolenaar if curwinid != s:gdbwin 532b3307b5eSBram Moolenaar call win_gotoid(curwinid) 533b3307b5eSBram Moolenaar endif 534b3307b5eSBram Moolenaar 535b3307b5eSBram Moolenaar call s:EndDebugCommon() 536b3307b5eSBram Moolenaar unlet s:gdbwin 537b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 538b3307b5eSBram Moolenaarendfunc 539b3307b5eSBram Moolenaar 540fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 541fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 542fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 543fe386641SBram Moolenaar 544fe386641SBram Moolenaar for msg in msgs 545fe386641SBram Moolenaar " remove prefixed NL 546fe386641SBram Moolenaar if msg[0] == "\n" 547fe386641SBram Moolenaar let msg = msg[1:] 548fe386641SBram Moolenaar endif 549fe386641SBram Moolenaar if msg != '' 5501b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 551e09ba7baSBram Moolenaar call s:HandleCursor(msg) 55245d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 553e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 554e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 555e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 5564551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 5574551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 55845d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 55945d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 56045d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 56145d5f26dSBram Moolenaar call s:HandleError(msg) 562e09ba7baSBram Moolenaar endif 563e09ba7baSBram Moolenaar endif 564e09ba7baSBram Moolenaar endfor 565e09ba7baSBram Moolenaarendfunc 566e09ba7baSBram Moolenaar 567e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 568e09ba7baSBram Moolenaarfunc s:InstallCommands() 569e09ba7baSBram Moolenaar command Break call s:SetBreakpoint() 57071137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 571e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 57245d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 573e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 57460e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 57560e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 57660e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 577b3307b5eSBram Moolenaar 578b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 579b3307b5eSBram Moolenaar if s:way == 'prompt' 580b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 581b3307b5eSBram Moolenaar else 582b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 583b3307b5eSBram Moolenaar endif 584b3307b5eSBram Moolenaar 58545d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 58645d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 58745d5f26dSBram Moolenaar command Program call win_gotoid(s:ptywin) 588b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 58971137fedSBram Moolenaar command Winbar call s:InstallWinbar() 59045d5f26dSBram Moolenaar 59145d5f26dSBram Moolenaar " TODO: can the K mapping be restored? 59245d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 5931b9645deSBram Moolenaar 594f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 59571137fedSBram Moolenaar call s:InstallWinbar() 59671137fedSBram Moolenaar 59771137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 59871137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 59971137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 60071137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 60171137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 60271137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 60371137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 60471137fedSBram Moolenaar endif 60571137fedSBram Moolenaar endif 60671137fedSBram Moolenaarendfunc 60771137fedSBram Moolenaar 60871137fedSBram Moolenaarlet s:winbar_winids = [] 60971137fedSBram Moolenaar 61071137fedSBram Moolenaar" Install the window toolbar in the current window. 61171137fedSBram Moolenaarfunc s:InstallWinbar() 612c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 61324a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 61424a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 61524a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 61624a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 61760e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 61824a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 61971137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 620c4b533e1SBram Moolenaar endif 621e09ba7baSBram Moolenaarendfunc 622e09ba7baSBram Moolenaar 623e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 624e09ba7baSBram Moolenaarfunc s:DeleteCommands() 625e09ba7baSBram Moolenaar delcommand Break 62671137fedSBram Moolenaar delcommand Clear 627e09ba7baSBram Moolenaar delcommand Step 62845d5f26dSBram Moolenaar delcommand Over 629e09ba7baSBram Moolenaar delcommand Finish 63060e73f2aSBram Moolenaar delcommand Run 63160e73f2aSBram Moolenaar delcommand Arguments 63260e73f2aSBram Moolenaar delcommand Stop 633e09ba7baSBram Moolenaar delcommand Continue 63445d5f26dSBram Moolenaar delcommand Evaluate 63545d5f26dSBram Moolenaar delcommand Gdb 63645d5f26dSBram Moolenaar delcommand Program 637b3623a38SBram Moolenaar delcommand Source 63871137fedSBram Moolenaar delcommand Winbar 63945d5f26dSBram Moolenaar 64045d5f26dSBram Moolenaar nunmap K 6411b9645deSBram Moolenaar 6421b9645deSBram Moolenaar if has('menu') 64371137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 64471137fedSBram Moolenaar let curwinid = win_getid(winnr()) 64571137fedSBram Moolenaar for winid in s:winbar_winids 64671137fedSBram Moolenaar if win_gotoid(winid) 6471b9645deSBram Moolenaar aunmenu WinBar.Step 6481b9645deSBram Moolenaar aunmenu WinBar.Next 6491b9645deSBram Moolenaar aunmenu WinBar.Finish 6501b9645deSBram Moolenaar aunmenu WinBar.Cont 65160e73f2aSBram Moolenaar aunmenu WinBar.Stop 6521b9645deSBram Moolenaar aunmenu WinBar.Eval 6531b9645deSBram Moolenaar endif 65471137fedSBram Moolenaar endfor 65571137fedSBram Moolenaar call win_gotoid(curwinid) 65671137fedSBram Moolenaar let s:winbar_winids = [] 65771137fedSBram Moolenaar 65871137fedSBram Moolenaar if exists('s:saved_mousemodel') 65971137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 66071137fedSBram Moolenaar unlet s:saved_mousemodel 66171137fedSBram Moolenaar aunmenu PopUp.-SEP3- 66271137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 66371137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 66471137fedSBram Moolenaar aunmenu PopUp.Evaluate 66571137fedSBram Moolenaar endif 66671137fedSBram Moolenaar endif 6671b9645deSBram Moolenaar 66845d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 66945d5f26dSBram Moolenaar for key in keys(s:breakpoints) 67045d5f26dSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 67145d5f26dSBram Moolenaar endfor 67245d5f26dSBram Moolenaar unlet s:breakpoints 673a15b0a93SBram Moolenaar 674a15b0a93SBram Moolenaar sign undefine debugPC 675a15b0a93SBram Moolenaar for val in s:BreakpointSigns 676a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 677a15b0a93SBram Moolenaar endfor 6784551c0a9SBram Moolenaar let s:BreakpointSigns = [] 679e09ba7baSBram Moolenaarendfunc 680e09ba7baSBram Moolenaar 681e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 682e09ba7baSBram Moolenaarfunc s:SetBreakpoint() 68360e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 68460e73f2aSBram Moolenaar " Interrupt to make it work. 68560e73f2aSBram Moolenaar let do_continue = 0 68660e73f2aSBram Moolenaar if !s:stopped 68760e73f2aSBram Moolenaar let do_continue = 1 688b3307b5eSBram Moolenaar if s:way == 'prompt' 6894551c0a9SBram Moolenaar call s:PromptInterrupt() 690b3307b5eSBram Moolenaar else 69160e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 692b3307b5eSBram Moolenaar endif 69360e73f2aSBram Moolenaar sleep 10m 69460e73f2aSBram Moolenaar endif 695a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 696a15b0a93SBram Moolenaar call s:SendCommand('-break-insert ' 697a15b0a93SBram Moolenaar \ . fnameescape(expand('%:p')) . ':' . line('.')) 69860e73f2aSBram Moolenaar if do_continue 69960e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 70060e73f2aSBram Moolenaar endif 701e09ba7baSBram Moolenaarendfunc 702e09ba7baSBram Moolenaar 70371137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 70471137fedSBram Moolenaarfunc s:ClearBreakpoint() 705e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 706e09ba7baSBram Moolenaar let lnum = line('.') 707e09ba7baSBram Moolenaar for [key, val] in items(s:breakpoints) 708e09ba7baSBram Moolenaar if val['fname'] == fname && val['lnum'] == lnum 709b3307b5eSBram Moolenaar call s:SendCommand('-break-delete ' . key) 710e09ba7baSBram Moolenaar " Assume this always wors, the reply is simply "^done". 711e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + key) 712e09ba7baSBram Moolenaar unlet s:breakpoints[key] 713e09ba7baSBram Moolenaar break 714e09ba7baSBram Moolenaar endif 715e09ba7baSBram Moolenaar endfor 716e09ba7baSBram Moolenaarendfunc 717e09ba7baSBram Moolenaar 71860e73f2aSBram Moolenaarfunc s:Run(args) 71960e73f2aSBram Moolenaar if a:args != '' 72060e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 72160e73f2aSBram Moolenaar endif 72260e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 72360e73f2aSBram Moolenaarendfunc 72460e73f2aSBram Moolenaar 72551b0f370SBram Moolenaarfunc s:SendEval(expr) 72651b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 72751b0f370SBram Moolenaar let s:evalexpr = a:expr 72851b0f370SBram Moolenaarendfunc 72951b0f370SBram Moolenaar 73045d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 73145d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 73245d5f26dSBram Moolenaar if a:arg != '' 73345d5f26dSBram Moolenaar let expr = a:arg 73445d5f26dSBram Moolenaar elseif a:range == 2 73545d5f26dSBram Moolenaar let pos = getcurpos() 73645d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 73745d5f26dSBram Moolenaar let regt = getregtype('v') 73845d5f26dSBram Moolenaar normal! gv"vy 73945d5f26dSBram Moolenaar let expr = @v 74045d5f26dSBram Moolenaar call setpos('.', pos) 74145d5f26dSBram Moolenaar call setreg('v', reg, regt) 74245d5f26dSBram Moolenaar else 74345d5f26dSBram Moolenaar let expr = expand('<cexpr>') 74445d5f26dSBram Moolenaar endif 74522f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 74651b0f370SBram Moolenaar call s:SendEval(expr) 74745d5f26dSBram Moolenaarendfunc 74845d5f26dSBram Moolenaar 74922f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 75051b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 75151b0f370SBram Moolenaar 75245d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 75345d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 7541b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 7551b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 75651b0f370SBram Moolenaar if s:evalFromBalloonExpr 75751b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 75851b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 75951b0f370SBram Moolenaar else 76051b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 76151b0f370SBram Moolenaar endif 76251b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 76351b0f370SBram Moolenaar else 7641b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 76551b0f370SBram Moolenaar endif 7661b9645deSBram Moolenaar 7677f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 7681b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 76922f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 77051b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 77151b0f370SBram Moolenaar else 77251b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 7731b9645deSBram Moolenaar endif 77445d5f26dSBram Moolenaarendfunc 77545d5f26dSBram Moolenaar 77651b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 77751b0f370SBram Moolenaar" if there is any. 77851b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 779b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 780b3307b5eSBram Moolenaar return 781b3307b5eSBram Moolenaar endif 782b3307b5eSBram Moolenaar if !s:stopped 783b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 784b3307b5eSBram Moolenaar " mouse triggers a balloon. 78551b0f370SBram Moolenaar return 78651b0f370SBram Moolenaar endif 78751b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 78851b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 78922f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 79022f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 79151b0f370SBram Moolenaar return '' 79251b0f370SBram Moolenaarendfunc 79351b0f370SBram Moolenaar 79445d5f26dSBram Moolenaar" Handle an error. 79545d5f26dSBram Moolenaarfunc s:HandleError(msg) 79622f1d0e3SBram Moolenaar if s:ignoreEvalError 79751b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 79822f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 79922f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 80051b0f370SBram Moolenaar return 80151b0f370SBram Moolenaar endif 80245d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 80345d5f26dSBram Moolenaarendfunc 80445d5f26dSBram Moolenaar 805b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 806b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 807c4b533e1SBram Moolenaar new 808b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 809c4b533e1SBram Moolenaar call s:InstallWinbar() 810c4b533e1SBram Moolenaar endif 811c4b533e1SBram Moolenaarendfunc 812c4b533e1SBram Moolenaar 813e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 814e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 815e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 816fe386641SBram Moolenaar let wid = win_getid(winnr()) 817fe386641SBram Moolenaar 81860e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 8194551c0a9SBram Moolenaar call ch_log('program stopped') 82060e73f2aSBram Moolenaar let s:stopped = 1 82160e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 8224551c0a9SBram Moolenaar call ch_log('program running') 82360e73f2aSBram Moolenaar let s:stopped = 0 82460e73f2aSBram Moolenaar endif 82560e73f2aSBram Moolenaar 826a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 827a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 828a15b0a93SBram Moolenaar else 829a15b0a93SBram Moolenaar let fname = '' 830a15b0a93SBram Moolenaar endif 8311b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 832e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 833fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 8344551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 8351b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 836fe386641SBram Moolenaar if &modified 837fe386641SBram Moolenaar " TODO: find existing window 838fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 839b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 840c4b533e1SBram Moolenaar call s:InstallWinbar() 841fe386641SBram Moolenaar else 842fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 843fe386641SBram Moolenaar endif 844fe386641SBram Moolenaar endif 845fe386641SBram Moolenaar exe lnum 84601164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 8471b9645deSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 848fe386641SBram Moolenaar setlocal signcolumn=yes 849fe386641SBram Moolenaar endif 8504551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 851fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 852fe386641SBram Moolenaar endif 853fe386641SBram Moolenaar 854fe386641SBram Moolenaar call win_gotoid(wid) 855e09ba7baSBram Moolenaarendfunc 856e09ba7baSBram Moolenaar 857de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 858a15b0a93SBram Moolenaar 859a15b0a93SBram Moolenaarfunc s:CreateBreakpoint(nr) 860de1a8314SBram Moolenaar if index(s:BreakpointSigns, a:nr) == -1 861de1a8314SBram Moolenaar call add(s:BreakpointSigns, a:nr) 862de1a8314SBram Moolenaar exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint" 863de1a8314SBram Moolenaar endif 864de1a8314SBram Moolenaarendfunc 865de1a8314SBram Moolenaar 866e09ba7baSBram Moolenaar" Handle setting a breakpoint 867e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 868e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 8696dccc962SBram Moolenaar if a:msg !~ 'fullname=' 8706dccc962SBram Moolenaar " a watch does not have a file name 8716dccc962SBram Moolenaar return 8726dccc962SBram Moolenaar endif 8736dccc962SBram Moolenaar 874e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*number="\([0-9]\)*\".*', '\1', '') + 0 875e09ba7baSBram Moolenaar if nr == 0 876e09ba7baSBram Moolenaar return 877fe386641SBram Moolenaar endif 878de1a8314SBram Moolenaar call s:CreateBreakpoint(nr) 879e09ba7baSBram Moolenaar 880e09ba7baSBram Moolenaar if has_key(s:breakpoints, nr) 881e09ba7baSBram Moolenaar let entry = s:breakpoints[nr] 882e09ba7baSBram Moolenaar else 883e09ba7baSBram Moolenaar let entry = {} 884e09ba7baSBram Moolenaar let s:breakpoints[nr] = entry 885fe386641SBram Moolenaar endif 886e09ba7baSBram Moolenaar 887a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 888e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 889e09ba7baSBram Moolenaar let entry['fname'] = fname 890e09ba7baSBram Moolenaar let entry['lnum'] = lnum 8911b9645deSBram Moolenaar 8921b9645deSBram Moolenaar if bufloaded(fname) 8931b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 8941b9645deSBram Moolenaar endif 8951b9645deSBram Moolenaarendfunc 8961b9645deSBram Moolenaar 8971b9645deSBram Moolenaarfunc s:PlaceSign(nr, entry) 898de1a8314SBram Moolenaar exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname'] 8991b9645deSBram Moolenaar let a:entry['placed'] = 1 900e09ba7baSBram Moolenaarendfunc 901e09ba7baSBram Moolenaar 902e09ba7baSBram Moolenaar" Handle deleting a breakpoint 903e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 904e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 905e09ba7baSBram Moolenaar let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 906e09ba7baSBram Moolenaar if nr == 0 907e09ba7baSBram Moolenaar return 908e09ba7baSBram Moolenaar endif 9091b9645deSBram Moolenaar if has_key(s:breakpoints, nr) 9101b9645deSBram Moolenaar let entry = s:breakpoints[nr] 9111b9645deSBram Moolenaar if has_key(entry, 'placed') 912e09ba7baSBram Moolenaar exe 'sign unplace ' . (s:break_id + nr) 9131b9645deSBram Moolenaar unlet entry['placed'] 9141b9645deSBram Moolenaar endif 915e09ba7baSBram Moolenaar unlet s:breakpoints[nr] 9161b9645deSBram Moolenaar endif 917c572da5fSBram Moolenaarendfunc 9181b9645deSBram Moolenaar 9194551c0a9SBram Moolenaar" Handle the debugged program starting to run. 9204551c0a9SBram Moolenaar" Will store the process ID in s:pid 9214551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 9224551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 9234551c0a9SBram Moolenaar if nr == 0 9244551c0a9SBram Moolenaar return 9254551c0a9SBram Moolenaar endif 9264551c0a9SBram Moolenaar let s:pid = nr 9274551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 9284551c0a9SBram Moolenaarendfunc 9294551c0a9SBram Moolenaar 9301b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 9311b9645deSBram Moolenaarfunc s:BufRead() 9321b9645deSBram Moolenaar let fname = expand('<afile>:p') 9331b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9341b9645deSBram Moolenaar if entry['fname'] == fname 9351b9645deSBram Moolenaar call s:PlaceSign(nr, entry) 9361b9645deSBram Moolenaar endif 9371b9645deSBram Moolenaar endfor 9381b9645deSBram Moolenaarendfunc 9391b9645deSBram Moolenaar 9401b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 9411b9645deSBram Moolenaarfunc s:BufUnloaded() 9421b9645deSBram Moolenaar let fname = expand('<afile>:p') 9431b9645deSBram Moolenaar for [nr, entry] in items(s:breakpoints) 9441b9645deSBram Moolenaar if entry['fname'] == fname 9451b9645deSBram Moolenaar let entry['placed'] = 0 9461b9645deSBram Moolenaar endif 9471b9645deSBram Moolenaar endfor 9481b9645deSBram Moolenaarendfunc 949*ca4cc018SBram Moolenaar 950*ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 951*ca4cc018SBram Moolenaarunlet s:keepcpo 952