1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3b3307b5eSBram Moolenaar" Author: Bram Moolenaar 4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license" 5*6aa57295SBram Moolenaar" Last Change: 2021 Aug 06 6c572da5fSBram Moolenaar" 7b3307b5eSBram Moolenaar" WORK IN PROGRESS - Only the basics work 8b3307b5eSBram Moolenaar" Note: On MS-Windows you need a recent version of gdb. The one included with 9b3307b5eSBram Moolenaar" MingW is too old (7.6.1). 10b3307b5eSBram Moolenaar" I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb 11fe386641SBram Moolenaar" 12b3307b5eSBram Moolenaar" There are two ways to run gdb: 13b3307b5eSBram Moolenaar" - In a terminal window; used if possible, does not work on MS-Windows 14b3307b5eSBram Moolenaar" Not used when g:termdebug_use_prompt is set to 1. 15b3307b5eSBram Moolenaar" - Using a "prompt" buffer; may use a terminal window for the program 16b3307b5eSBram Moolenaar" 17b3307b5eSBram Moolenaar" For both the current window is used to view source code and shows the 18b3307b5eSBram Moolenaar" current statement from gdb. 19b3307b5eSBram Moolenaar" 20b3307b5eSBram Moolenaar" USING A TERMINAL WINDOW 21b3307b5eSBram Moolenaar" 22b3307b5eSBram Moolenaar" Opens two visible terminal windows: 23b3307b5eSBram Moolenaar" 1. runs a pty for the debugged program, as with ":term NONE" 24b3307b5eSBram Moolenaar" 2. runs gdb, passing the pty of the debugged program 25fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb. 26fe386641SBram Moolenaar" 27b3307b5eSBram Moolenaar" USING A PROMPT BUFFER 28b3307b5eSBram Moolenaar" 29b3307b5eSBram Moolenaar" Opens a window with a prompt buffer to communicate with gdb. 30b3307b5eSBram Moolenaar" Gdb is run as a job with callbacks for I/O. 31b3307b5eSBram Moolenaar" On Unix another terminal window is opened to run the debugged program 32b3307b5eSBram Moolenaar" On MS-Windows a separate console is opened to run the debugged program 33b3307b5eSBram Moolenaar" 34fe386641SBram Moolenaar" The communication with gdb uses GDB/MI. See: 35fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html 36c572da5fSBram Moolenaar 37b3307b5eSBram Moolenaar" In case this gets sourced twice. 3837c64c78SBram Moolenaarif exists(':Termdebug') 3937c64c78SBram Moolenaar finish 4037c64c78SBram Moolenaarendif 4137c64c78SBram Moolenaar 42b3307b5eSBram Moolenaar" Need either the +terminal feature or +channel and the prompt buffer. 43b3307b5eSBram Moolenaar" The terminal feature does not work with gdb on win32. 44b3307b5eSBram Moolenaarif has('terminal') && !has('win32') 45b3307b5eSBram Moolenaar let s:way = 'terminal' 46b3307b5eSBram Moolenaarelseif has('channel') && exists('*prompt_setprompt') 47b3307b5eSBram Moolenaar let s:way = 'prompt' 48b3307b5eSBram Moolenaarelse 49b3307b5eSBram Moolenaar if has('terminal') 50b3307b5eSBram Moolenaar let s:err = 'Cannot debug, missing prompt buffer support' 51b3307b5eSBram Moolenaar else 52b3307b5eSBram Moolenaar let s:err = 'Cannot debug, +channel feature is not supported' 53b3307b5eSBram Moolenaar endif 54b3307b5eSBram Moolenaar command -nargs=* -complete=file -bang Termdebug echoerr s:err 55b3307b5eSBram Moolenaar command -nargs=+ -complete=file -bang TermdebugCommand echoerr s:err 56b3307b5eSBram Moolenaar finish 57b3307b5eSBram Moolenaarendif 5860e73f2aSBram Moolenaar 59ca4cc018SBram Moolenaarlet s:keepcpo = &cpo 60ca4cc018SBram Moolenaarset cpo&vim 61ca4cc018SBram Moolenaar 62fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim". 63fe386641SBram Moolenaar" To end type "quit" in the gdb window. 6432c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>) 6532c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>) 66c572da5fSBram Moolenaar 67fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb". 6818223a59SBram Moolenaarif !exists('g:termdebugger') 6918223a59SBram Moolenaar let g:termdebugger = 'gdb' 70c572da5fSBram Moolenaarendif 71c572da5fSBram Moolenaar 72fe386641SBram Moolenaarlet s:pc_id = 12 7382be4849SBram Moolenaarlet s:asm_id = 13 7482be4849SBram Moolenaarlet s:break_id = 14 " breakpoint number is added to this 7560e73f2aSBram Moolenaarlet s:stopped = 1 76e09ba7baSBram Moolenaar 7782be4849SBram Moolenaarlet s:parsing_disasm_msg = 0 7882be4849SBram Moolenaarlet s:asm_lines = [] 7982be4849SBram Moolenaarlet s:asm_addr = '' 8082be4849SBram Moolenaar 815378e1cfSBram Moolenaar" Take a breakpoint number as used by GDB and turn it into an integer. 8237402ed5SBram Moolenaar" The breakpoint may contain a dot: 123.4 -> 123004 8337402ed5SBram Moolenaar" The main breakpoint has a zero subid. 8437402ed5SBram Moolenaarfunc s:Breakpoint2SignNumber(id, subid) 8537402ed5SBram Moolenaar return s:break_id + a:id * 1000 + a:subid 865378e1cfSBram Moolenaarendfunction 875378e1cfSBram Moolenaar 88f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new) 89f07f9e73SBram Moolenaar let default = a:init ? 'default ' : '' 90f07f9e73SBram Moolenaar if a:new ==# 'light' && a:old !=# 'light' 91f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" 92f07f9e73SBram Moolenaar elseif a:new ==# 'dark' && a:old !=# 'dark' 93f07f9e73SBram Moolenaar exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue" 94e09ba7baSBram Moolenaar endif 95f07f9e73SBram Moolenaarendfunc 96f07f9e73SBram Moolenaar 97f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background) 98e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red 99fe386641SBram Moolenaar 10032c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...) 10132c67ba7SBram Moolenaar " First argument is the command to debug, second core file or process ID. 10232c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang}) 10332c67ba7SBram Moolenaarendfunc 10432c67ba7SBram Moolenaar 10532c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...) 10632c67ba7SBram Moolenaar " First argument is the command to debug, rest are run arguments. 10732c67ba7SBram Moolenaar call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang}) 10832c67ba7SBram Moolenaarendfunc 10932c67ba7SBram Moolenaar 11032c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict) 111b3623a38SBram Moolenaar if exists('s:gdbwin') 11218223a59SBram Moolenaar echoerr 'Terminal debugger already running, cannot run two' 113b3623a38SBram Moolenaar return 114b3623a38SBram Moolenaar endif 11518223a59SBram Moolenaar if !executable(g:termdebugger) 11618223a59SBram Moolenaar echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"' 11718223a59SBram Moolenaar return 11818223a59SBram Moolenaar endif 11918223a59SBram Moolenaar 120b3307b5eSBram Moolenaar let s:ptywin = 0 1214551c0a9SBram Moolenaar let s:pid = 0 12282be4849SBram Moolenaar let s:asmwin = 0 123b3623a38SBram Moolenaar 124*6aa57295SBram Moolenaar if exists('#User#TermdebugStartPre') 125*6aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStartPre 126*6aa57295SBram Moolenaar endif 127*6aa57295SBram Moolenaar 128b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 129b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 130b3307b5eSBram Moolenaar 131b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 132cb80aa2dSBram Moolenaar 133cb80aa2dSBram Moolenaar " Remember the old value of 'signcolumn' for each buffer that it's set in, so 134cb80aa2dSBram Moolenaar " that we can restore the value for all buffers. 135cb80aa2dSBram Moolenaar let b:save_signcolumn = &signcolumn 136cb80aa2dSBram Moolenaar let s:signcolumn_buflist = [bufnr()] 137fe386641SBram Moolenaar 13824a98a0eSBram Moolenaar let s:save_columns = 0 13968e6560bSBram Moolenaar let s:allleft = 0 14024a98a0eSBram Moolenaar if exists('g:termdebug_wide') 14124a98a0eSBram Moolenaar if &columns < g:termdebug_wide 14238baa3e6SBram Moolenaar let s:save_columns = &columns 14338baa3e6SBram Moolenaar let &columns = g:termdebug_wide 14468e6560bSBram Moolenaar " If we make the Vim window wider, use the whole left halve for the debug 14568e6560bSBram Moolenaar " windows. 14668e6560bSBram Moolenaar let s:allleft = 1 14724a98a0eSBram Moolenaar endif 148b3307b5eSBram Moolenaar let s:vertical = 1 14938baa3e6SBram Moolenaar else 150b3307b5eSBram Moolenaar let s:vertical = 0 15138baa3e6SBram Moolenaar endif 15238baa3e6SBram Moolenaar 153b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 154b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 155b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 156b3307b5eSBram Moolenaar let s:way = 'terminal' 157b3307b5eSBram Moolenaar else 158b3307b5eSBram Moolenaar let s:way = 'prompt' 159b3307b5eSBram Moolenaar endif 160b3307b5eSBram Moolenaar 161b3307b5eSBram Moolenaar if s:way == 'prompt' 162b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 163b3307b5eSBram Moolenaar else 164b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 165b3307b5eSBram Moolenaar endif 16682be4849SBram Moolenaar 16782be4849SBram Moolenaar if exists('g:termdebug_disasm_window') 16882be4849SBram Moolenaar if g:termdebug_disasm_window 16982be4849SBram Moolenaar let curwinid = win_getid(winnr()) 17082be4849SBram Moolenaar call s:GotoAsmwinOrCreateIt() 17182be4849SBram Moolenaar call win_gotoid(curwinid) 17282be4849SBram Moolenaar endif 17382be4849SBram Moolenaar endif 174*6aa57295SBram Moolenaar 175*6aa57295SBram Moolenaar if exists('#User#TermdebugStartPost') 176*6aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStartPost 177*6aa57295SBram Moolenaar endif 178b3307b5eSBram Moolenaarendfunc 179b3307b5eSBram Moolenaar 180ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended. 181ef3c6a5bSBram Moolenaarfunc s:CloseBuffers() 182ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:ptybuf 183ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:commbuf 184ef3c6a5bSBram Moolenaar unlet! s:gdbwin 185ef3c6a5bSBram Moolenaarendfunc 186ef3c6a5bSBram Moolenaar 187d2ea7cf1SBram Moolenaarfunc s:CheckGdbRunning() 188d2ea7cf1SBram Moolenaar let gdbproc = term_getjob(s:gdbbuf) 189d2ea7cf1SBram Moolenaar if gdbproc == v:null || job_status(gdbproc) !=# 'run' 190d2ea7cf1SBram Moolenaar echoerr string(g:termdebugger) . ' exited unexpectedly' 191d2ea7cf1SBram Moolenaar call s:CloseBuffers() 192d2ea7cf1SBram Moolenaar return '' 193d2ea7cf1SBram Moolenaar endif 194d2ea7cf1SBram Moolenaar return 'ok' 195d2ea7cf1SBram Moolenaarendfunc 196d2ea7cf1SBram Moolenaar 197b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 198b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 199fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 200b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 201b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 202fe386641SBram Moolenaar \ }) 203fe386641SBram Moolenaar if s:ptybuf == 0 204fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 205fe386641SBram Moolenaar return 206fe386641SBram Moolenaar endif 207fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 20845d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 209b3307b5eSBram Moolenaar if s:vertical 21051b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 21151b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 21251b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 21368e6560bSBram Moolenaar if s:allleft 21468e6560bSBram Moolenaar " use the whole left column 21568e6560bSBram Moolenaar wincmd H 21668e6560bSBram Moolenaar endif 21751b0f370SBram Moolenaar endif 218fe386641SBram Moolenaar 219fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 220fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 221fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 222fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 223fe386641SBram Moolenaar \ 'hidden': 1, 224fe386641SBram Moolenaar \ }) 225fe386641SBram Moolenaar if s:commbuf == 0 226fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 227fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 228fe386641SBram Moolenaar return 229fe386641SBram Moolenaar endif 230fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 231c572da5fSBram Moolenaar 232c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 233c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 23432c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 23532c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 23632c67ba7SBram Moolenaar 237d2ea7cf1SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty, '--eval-command', 'echo startupdone\n'] + gdb_args 238b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 23960e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 240fe386641SBram Moolenaar \ 'term_finish': 'close', 241c572da5fSBram Moolenaar \ }) 24260e73f2aSBram Moolenaar if s:gdbbuf == 0 243fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 244ef3c6a5bSBram Moolenaar call s:CloseBuffers() 245fe386641SBram Moolenaar return 246fe386641SBram Moolenaar endif 24745d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 248fe386641SBram Moolenaar 249d2ea7cf1SBram Moolenaar " Wait for the "startupdone" message before sending any commands. 250d2ea7cf1SBram Moolenaar let try_count = 0 251d2ea7cf1SBram Moolenaar while 1 252d2ea7cf1SBram Moolenaar if s:CheckGdbRunning() != 'ok' 253d2ea7cf1SBram Moolenaar return 254d2ea7cf1SBram Moolenaar endif 255d2ea7cf1SBram Moolenaar 256d2ea7cf1SBram Moolenaar for lnum in range(1, 200) 257d2ea7cf1SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'startupdone' 258d2ea7cf1SBram Moolenaar let try_count = 9999 259d2ea7cf1SBram Moolenaar break 260d2ea7cf1SBram Moolenaar endif 261d2ea7cf1SBram Moolenaar endfor 262d2ea7cf1SBram Moolenaar let try_count += 1 263d2ea7cf1SBram Moolenaar if try_count > 300 264d2ea7cf1SBram Moolenaar " done or give up after five seconds 265d2ea7cf1SBram Moolenaar break 266d2ea7cf1SBram Moolenaar endif 267d2ea7cf1SBram Moolenaar sleep 10m 268d2ea7cf1SBram Moolenaar endwhile 269d2ea7cf1SBram Moolenaar 270d2ea7cf1SBram Moolenaar " Set arguments to be run. 27132c67ba7SBram Moolenaar if len(proc_args) 27232c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 27332c67ba7SBram Moolenaar endif 27432c67ba7SBram Moolenaar 275fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 27660e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 27760e73f2aSBram Moolenaar 2783e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 2793e4b84d0SBram Moolenaar " why the debugger doesn't work. 2803e4b84d0SBram Moolenaar let try_count = 0 2813e4b84d0SBram Moolenaar while 1 282d2ea7cf1SBram Moolenaar if s:CheckGdbRunning() != 'ok' 283ef3c6a5bSBram Moolenaar return 284ef3c6a5bSBram Moolenaar endif 285ef3c6a5bSBram Moolenaar 2863e4b84d0SBram Moolenaar let response = '' 287b3623a38SBram Moolenaar for lnum in range(1, 200) 28819c8fe19SBram Moolenaar let line1 = term_getline(s:gdbbuf, lnum) 28919c8fe19SBram Moolenaar let line2 = term_getline(s:gdbbuf, lnum + 1) 29019c8fe19SBram Moolenaar if line1 =~ 'new-ui mi ' 291f63db65bSBram Moolenaar " response can be in the same line or the next line 29219c8fe19SBram Moolenaar let response = line1 . line2 2933e4b84d0SBram Moolenaar if response =~ 'Undefined command' 294f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 295ef3c6a5bSBram Moolenaar call s:CloseBuffers() 2963e4b84d0SBram Moolenaar return 2973e4b84d0SBram Moolenaar endif 2983e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2993e4b84d0SBram Moolenaar " Success! 3003e4b84d0SBram Moolenaar break 3013e4b84d0SBram Moolenaar endif 30219c8fe19SBram Moolenaar elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' 30319c8fe19SBram Moolenaar " Reading symbols might take a while, try more times 30406fe74aeSBram Moolenaar let try_count -= 1 30506fe74aeSBram Moolenaar endif 3063e4b84d0SBram Moolenaar endfor 3073e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 3083e4b84d0SBram Moolenaar break 3093e4b84d0SBram Moolenaar endif 3103e4b84d0SBram Moolenaar let try_count += 1 3113e4b84d0SBram Moolenaar if try_count > 100 3123e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 3133e4b84d0SBram Moolenaar break 3143e4b84d0SBram Moolenaar endif 3153e4b84d0SBram Moolenaar sleep 10m 3163e4b84d0SBram Moolenaar endwhile 3173e4b84d0SBram Moolenaar 31891359014SBram Moolenaar " Interpret commands while the target is running. This should usually only be 31960e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 32060e73f2aSBram Moolenaar " running. 32160e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 322b3307b5eSBram Moolenaar " Older gdb uses a different command. 323b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 324e09ba7baSBram Moolenaar 325f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 326f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 327b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 328f3ba14ffSBram Moolenaar 329d2ea7cf1SBram Moolenaar call job_setoptions(term_getjob(s:gdbbuf), {'exit_cb': function('s:EndTermDebug')}) 330b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 331b3307b5eSBram Moolenaarendfunc 332b3307b5eSBram Moolenaar 333b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 334b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 335b3307b5eSBram Moolenaar if s:vertical 336b3307b5eSBram Moolenaar vertical new 337b3307b5eSBram Moolenaar else 338b3307b5eSBram Moolenaar new 339b3307b5eSBram Moolenaar endif 340b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 341b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 342b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 343b3307b5eSBram Moolenaar set buftype=prompt 344b3307b5eSBram Moolenaar file gdb 345b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 346b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 347b3307b5eSBram Moolenaar 348b3307b5eSBram Moolenaar if s:vertical 349b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 350b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 351b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 352b3307b5eSBram Moolenaar endif 353b3307b5eSBram Moolenaar 354b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 355b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 356b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 357b3307b5eSBram Moolenaar 358b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 359b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 360b3307b5eSBram Moolenaar 361b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 362b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 363b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 364b3307b5eSBram Moolenaar \ }) 365b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 366b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 367b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 368b3307b5eSBram Moolenaar return 369b3307b5eSBram Moolenaar endif 3704551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 3714551c0a9SBram Moolenaar set modified 372b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 373b3307b5eSBram Moolenaar 37491359014SBram Moolenaar " Interpret commands while the target is running. This should usually only 375b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 376b3307b5eSBram Moolenaar " target is running. 377b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 378b3307b5eSBram Moolenaar " Older gdb uses a different command. 379b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 380b3307b5eSBram Moolenaar 381b3307b5eSBram Moolenaar let s:ptybuf = 0 382b3307b5eSBram Moolenaar if has('win32') 383b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 384b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 385b3307b5eSBram Moolenaar elseif has('terminal') 386b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 387b3307b5eSBram Moolenaar " gdb window. 388b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 389b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 390b3307b5eSBram Moolenaar \ }) 391b3307b5eSBram Moolenaar if s:ptybuf == 0 392b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 393b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 394b3307b5eSBram Moolenaar return 395b3307b5eSBram Moolenaar endif 396b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 397b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 398b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 399b3307b5eSBram Moolenaar 400b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 401b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 402b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 403b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 404b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 405b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 406b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 407b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 408b3307b5eSBram Moolenaar else 409b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 410b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 411b3307b5eSBram Moolenaar endif 412b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 413b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 414b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 415b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 416b3307b5eSBram Moolenaar 417b3307b5eSBram Moolenaar " Set arguments to be run 418b3307b5eSBram Moolenaar if len(proc_args) 419b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 420b3307b5eSBram Moolenaar endif 421b3307b5eSBram Moolenaar 422b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 423b3307b5eSBram Moolenaar startinsert 424b3307b5eSBram Moolenaarendfunc 425b3307b5eSBram Moolenaar 426b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 42738baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 42838baa3e6SBram Moolenaar " There can be only one. 42938baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 43038baa3e6SBram Moolenaar 43145d5f26dSBram Moolenaar " Install debugger commands in the text window. 432b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 433e09ba7baSBram Moolenaar call s:InstallCommands() 43445d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 435e09ba7baSBram Moolenaar 43651b0f370SBram Moolenaar " Enable showing a balloon with eval info 437246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 438246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 43951b0f370SBram Moolenaar if has("balloon_eval") 44051b0f370SBram Moolenaar set ballooneval 441246fe03dSBram Moolenaar endif 44251b0f370SBram Moolenaar if has("balloon_eval_term") 44351b0f370SBram Moolenaar set balloonevalterm 44451b0f370SBram Moolenaar endif 44551b0f370SBram Moolenaar endif 44651b0f370SBram Moolenaar 4475378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 4485378e1cfSBram Moolenaar " breakpoint number. 44937402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 45037402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 45137402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 45237402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 45337402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 454e09ba7baSBram Moolenaar let s:breakpoints = {} 4551b9645deSBram Moolenaar 45637402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 45737402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 45837402ed5SBram Moolenaar let s:breakpoint_locations = {} 45937402ed5SBram Moolenaar 4601b9645deSBram Moolenaar augroup TermDebug 4611b9645deSBram Moolenaar au BufRead * call s:BufRead() 4621b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 463f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 4641b9645deSBram Moolenaar augroup END 46532c67ba7SBram Moolenaar 466b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 467b3307b5eSBram Moolenaar " window. 46832c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 46932c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 47032c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 47132c67ba7SBram Moolenaar endif 472c572da5fSBram Moolenaarendfunc 473c572da5fSBram Moolenaar 474b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 475b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 476b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 477b3307b5eSBram Moolenaar if s:way == 'prompt' 478b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 479b3307b5eSBram Moolenaar else 480b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 481b3307b5eSBram Moolenaar endif 482b3307b5eSBram Moolenaarendfunc 483b3307b5eSBram Moolenaar 484b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 485b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 486b3307b5eSBram Moolenaar if s:way == 'prompt' 487b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 488b3307b5eSBram Moolenaar else 489b3307b5eSBram Moolenaar let do_continue = 0 490b3307b5eSBram Moolenaar if !s:stopped 491b3307b5eSBram Moolenaar let do_continue = 1 492b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 493b3307b5eSBram Moolenaar sleep 10m 494b3307b5eSBram Moolenaar endif 495b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 496b3307b5eSBram Moolenaar if do_continue 497b3307b5eSBram Moolenaar Continue 498b3307b5eSBram Moolenaar endif 499b3307b5eSBram Moolenaar endif 500b3307b5eSBram Moolenaarendfunc 501b3307b5eSBram Moolenaar 502b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 503b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 504b3307b5eSBram Moolenaar call s:SendCommand(a:text) 505b3307b5eSBram Moolenaarendfunc 506b3307b5eSBram Moolenaar 5074551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 5084551c0a9SBram Moolenaar" breakpoint. 509b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 5102ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 5112ed890f1SBram Moolenaar if has('win32') 5122ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 5132ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 5144551c0a9SBram Moolenaar if s:pid == 0 5154551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 5164551c0a9SBram Moolenaar else 5174551c0a9SBram Moolenaar call debugbreak(s:pid) 5184551c0a9SBram Moolenaar endif 5192ed890f1SBram Moolenaar else 5202ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 5212ed890f1SBram Moolenaar endif 522b3307b5eSBram Moolenaarendfunc 523b3307b5eSBram Moolenaar 524b3307b5eSBram Moolenaar" Function called when gdb outputs text. 525b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 526b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 527b3307b5eSBram Moolenaar 528b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 529b3307b5eSBram Moolenaar " Drop status and echo'd commands. 530a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 531b3307b5eSBram Moolenaar return 532b3307b5eSBram Moolenaar endif 533b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 534b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 535b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 536b3307b5eSBram Moolenaar " Silently drop evaluation errors. 537b3307b5eSBram Moolenaar unlet s:evalexpr 538b3307b5eSBram Moolenaar return 539b3307b5eSBram Moolenaar endif 540b3307b5eSBram Moolenaar elseif a:text[0] == '~' 541b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 542b3307b5eSBram Moolenaar else 543b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 544b3307b5eSBram Moolenaar return 545b3307b5eSBram Moolenaar endif 546b3307b5eSBram Moolenaar 547b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 548b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 549b3307b5eSBram Moolenaar 550b3307b5eSBram Moolenaar " Add the output above the current prompt. 551b3307b5eSBram Moolenaar call append(line('$') - 1, text) 5524551c0a9SBram Moolenaar set modified 553b3307b5eSBram Moolenaar 554b3307b5eSBram Moolenaar call win_gotoid(curwinid) 555b3307b5eSBram Moolenaarendfunc 556b3307b5eSBram Moolenaar 557b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 558b3307b5eSBram Moolenaar" to the next ", unescaping characters. 559b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 560b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 561a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 562b3307b5eSBram Moolenaar return 563b3307b5eSBram Moolenaar endif 564b3307b5eSBram Moolenaar let result = '' 565b3307b5eSBram Moolenaar let i = 1 566b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 567b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 568b3307b5eSBram Moolenaar let i += 1 569b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 570b3307b5eSBram Moolenaar " drop \n 571b3307b5eSBram Moolenaar let i += 1 572b3307b5eSBram Moolenaar continue 573589edb34SBram Moolenaar elseif a:quotedText[i] == 't' 574589edb34SBram Moolenaar " append \t 575589edb34SBram Moolenaar let i += 1 576589edb34SBram Moolenaar let result .= "\t" 577589edb34SBram Moolenaar continue 578b3307b5eSBram Moolenaar endif 579b3307b5eSBram Moolenaar endif 580b3307b5eSBram Moolenaar let result .= a:quotedText[i] 581b3307b5eSBram Moolenaar let i += 1 582b3307b5eSBram Moolenaar endwhile 583b3307b5eSBram Moolenaar return result 584b3307b5eSBram Moolenaarendfunc 585b3307b5eSBram Moolenaar 586a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 587a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5885378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5895378e1cfSBram Moolenaar return '' 5905378e1cfSBram Moolenaar endif 591a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 592a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 593a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 594a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 595a15b0a93SBram Moolenaar endif 596a15b0a93SBram Moolenaar return name 597a15b0a93SBram Moolenaarendfunc 598a15b0a93SBram Moolenaar 59982be4849SBram Moolenaar" Extract the "addr" value from a gdb message with addr="0x0001234". 60082be4849SBram Moolenaarfunc s:GetAsmAddr(msg) 60182be4849SBram Moolenaar if a:msg !~ 'addr=' 60282be4849SBram Moolenaar return '' 60382be4849SBram Moolenaar endif 60482be4849SBram Moolenaar let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', '')) 60582be4849SBram Moolenaar return addr 60682be4849SBram Moolenaarendfunc 607*6aa57295SBram Moolenaar 608b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 609*6aa57295SBram Moolenaar if exists('#User#TermdebugStopPre') 610*6aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStopPre 611*6aa57295SBram Moolenaar endif 612*6aa57295SBram Moolenaar 613fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 614b3623a38SBram Moolenaar unlet s:gdbwin 615e09ba7baSBram Moolenaar 616b3307b5eSBram Moolenaar call s:EndDebugCommon() 617b3307b5eSBram Moolenaarendfunc 618b3307b5eSBram Moolenaar 619b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 620e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 621e09ba7baSBram Moolenaar 622b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 623b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 624b3307b5eSBram Moolenaar endif 625b3307b5eSBram Moolenaar 626cb80aa2dSBram Moolenaar " Restore 'signcolumn' in all buffers for which it was set. 627b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 628cb80aa2dSBram Moolenaar let was_buf = bufnr() 629cb80aa2dSBram Moolenaar for bufnr in s:signcolumn_buflist 630cb80aa2dSBram Moolenaar if bufexists(bufnr) 631cb80aa2dSBram Moolenaar exe bufnr .. "buf" 632cb80aa2dSBram Moolenaar if exists('b:save_signcolumn') 633cb80aa2dSBram Moolenaar let &signcolumn = b:save_signcolumn 634cb80aa2dSBram Moolenaar unlet b:save_signcolumn 635cb80aa2dSBram Moolenaar endif 636cb80aa2dSBram Moolenaar endif 637cb80aa2dSBram Moolenaar endfor 638cb80aa2dSBram Moolenaar exe was_buf .. "buf" 639cb80aa2dSBram Moolenaar 640e09ba7baSBram Moolenaar call s:DeleteCommands() 641e09ba7baSBram Moolenaar 642e09ba7baSBram Moolenaar call win_gotoid(curwinid) 643b3307b5eSBram Moolenaar 64438baa3e6SBram Moolenaar if s:save_columns > 0 64538baa3e6SBram Moolenaar let &columns = s:save_columns 64638baa3e6SBram Moolenaar endif 6471b9645deSBram Moolenaar 648246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 649246fe03dSBram Moolenaar set balloonexpr= 65051b0f370SBram Moolenaar if has("balloon_eval") 65151b0f370SBram Moolenaar set noballooneval 652246fe03dSBram Moolenaar endif 65351b0f370SBram Moolenaar if has("balloon_eval_term") 65451b0f370SBram Moolenaar set noballoonevalterm 65551b0f370SBram Moolenaar endif 65651b0f370SBram Moolenaar endif 65751b0f370SBram Moolenaar 658*6aa57295SBram Moolenaar if exists('#User#TermdebugStopPost') 659*6aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStopPost 660*6aa57295SBram Moolenaar endif 661*6aa57295SBram Moolenaar 6621b9645deSBram Moolenaar au! TermDebug 663fe386641SBram Moolenaarendfunc 664fe386641SBram Moolenaar 665b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 666*6aa57295SBram Moolenaar if exists('#User#TermdebugStopPre') 667*6aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStopPre 668*6aa57295SBram Moolenaar endif 669*6aa57295SBram Moolenaar 670b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 671b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 6724551c0a9SBram Moolenaar set nomodified 673b3307b5eSBram Moolenaar close 674b3307b5eSBram Moolenaar if curwinid != s:gdbwin 675b3307b5eSBram Moolenaar call win_gotoid(curwinid) 676b3307b5eSBram Moolenaar endif 677b3307b5eSBram Moolenaar 678b3307b5eSBram Moolenaar call s:EndDebugCommon() 679b3307b5eSBram Moolenaar unlet s:gdbwin 680b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 681b3307b5eSBram Moolenaarendfunc 682b3307b5eSBram Moolenaar 68382be4849SBram Moolenaar" Disassembly window - added by Michael Sartain 68482be4849SBram Moolenaar" 68582be4849SBram Moolenaar" - CommOutput: disassemble $pc 68682be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n" 68782be4849SBram Moolenaar" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n" 68882be4849SBram Moolenaar" - CommOutput: ~" 0x0000555556466f69 <+0>:\tpush rbp\n" 68982be4849SBram Moolenaar" ... 69082be4849SBram Moolenaar" - CommOutput: ~" 0x0000555556467cd0:\tpop rbp\n" 69182be4849SBram Moolenaar" - CommOutput: ~" 0x0000555556467cd1:\tret \n" 69282be4849SBram Moolenaar" - CommOutput: ~"End of assembler dump.\n" 69382be4849SBram Moolenaar" - CommOutput: ^done 69482be4849SBram Moolenaar 69582be4849SBram Moolenaar" - CommOutput: disassemble $pc 69682be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n" 69782be4849SBram Moolenaar" - CommOutput: &"No function contains specified address.\n" 69882be4849SBram Moolenaar" - CommOutput: ^error,msg="No function contains specified address." 69982be4849SBram Moolenaarfunc s:HandleDisasmMsg(msg) 70082be4849SBram Moolenaar if a:msg =~ '^\^done' 70182be4849SBram Moolenaar let curwinid = win_getid(winnr()) 70282be4849SBram Moolenaar if win_gotoid(s:asmwin) 70382be4849SBram Moolenaar silent normal! gg0"_dG 70482be4849SBram Moolenaar call setline(1, s:asm_lines) 70582be4849SBram Moolenaar set nomodified 70682be4849SBram Moolenaar set filetype=asm 70782be4849SBram Moolenaar 70882be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 70982be4849SBram Moolenaar if lnum != 0 71082be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 71182be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 71282be4849SBram Moolenaar endif 71382be4849SBram Moolenaar 71482be4849SBram Moolenaar call win_gotoid(curwinid) 71582be4849SBram Moolenaar endif 71682be4849SBram Moolenaar 71782be4849SBram Moolenaar let s:parsing_disasm_msg = 0 71882be4849SBram Moolenaar let s:asm_lines = [] 71982be4849SBram Moolenaar elseif a:msg =~ '^\^error,msg=' 72082be4849SBram Moolenaar if s:parsing_disasm_msg == 1 72182be4849SBram Moolenaar " Disassemble call ran into an error. This can happen when gdb can't 72282be4849SBram Moolenaar " find the function frame address, so let's try to disassemble starting 72382be4849SBram Moolenaar " at current PC 72482be4849SBram Moolenaar call s:SendCommand('disassemble $pc,+100') 72582be4849SBram Moolenaar endif 72682be4849SBram Moolenaar let s:parsing_disasm_msg = 0 72782be4849SBram Moolenaar elseif a:msg =~ '\&\"disassemble \$pc' 72882be4849SBram Moolenaar if a:msg =~ '+100' 72982be4849SBram Moolenaar " This is our second disasm attempt 73082be4849SBram Moolenaar let s:parsing_disasm_msg = 2 73182be4849SBram Moolenaar endif 73282be4849SBram Moolenaar else 73382be4849SBram Moolenaar let value = substitute(a:msg, '^\~\"[ ]*', '', '') 73482be4849SBram Moolenaar let value = substitute(value, '^=>[ ]*', '', '') 73582be4849SBram Moolenaar let value = substitute(value, '\\n\" 73682be4849SBram Moolenaar$', '', '') 73782be4849SBram Moolenaar let value = substitute(value, '\\n\"$', '', '') 73882be4849SBram Moolenaar let value = substitute(value, ' 73982be4849SBram Moolenaar', '', '') 74082be4849SBram Moolenaar let value = substitute(value, '\\t', ' ', 'g') 74182be4849SBram Moolenaar 74282be4849SBram Moolenaar if value != '' || !empty(s:asm_lines) 74382be4849SBram Moolenaar call add(s:asm_lines, value) 74482be4849SBram Moolenaar endif 74582be4849SBram Moolenaar endif 746fe386641SBram Moolenaarendfunc 747fe386641SBram Moolenaar 748fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 749fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 750fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 751fe386641SBram Moolenaar 752fe386641SBram Moolenaar for msg in msgs 753fe386641SBram Moolenaar " remove prefixed NL 754fe386641SBram Moolenaar if msg[0] == "\n" 75582be4849SBram Moolenaar let msg = msg[1:] 75682be4849SBram Moolenaar endif 75782be4849SBram Moolenaar 75882be4849SBram Moolenaar if s:parsing_disasm_msg 7591b9645deSBram Moolenaar call s:HandleDisasmMsg(msg) 760e09ba7baSBram Moolenaar elseif msg != '' 76145d5f26dSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 762e09ba7baSBram Moolenaar call s:HandleCursor(msg) 763e09ba7baSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 764e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 7654551c0a9SBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 7664551c0a9SBram Moolenaar call s:HandleBreakpointDelete(msg) 76745d5f26dSBram Moolenaar elseif msg =~ '^=thread-group-started' 76845d5f26dSBram Moolenaar call s:HandleProgramRun(msg) 76945d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 77045d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 77182be4849SBram Moolenaar elseif msg =~ '^\^error,msg=' 77282be4849SBram Moolenaar call s:HandleError(msg) 77382be4849SBram Moolenaar elseif msg =~ '^disassemble' 774e09ba7baSBram Moolenaar let s:parsing_disasm_msg = 1 775e09ba7baSBram Moolenaar let s:asm_lines = [] 776e09ba7baSBram Moolenaar endif 777e09ba7baSBram Moolenaar endif 778e09ba7baSBram Moolenaar endfor 779589edb34SBram Moolenaarendfunc 780589edb34SBram Moolenaar 781589edb34SBram Moolenaarfunc s:GotoProgram() 782589edb34SBram Moolenaar if has('win32') 783589edb34SBram Moolenaar if executable('powershell') 784589edb34SBram Moolenaar call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid)) 785469bdbdeSBram Moolenaar endif 786589edb34SBram Moolenaar else 787589edb34SBram Moolenaar call win_gotoid(s:ptywin) 788589edb34SBram Moolenaar endif 789e09ba7baSBram Moolenaarendfunc 790e09ba7baSBram Moolenaar 791963c1ad5SBram Moolenaar" Install commands in the current window to control the debugger. 792963c1ad5SBram Moolenaarfunc s:InstallCommands() 793963c1ad5SBram Moolenaar let save_cpo = &cpo 794589edb34SBram Moolenaar set cpo&vim 79571137fedSBram Moolenaar 796e09ba7baSBram Moolenaar command -nargs=? Break call s:SetBreakpoint(<q-args>) 79745d5f26dSBram Moolenaar command Clear call s:ClearBreakpoint() 798e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 79960e73f2aSBram Moolenaar command Over call s:SendCommand('-exec-next') 80060e73f2aSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 80160e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 802b3307b5eSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 803b3307b5eSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 804b3307b5eSBram Moolenaar 805b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 806b3307b5eSBram Moolenaar if s:way == 'prompt' 807b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 808b3307b5eSBram Moolenaar else 809b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 81045d5f26dSBram Moolenaar endif 81145d5f26dSBram Moolenaar 812589edb34SBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 813b3307b5eSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 81482be4849SBram Moolenaar command Program call s:GotoProgram() 81571137fedSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 81645d5f26dSBram Moolenaar command Asm call s:GotoAsmwinOrCreateIt() 817388a5d4fSBram Moolenaar command Winbar call s:InstallWinbar() 818388a5d4fSBram Moolenaar 81945d5f26dSBram Moolenaar if !exists('g:termdebug_map_K') || g:termdebug_map_K 820388a5d4fSBram Moolenaar let s:k_map_saved = maparg('K', 'n', 0, 1) 8211b9645deSBram Moolenaar nnoremap K :Evaluate<CR> 822f0b03c4eSBram Moolenaar endif 82371137fedSBram Moolenaar 82471137fedSBram Moolenaar if has('menu') && &mouse != '' 82571137fedSBram Moolenaar call s:InstallWinbar() 82671137fedSBram Moolenaar 82771137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 82871137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 82971137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 83071137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 83171137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 83271137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 83371137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 834963c1ad5SBram Moolenaar endif 835963c1ad5SBram Moolenaar endif 83671137fedSBram Moolenaar 83771137fedSBram Moolenaar let &cpo = save_cpo 83871137fedSBram Moolenaarendfunc 83971137fedSBram Moolenaar 84071137fedSBram Moolenaarlet s:winbar_winids = [] 84171137fedSBram Moolenaar 842c4b533e1SBram Moolenaar" Install the window toolbar in the current window. 84324a98a0eSBram Moolenaarfunc s:InstallWinbar() 84424a98a0eSBram Moolenaar if has('menu') && &mouse != '' 84524a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 84624a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 84760e73f2aSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 84824a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 84971137fedSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 850c4b533e1SBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 851e09ba7baSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 852e09ba7baSBram Moolenaar endif 853e09ba7baSBram Moolenaarendfunc 854e09ba7baSBram Moolenaar 855e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 85671137fedSBram Moolenaarfunc s:DeleteCommands() 857e09ba7baSBram Moolenaar delcommand Break 85845d5f26dSBram Moolenaar delcommand Clear 859e09ba7baSBram Moolenaar delcommand Step 86060e73f2aSBram Moolenaar delcommand Over 86160e73f2aSBram Moolenaar delcommand Finish 86260e73f2aSBram Moolenaar delcommand Run 863e09ba7baSBram Moolenaar delcommand Arguments 86445d5f26dSBram Moolenaar delcommand Stop 86545d5f26dSBram Moolenaar delcommand Continue 86645d5f26dSBram Moolenaar delcommand Evaluate 867b3623a38SBram Moolenaar delcommand Gdb 86882be4849SBram Moolenaar delcommand Program 86971137fedSBram Moolenaar delcommand Source 87045d5f26dSBram Moolenaar delcommand Asm 8711b884a00SBram Moolenaar delcommand Winbar 8721b884a00SBram Moolenaar 8731b884a00SBram Moolenaar if exists('s:k_map_saved') 8741b884a00SBram Moolenaar if empty(s:k_map_saved) 875388a5d4fSBram Moolenaar nunmap K 8761b884a00SBram Moolenaar else 877388a5d4fSBram Moolenaar call mapset('n', 0, s:k_map_saved) 878388a5d4fSBram Moolenaar endif 8791b9645deSBram Moolenaar unlet s:k_map_saved 8801b9645deSBram Moolenaar endif 88171137fedSBram Moolenaar 88271137fedSBram Moolenaar if has('menu') 88371137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 88471137fedSBram Moolenaar let curwinid = win_getid(winnr()) 8851b9645deSBram Moolenaar for winid in s:winbar_winids 8861b9645deSBram Moolenaar if win_gotoid(winid) 8871b9645deSBram Moolenaar aunmenu WinBar.Step 8881b9645deSBram Moolenaar aunmenu WinBar.Next 88960e73f2aSBram Moolenaar aunmenu WinBar.Finish 8901b9645deSBram Moolenaar aunmenu WinBar.Cont 8911b9645deSBram Moolenaar aunmenu WinBar.Stop 89271137fedSBram Moolenaar aunmenu WinBar.Eval 89371137fedSBram Moolenaar endif 89471137fedSBram Moolenaar endfor 89571137fedSBram Moolenaar call win_gotoid(curwinid) 89671137fedSBram Moolenaar let s:winbar_winids = [] 89771137fedSBram Moolenaar 89871137fedSBram Moolenaar if exists('s:saved_mousemodel') 89971137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 90071137fedSBram Moolenaar unlet s:saved_mousemodel 90171137fedSBram Moolenaar aunmenu PopUp.-SEP3- 90271137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 90371137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 90471137fedSBram Moolenaar aunmenu PopUp.Evaluate 9051b9645deSBram Moolenaar endif 90645d5f26dSBram Moolenaar endif 90737402ed5SBram Moolenaar 90837402ed5SBram Moolenaar exe 'sign unplace ' . s:pc_id 90937402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 91037402ed5SBram Moolenaar for subid in keys(entries) 91145d5f26dSBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 91245d5f26dSBram Moolenaar endfor 91337402ed5SBram Moolenaar endfor 914a15b0a93SBram Moolenaar unlet s:breakpoints 915a15b0a93SBram Moolenaar unlet s:breakpoint_locations 916a15b0a93SBram Moolenaar 917a15b0a93SBram Moolenaar sign undefine debugPC 918a15b0a93SBram Moolenaar for val in s:BreakpointSigns 9194551c0a9SBram Moolenaar exe "sign undefine debugBreakpoint" . val 920e09ba7baSBram Moolenaar endfor 921e09ba7baSBram Moolenaar let s:BreakpointSigns = [] 922e09ba7baSBram Moolenaarendfunc 923589edb34SBram Moolenaar 92460e73f2aSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 92560e73f2aSBram Moolenaarfunc s:SetBreakpoint(at) 92660e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 92760e73f2aSBram Moolenaar " Interrupt to make it work. 92860e73f2aSBram Moolenaar let do_continue = 0 929b3307b5eSBram Moolenaar if !s:stopped 9304551c0a9SBram Moolenaar let do_continue = 1 931b3307b5eSBram Moolenaar if s:way == 'prompt' 93260e73f2aSBram Moolenaar call s:PromptInterrupt() 933b3307b5eSBram Moolenaar else 93460e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 93560e73f2aSBram Moolenaar endif 936589edb34SBram Moolenaar sleep 10m 937a15b0a93SBram Moolenaar endif 938589edb34SBram Moolenaar 939589edb34SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 940589edb34SBram Moolenaar let at = empty(a:at) ? 94160e73f2aSBram Moolenaar \ fnameescape(expand('%:p')) . ':' . line('.') : a:at 94260e73f2aSBram Moolenaar call s:SendCommand('-break-insert ' . at) 94360e73f2aSBram Moolenaar if do_continue 944e09ba7baSBram Moolenaar call s:SendCommand('-exec-continue') 945e09ba7baSBram Moolenaar endif 94671137fedSBram Moolenaarendfunc 94771137fedSBram Moolenaar 948e09ba7baSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 949e09ba7baSBram Moolenaarfunc s:ClearBreakpoint() 95037402ed5SBram Moolenaar let fname = fnameescape(expand('%:p')) 95137402ed5SBram Moolenaar let lnum = line('.') 95237402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 95337402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 95437402ed5SBram Moolenaar let idx = 0 95537402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 95637402ed5SBram Moolenaar if has_key(s:breakpoints, id) 95737402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 95837402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 95937402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 96037402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 96137402ed5SBram Moolenaar endfor 962e09ba7baSBram Moolenaar unlet s:breakpoints[id] 96337402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 96437402ed5SBram Moolenaar break 965e09ba7baSBram Moolenaar else 966e09ba7baSBram Moolenaar let idx += 1 96737402ed5SBram Moolenaar endif 96837402ed5SBram Moolenaar endfor 96937402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 97037402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 971e09ba7baSBram Moolenaar endif 972e09ba7baSBram Moolenaar endif 97360e73f2aSBram Moolenaarendfunc 97460e73f2aSBram Moolenaar 97560e73f2aSBram Moolenaarfunc s:Run(args) 97660e73f2aSBram Moolenaar if a:args != '' 97760e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 97860e73f2aSBram Moolenaar endif 97960e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 98051b0f370SBram Moolenaarendfunc 98151b0f370SBram Moolenaar 98251b0f370SBram Moolenaarfunc s:SendEval(expr) 98351b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 98451b0f370SBram Moolenaar let s:evalexpr = a:expr 98545d5f26dSBram Moolenaarendfunc 98645d5f26dSBram Moolenaar 98745d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 98845d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 98945d5f26dSBram Moolenaar if a:arg != '' 99045d5f26dSBram Moolenaar let expr = a:arg 99145d5f26dSBram Moolenaar elseif a:range == 2 99245d5f26dSBram Moolenaar let pos = getcurpos() 99345d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 99445d5f26dSBram Moolenaar let regt = getregtype('v') 99545d5f26dSBram Moolenaar normal! gv"vy 99645d5f26dSBram Moolenaar let expr = @v 99745d5f26dSBram Moolenaar call setpos('.', pos) 99845d5f26dSBram Moolenaar call setreg('v', reg, regt) 99945d5f26dSBram Moolenaar else 100022f1d0e3SBram Moolenaar let expr = expand('<cexpr>') 100151b0f370SBram Moolenaar endif 100245d5f26dSBram Moolenaar let s:ignoreEvalError = 0 100345d5f26dSBram Moolenaar call s:SendEval(expr) 100422f1d0e3SBram Moolenaarendfunc 100551b0f370SBram Moolenaar 100651b0f370SBram Moolenaarlet s:ignoreEvalError = 0 100745d5f26dSBram Moolenaarlet s:evalFromBalloonExpr = 0 100845d5f26dSBram Moolenaar 10091b9645deSBram Moolenaar" Handle the result of data-evaluate-expression 10101b9645deSBram Moolenaarfunc s:HandleEvaluate(msg) 101151b0f370SBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 101251b0f370SBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 101351b0f370SBram Moolenaar if s:evalFromBalloonExpr 101451b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 101551b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 101651b0f370SBram Moolenaar else 101751b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 101851b0f370SBram Moolenaar endif 10191b9645deSBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 102051b0f370SBram Moolenaar else 10211b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 10227f2e9d7cSBram Moolenaar endif 10231b9645deSBram Moolenaar 102422f1d0e3SBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 102551b0f370SBram Moolenaar " Looks like a pointer, also display what it points to. 102651b0f370SBram Moolenaar let s:ignoreEvalError = 1 102751b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 10281b9645deSBram Moolenaar else 102945d5f26dSBram Moolenaar let s:evalFromBalloonExpr = 0 103045d5f26dSBram Moolenaar endif 103151b0f370SBram Moolenaarendfunc 103251b0f370SBram Moolenaar 103351b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 1034b3307b5eSBram Moolenaar" if there is any. 1035396e829fSBram Moolenaarfunc TermDebugBalloonExpr() 1036b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 1037b3307b5eSBram Moolenaar return '' 1038b3307b5eSBram Moolenaar endif 1039b3307b5eSBram Moolenaar if !s:stopped 1040396e829fSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 104151b0f370SBram Moolenaar " mouse triggers a balloon. 104251b0f370SBram Moolenaar return '' 104351b0f370SBram Moolenaar endif 104422f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 1 104522f1d0e3SBram Moolenaar let s:evalFromBalloonExprResult = '' 104651b0f370SBram Moolenaar let s:ignoreEvalError = 1 104751b0f370SBram Moolenaar call s:SendEval(v:beval_text) 104851b0f370SBram Moolenaar return '' 104945d5f26dSBram Moolenaarendfunc 105045d5f26dSBram Moolenaar 105122f1d0e3SBram Moolenaar" Handle an error. 105251b0f370SBram Moolenaarfunc s:HandleError(msg) 105322f1d0e3SBram Moolenaar if s:ignoreEvalError 105422f1d0e3SBram Moolenaar " Result of s:SendEval() failed, ignore. 105551b0f370SBram Moolenaar let s:ignoreEvalError = 0 105651b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 105745d5f26dSBram Moolenaar return 105845d5f26dSBram Moolenaar endif 105945d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 1060b3307b5eSBram Moolenaarendfunc 1061b3307b5eSBram Moolenaar 1062c4b533e1SBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 1063b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 1064c4b533e1SBram Moolenaar new 1065c4b533e1SBram Moolenaar let s:sourcewin = win_getid(winnr()) 1066c4b533e1SBram Moolenaar call s:InstallWinbar() 1067c4b533e1SBram Moolenaar endif 106882be4849SBram Moolenaarendfunc 106982be4849SBram Moolenaar 107082be4849SBram Moolenaarfunc s:GotoAsmwinOrCreateIt() 107182be4849SBram Moolenaar if !win_gotoid(s:asmwin) 107282be4849SBram Moolenaar if win_gotoid(s:sourcewin) 107382be4849SBram Moolenaar exe 'rightbelow new' 107482be4849SBram Moolenaar else 107582be4849SBram Moolenaar exe 'new' 107682be4849SBram Moolenaar endif 107782be4849SBram Moolenaar 107882be4849SBram Moolenaar let s:asmwin = win_getid(winnr()) 107982be4849SBram Moolenaar 108082be4849SBram Moolenaar setlocal nowrap 108182be4849SBram Moolenaar setlocal number 108282be4849SBram Moolenaar setlocal noswapfile 108382be4849SBram Moolenaar setlocal buftype=nofile 108482be4849SBram Moolenaar 108582be4849SBram Moolenaar let asmbuf = bufnr('Termdebug-asm-listing') 108682be4849SBram Moolenaar if asmbuf > 0 108782be4849SBram Moolenaar exe 'buffer' . asmbuf 108882be4849SBram Moolenaar else 108982be4849SBram Moolenaar exe 'file Termdebug-asm-listing' 109082be4849SBram Moolenaar endif 109182be4849SBram Moolenaar 109282be4849SBram Moolenaar if exists('g:termdebug_disasm_window') 109382be4849SBram Moolenaar if g:termdebug_disasm_window > 1 109482be4849SBram Moolenaar exe 'resize ' . g:termdebug_disasm_window 109582be4849SBram Moolenaar endif 109682be4849SBram Moolenaar endif 109782be4849SBram Moolenaar endif 109882be4849SBram Moolenaar 109982be4849SBram Moolenaar if s:asm_addr != '' 110082be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 110182be4849SBram Moolenaar if lnum == 0 110282be4849SBram Moolenaar if s:stopped 110382be4849SBram Moolenaar call s:SendCommand('disassemble $pc') 110482be4849SBram Moolenaar endif 110582be4849SBram Moolenaar else 110682be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 110782be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 110882be4849SBram Moolenaar endif 110982be4849SBram Moolenaar endif 1110e09ba7baSBram Moolenaarendfunc 1111e09ba7baSBram Moolenaar 1112e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 1113fe386641SBram Moolenaar" Will update the sign that shows the current position. 1114fe386641SBram Moolenaarfunc s:HandleCursor(msg) 111560e73f2aSBram Moolenaar let wid = win_getid(winnr()) 11164551c0a9SBram Moolenaar 111760e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 111860e73f2aSBram Moolenaar call ch_log('program stopped') 11194551c0a9SBram Moolenaar let s:stopped = 1 112060e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 112160e73f2aSBram Moolenaar call ch_log('program running') 112260e73f2aSBram Moolenaar let s:stopped = 0 1123a15b0a93SBram Moolenaar endif 1124a15b0a93SBram Moolenaar 1125a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 1126a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 1127a15b0a93SBram Moolenaar else 112882be4849SBram Moolenaar let fname = '' 112982be4849SBram Moolenaar endif 113082be4849SBram Moolenaar 113182be4849SBram Moolenaar if a:msg =~ 'addr=' 113282be4849SBram Moolenaar let asm_addr = s:GetAsmAddr(a:msg) 113382be4849SBram Moolenaar if asm_addr != '' 113482be4849SBram Moolenaar let s:asm_addr = asm_addr 113582be4849SBram Moolenaar 113682be4849SBram Moolenaar let curwinid = win_getid(winnr()) 113782be4849SBram Moolenaar if win_gotoid(s:asmwin) 113882be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 113982be4849SBram Moolenaar if lnum == 0 114082be4849SBram Moolenaar call s:SendCommand('disassemble $pc') 114182be4849SBram Moolenaar else 114282be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 114382be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 114482be4849SBram Moolenaar endif 114582be4849SBram Moolenaar 114682be4849SBram Moolenaar call win_gotoid(curwinid) 114782be4849SBram Moolenaar endif 114882be4849SBram Moolenaar endif 11491b9645deSBram Moolenaar endif 1150e09ba7baSBram Moolenaar 1151fe386641SBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 11524551c0a9SBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 11531b9645deSBram Moolenaar if lnum =~ '^[0-9]*$' 1154fe386641SBram Moolenaar call s:GotoSourcewinOrCreateIt() 1155fe386641SBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 1156fe386641SBram Moolenaar if &modified 1157b3307b5eSBram Moolenaar " TODO: find existing window 1158c4b533e1SBram Moolenaar exe 'split ' . fnameescape(fname) 1159fe386641SBram Moolenaar let s:sourcewin = win_getid(winnr()) 1160fe386641SBram Moolenaar call s:InstallWinbar() 1161fe386641SBram Moolenaar else 1162fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 1163fe386641SBram Moolenaar endif 116401164a65SBram Moolenaar endif 116539f7aa3cSBram Moolenaar exe lnum 1166cb80aa2dSBram Moolenaar exe 'sign unplace ' . s:pc_id 1167cb80aa2dSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname 1168cb80aa2dSBram Moolenaar if !exists('b:save_signcolumn') 1169cb80aa2dSBram Moolenaar let b:save_signcolumn = &signcolumn 1170fe386641SBram Moolenaar call add(s:signcolumn_buflist, bufnr()) 1171fe386641SBram Moolenaar endif 11724551c0a9SBram Moolenaar setlocal signcolumn=yes 1173fe386641SBram Moolenaar endif 1174fe386641SBram Moolenaar elseif !s:stopped || fname != '' 1175fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 1176fe386641SBram Moolenaar endif 1177e09ba7baSBram Moolenaar 1178e09ba7baSBram Moolenaar call win_gotoid(wid) 1179de1a8314SBram Moolenaarendfunc 1180a15b0a93SBram Moolenaar 118137402ed5SBram Moolenaarlet s:BreakpointSigns = [] 118237402ed5SBram Moolenaar 118337402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 118437402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 118537402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 1186de1a8314SBram Moolenaar call add(s:BreakpointSigns, nr) 1187de1a8314SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 1188de1a8314SBram Moolenaar endif 118937402ed5SBram Moolenaarendfunc 119037402ed5SBram Moolenaar 11915378e1cfSBram Moolenaarfunc! s:SplitMsg(s) 11925378e1cfSBram Moolenaar return split(a:s, '{.\{-}}\zs') 1193e09ba7baSBram Moolenaarendfunction 1194e09ba7baSBram Moolenaar 1195e09ba7baSBram Moolenaar" Handle setting a breakpoint 11966dccc962SBram Moolenaar" Will update the sign that shows the breakpoint 11976dccc962SBram Moolenaarfunc s:HandleNewBreakpoint(msg) 11986dccc962SBram Moolenaar if a:msg !~ 'fullname=' 11996dccc962SBram Moolenaar " a watch does not have a file name 12005378e1cfSBram Moolenaar return 12015378e1cfSBram Moolenaar endif 12025378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 12035378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 12045378e1cfSBram Moolenaar if empty(fname) 12055378e1cfSBram Moolenaar continue 12065378e1cfSBram Moolenaar endif 1207e09ba7baSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 1208fe386641SBram Moolenaar if empty(nr) 1209e09ba7baSBram Moolenaar return 121037402ed5SBram Moolenaar endif 121137402ed5SBram Moolenaar 121237402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 121337402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 121437402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 121537402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 121637402ed5SBram Moolenaar 121737402ed5SBram Moolenaar if has_key(s:breakpoints, id) 121837402ed5SBram Moolenaar let entries = s:breakpoints[id] 121937402ed5SBram Moolenaar else 122037402ed5SBram Moolenaar let entries = {} 122137402ed5SBram Moolenaar let s:breakpoints[id] = entries 122237402ed5SBram Moolenaar endif 1223e09ba7baSBram Moolenaar if has_key(entries, subid) 1224e09ba7baSBram Moolenaar let entry = entries[subid] 122537402ed5SBram Moolenaar else 1226fe386641SBram Moolenaar let entry = {} 1227e09ba7baSBram Moolenaar let entries[subid] = entry 12285378e1cfSBram Moolenaar endif 1229e09ba7baSBram Moolenaar 1230e09ba7baSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 12311b9645deSBram Moolenaar let entry['fname'] = fname 123237402ed5SBram Moolenaar let entry['lnum'] = lnum 123337402ed5SBram Moolenaar 123437402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 123537402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 123637402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 123737402ed5SBram Moolenaar endif 12381b9645deSBram Moolenaar let s:breakpoint_locations[bploc] += [id] 123937402ed5SBram Moolenaar 12401b9645deSBram Moolenaar if bufloaded(fname) 12415378e1cfSBram Moolenaar call s:PlaceSign(id, subid, entry) 12421b9645deSBram Moolenaar endif 12431b9645deSBram Moolenaar endfor 124437402ed5SBram Moolenaarendfunc 124537402ed5SBram Moolenaar 12463132cdddSBram Moolenaarfunc s:PlaceSign(id, subid, entry) 12471b9645deSBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 1248e09ba7baSBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' priority=110 file=' . a:entry['fname'] 1249e09ba7baSBram Moolenaar let a:entry['placed'] = 1 1250e09ba7baSBram Moolenaarendfunc 1251e09ba7baSBram Moolenaar 1252e09ba7baSBram Moolenaar" Handle deleting a breakpoint 125337402ed5SBram Moolenaar" Will remove the sign that shows the breakpoint 125437402ed5SBram Moolenaarfunc s:HandleBreakpointDelete(msg) 1255e09ba7baSBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 1256e09ba7baSBram Moolenaar if empty(id) 125737402ed5SBram Moolenaar return 125837402ed5SBram Moolenaar endif 12591b9645deSBram Moolenaar if has_key(s:breakpoints, id) 126037402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 12611b9645deSBram Moolenaar if has_key(entry, 'placed') 12621b9645deSBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 12635378e1cfSBram Moolenaar unlet entry['placed'] 126437402ed5SBram Moolenaar endif 126537402ed5SBram Moolenaar endfor 1266c572da5fSBram Moolenaar unlet s:breakpoints[id] 12671b9645deSBram Moolenaar endif 12684551c0a9SBram Moolenaarendfunc 12694551c0a9SBram Moolenaar 12704551c0a9SBram Moolenaar" Handle the debugged program starting to run. 12714551c0a9SBram Moolenaar" Will store the process ID in s:pid 12724551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 12734551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 12744551c0a9SBram Moolenaar if nr == 0 12754551c0a9SBram Moolenaar return 12764551c0a9SBram Moolenaar endif 12774551c0a9SBram Moolenaar let s:pid = nr 12784551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 12791b9645deSBram Moolenaarendfunc 12801b9645deSBram Moolenaar 12811b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 128237402ed5SBram Moolenaarfunc s:BufRead() 128337402ed5SBram Moolenaar let fname = expand('<afile>:p') 12841b9645deSBram Moolenaar for [id, entries] in items(s:breakpoints) 128537402ed5SBram Moolenaar for [subid, entry] in items(entries) 12861b9645deSBram Moolenaar if entry['fname'] == fname 12871b9645deSBram Moolenaar call s:PlaceSign(id, subid, entry) 128837402ed5SBram Moolenaar endif 12891b9645deSBram Moolenaar endfor 12901b9645deSBram Moolenaar endfor 12911b9645deSBram Moolenaarendfunc 12921b9645deSBram Moolenaar 12931b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 129437402ed5SBram Moolenaarfunc s:BufUnloaded() 129537402ed5SBram Moolenaar let fname = expand('<afile>:p') 12961b9645deSBram Moolenaar for [id, entries] in items(s:breakpoints) 12971b9645deSBram Moolenaar for [subid, entry] in items(entries) 12981b9645deSBram Moolenaar if entry['fname'] == fname 12991b9645deSBram Moolenaar let entry['placed'] = 0 130037402ed5SBram Moolenaar endif 13011b9645deSBram Moolenaar endfor 1302ca4cc018SBram Moolenaar endfor 1303ca4cc018SBram Moolenaarendfunc 1304ca4cc018SBram Moolenaar 1305let &cpo = s:keepcpo 1306unlet s:keepcpo 1307