1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3b3307b5eSBram Moolenaar" Author: Bram Moolenaar 4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license" 5*519cc559SBram Moolenaar" Last Change: 2021 Nov 14 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 1246aa57295SBram Moolenaar if exists('#User#TermdebugStartPre') 1256aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStartPre 1266aa57295SBram Moolenaar endif 1276aa57295SBram 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 1746aa57295SBram Moolenaar 1756aa57295SBram Moolenaar if exists('#User#TermdebugStartPost') 1766aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStartPost 1776aa57295SBram 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')}) 33089a9c159SBram Moolenaar 33189a9c159SBram Moolenaar " Set the filetype, this can be used to add mappings. 33289a9c159SBram Moolenaar set filetype=termdebug 33389a9c159SBram Moolenaar 334b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 335b3307b5eSBram Moolenaarendfunc 336b3307b5eSBram Moolenaar 337b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 338b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 339b3307b5eSBram Moolenaar if s:vertical 340b3307b5eSBram Moolenaar vertical new 341b3307b5eSBram Moolenaar else 342b3307b5eSBram Moolenaar new 343b3307b5eSBram Moolenaar endif 344b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 345b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 346b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 347b3307b5eSBram Moolenaar set buftype=prompt 348b3307b5eSBram Moolenaar file gdb 349b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 350b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 351b3307b5eSBram Moolenaar 352b3307b5eSBram Moolenaar if s:vertical 353b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 354b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 355b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 356b3307b5eSBram Moolenaar endif 357b3307b5eSBram Moolenaar 358b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 359b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 360b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 361b3307b5eSBram Moolenaar 362b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 363b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 364b3307b5eSBram Moolenaar 365b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 366b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 367b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 368b3307b5eSBram Moolenaar \ }) 369b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 370b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 371b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 372b3307b5eSBram Moolenaar return 373b3307b5eSBram Moolenaar endif 3744551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 3754551c0a9SBram Moolenaar set modified 376b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 377b3307b5eSBram Moolenaar 37891359014SBram Moolenaar " Interpret commands while the target is running. This should usually only 379b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 380b3307b5eSBram Moolenaar " target is running. 381b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 382b3307b5eSBram Moolenaar " Older gdb uses a different command. 383b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 384b3307b5eSBram Moolenaar 385b3307b5eSBram Moolenaar let s:ptybuf = 0 386b3307b5eSBram Moolenaar if has('win32') 387b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 388b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 389b3307b5eSBram Moolenaar elseif has('terminal') 390b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 391b3307b5eSBram Moolenaar " gdb window. 392b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 393b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 394b3307b5eSBram Moolenaar \ }) 395b3307b5eSBram Moolenaar if s:ptybuf == 0 396b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 397b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 398b3307b5eSBram Moolenaar return 399b3307b5eSBram Moolenaar endif 400b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 401b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 402b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 403b3307b5eSBram Moolenaar 404b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 405b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 406b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 407b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 408b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 409b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 410b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 411b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 412b3307b5eSBram Moolenaar else 413b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 414b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 415b3307b5eSBram Moolenaar endif 416b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 417b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 418b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 419b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 420b3307b5eSBram Moolenaar 421b3307b5eSBram Moolenaar " Set arguments to be run 422b3307b5eSBram Moolenaar if len(proc_args) 423b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 424b3307b5eSBram Moolenaar endif 425b3307b5eSBram Moolenaar 426b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 427b3307b5eSBram Moolenaar startinsert 428b3307b5eSBram Moolenaarendfunc 429b3307b5eSBram Moolenaar 430b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 43138baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 43238baa3e6SBram Moolenaar " There can be only one. 43338baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 43438baa3e6SBram Moolenaar 43545d5f26dSBram Moolenaar " Install debugger commands in the text window. 436b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 437e09ba7baSBram Moolenaar call s:InstallCommands() 43845d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 439e09ba7baSBram Moolenaar 44051b0f370SBram Moolenaar " Enable showing a balloon with eval info 441246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 442246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 44351b0f370SBram Moolenaar if has("balloon_eval") 44451b0f370SBram Moolenaar set ballooneval 445246fe03dSBram Moolenaar endif 44651b0f370SBram Moolenaar if has("balloon_eval_term") 44751b0f370SBram Moolenaar set balloonevalterm 44851b0f370SBram Moolenaar endif 44951b0f370SBram Moolenaar endif 45051b0f370SBram Moolenaar 4515378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 4525378e1cfSBram Moolenaar " breakpoint number. 45337402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 45437402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 45537402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 45637402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 45737402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 458e09ba7baSBram Moolenaar let s:breakpoints = {} 4591b9645deSBram Moolenaar 46037402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 46137402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 46237402ed5SBram Moolenaar let s:breakpoint_locations = {} 46337402ed5SBram Moolenaar 4641b9645deSBram Moolenaar augroup TermDebug 4651b9645deSBram Moolenaar au BufRead * call s:BufRead() 4661b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 467f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 4681b9645deSBram Moolenaar augroup END 46932c67ba7SBram Moolenaar 470b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 471b3307b5eSBram Moolenaar " window. 47232c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 47332c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 47432c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 47532c67ba7SBram Moolenaar endif 476c572da5fSBram Moolenaarendfunc 477c572da5fSBram Moolenaar 478b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 479b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 480b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 481b3307b5eSBram Moolenaar if s:way == 'prompt' 482b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 483b3307b5eSBram Moolenaar else 484b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 485b3307b5eSBram Moolenaar endif 486b3307b5eSBram Moolenaarendfunc 487b3307b5eSBram Moolenaar 488b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 489b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 490b3307b5eSBram Moolenaar if s:way == 'prompt' 491b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 492b3307b5eSBram Moolenaar else 493b3307b5eSBram Moolenaar let do_continue = 0 494b3307b5eSBram Moolenaar if !s:stopped 495b3307b5eSBram Moolenaar let do_continue = 1 496b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 497b3307b5eSBram Moolenaar sleep 10m 498b3307b5eSBram Moolenaar endif 499b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 500b3307b5eSBram Moolenaar if do_continue 501b3307b5eSBram Moolenaar Continue 502b3307b5eSBram Moolenaar endif 503b3307b5eSBram Moolenaar endif 504b3307b5eSBram Moolenaarendfunc 505b3307b5eSBram Moolenaar 506b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 507b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 508b3307b5eSBram Moolenaar call s:SendCommand(a:text) 509b3307b5eSBram Moolenaarendfunc 510b3307b5eSBram Moolenaar 5114551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 5124551c0a9SBram Moolenaar" breakpoint. 513b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 5142ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 5152ed890f1SBram Moolenaar if has('win32') 5162ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 5172ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 5184551c0a9SBram Moolenaar if s:pid == 0 5194551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 5204551c0a9SBram Moolenaar else 5214551c0a9SBram Moolenaar call debugbreak(s:pid) 5224551c0a9SBram Moolenaar endif 5232ed890f1SBram Moolenaar else 5242ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 5252ed890f1SBram Moolenaar endif 526b3307b5eSBram Moolenaarendfunc 527b3307b5eSBram Moolenaar 528b3307b5eSBram Moolenaar" Function called when gdb outputs text. 529b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 530b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 531b3307b5eSBram Moolenaar 532b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 533b3307b5eSBram Moolenaar " Drop status and echo'd commands. 534a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 535b3307b5eSBram Moolenaar return 536b3307b5eSBram Moolenaar endif 537113cb513SBram Moolenaar if a:text =~ '^\^error,msg=' 538b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 539b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 540b3307b5eSBram Moolenaar " Silently drop evaluation errors. 541b3307b5eSBram Moolenaar unlet s:evalexpr 542b3307b5eSBram Moolenaar return 543b3307b5eSBram Moolenaar endif 544b3307b5eSBram Moolenaar elseif a:text[0] == '~' 545b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 546b3307b5eSBram Moolenaar else 547b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 548b3307b5eSBram Moolenaar return 549b3307b5eSBram Moolenaar endif 550b3307b5eSBram Moolenaar 551b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 552b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 553b3307b5eSBram Moolenaar 554b3307b5eSBram Moolenaar " Add the output above the current prompt. 555b3307b5eSBram Moolenaar call append(line('$') - 1, text) 5564551c0a9SBram Moolenaar set modified 557b3307b5eSBram Moolenaar 558b3307b5eSBram Moolenaar call win_gotoid(curwinid) 559b3307b5eSBram Moolenaarendfunc 560b3307b5eSBram Moolenaar 561b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 562b3307b5eSBram Moolenaar" to the next ", unescaping characters. 563b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 564b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 565a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 566b3307b5eSBram Moolenaar return 567b3307b5eSBram Moolenaar endif 568b3307b5eSBram Moolenaar let result = '' 569b3307b5eSBram Moolenaar let i = 1 570b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 571b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 572b3307b5eSBram Moolenaar let i += 1 573b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 574b3307b5eSBram Moolenaar " drop \n 575b3307b5eSBram Moolenaar let i += 1 576b3307b5eSBram Moolenaar continue 577589edb34SBram Moolenaar elseif a:quotedText[i] == 't' 578589edb34SBram Moolenaar " append \t 579589edb34SBram Moolenaar let i += 1 580589edb34SBram Moolenaar let result .= "\t" 581589edb34SBram Moolenaar continue 582b3307b5eSBram Moolenaar endif 583b3307b5eSBram Moolenaar endif 584b3307b5eSBram Moolenaar let result .= a:quotedText[i] 585b3307b5eSBram Moolenaar let i += 1 586b3307b5eSBram Moolenaar endwhile 587b3307b5eSBram Moolenaar return result 588b3307b5eSBram Moolenaarendfunc 589b3307b5eSBram Moolenaar 590a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 591a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5925378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5935378e1cfSBram Moolenaar return '' 5945378e1cfSBram Moolenaar endif 595a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 596a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 597a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 598a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 599a15b0a93SBram Moolenaar endif 600a15b0a93SBram Moolenaar return name 601a15b0a93SBram Moolenaarendfunc 602a15b0a93SBram Moolenaar 60382be4849SBram Moolenaar" Extract the "addr" value from a gdb message with addr="0x0001234". 60482be4849SBram Moolenaarfunc s:GetAsmAddr(msg) 60582be4849SBram Moolenaar if a:msg !~ 'addr=' 60682be4849SBram Moolenaar return '' 60782be4849SBram Moolenaar endif 60882be4849SBram Moolenaar let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', '')) 60982be4849SBram Moolenaar return addr 61082be4849SBram Moolenaarendfunc 6116aa57295SBram Moolenaar 612b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 6136aa57295SBram Moolenaar if exists('#User#TermdebugStopPre') 6146aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStopPre 6156aa57295SBram Moolenaar endif 6166aa57295SBram Moolenaar 617fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 618b3623a38SBram Moolenaar unlet s:gdbwin 619e09ba7baSBram Moolenaar 620b3307b5eSBram Moolenaar call s:EndDebugCommon() 621b3307b5eSBram Moolenaarendfunc 622b3307b5eSBram Moolenaar 623b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 624e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 625e09ba7baSBram Moolenaar 626b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 627b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 628b3307b5eSBram Moolenaar endif 629b3307b5eSBram Moolenaar 630cb80aa2dSBram Moolenaar " Restore 'signcolumn' in all buffers for which it was set. 631b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 632cb80aa2dSBram Moolenaar let was_buf = bufnr() 633cb80aa2dSBram Moolenaar for bufnr in s:signcolumn_buflist 634cb80aa2dSBram Moolenaar if bufexists(bufnr) 635cb80aa2dSBram Moolenaar exe bufnr .. "buf" 636cb80aa2dSBram Moolenaar if exists('b:save_signcolumn') 637cb80aa2dSBram Moolenaar let &signcolumn = b:save_signcolumn 638cb80aa2dSBram Moolenaar unlet b:save_signcolumn 639cb80aa2dSBram Moolenaar endif 640cb80aa2dSBram Moolenaar endif 641cb80aa2dSBram Moolenaar endfor 642cb80aa2dSBram Moolenaar exe was_buf .. "buf" 643cb80aa2dSBram Moolenaar 644e09ba7baSBram Moolenaar call s:DeleteCommands() 645e09ba7baSBram Moolenaar 646e09ba7baSBram Moolenaar call win_gotoid(curwinid) 647b3307b5eSBram Moolenaar 64838baa3e6SBram Moolenaar if s:save_columns > 0 64938baa3e6SBram Moolenaar let &columns = s:save_columns 65038baa3e6SBram Moolenaar endif 6511b9645deSBram Moolenaar 652246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 653246fe03dSBram Moolenaar set balloonexpr= 65451b0f370SBram Moolenaar if has("balloon_eval") 65551b0f370SBram Moolenaar set noballooneval 656246fe03dSBram Moolenaar endif 65751b0f370SBram Moolenaar if has("balloon_eval_term") 65851b0f370SBram Moolenaar set noballoonevalterm 65951b0f370SBram Moolenaar endif 66051b0f370SBram Moolenaar endif 66151b0f370SBram Moolenaar 6626aa57295SBram Moolenaar if exists('#User#TermdebugStopPost') 6636aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStopPost 6646aa57295SBram Moolenaar endif 6656aa57295SBram Moolenaar 6661b9645deSBram Moolenaar au! TermDebug 667fe386641SBram Moolenaarendfunc 668fe386641SBram Moolenaar 669b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 6706aa57295SBram Moolenaar if exists('#User#TermdebugStopPre') 6716aa57295SBram Moolenaar doauto <nomodeline> User TermdebugStopPre 6726aa57295SBram Moolenaar endif 6736aa57295SBram Moolenaar 674b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 675b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 6764551c0a9SBram Moolenaar set nomodified 677b3307b5eSBram Moolenaar close 678b3307b5eSBram Moolenaar if curwinid != s:gdbwin 679b3307b5eSBram Moolenaar call win_gotoid(curwinid) 680b3307b5eSBram Moolenaar endif 681b3307b5eSBram Moolenaar 682b3307b5eSBram Moolenaar call s:EndDebugCommon() 683b3307b5eSBram Moolenaar unlet s:gdbwin 684b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 685b3307b5eSBram Moolenaarendfunc 686b3307b5eSBram Moolenaar 68782be4849SBram Moolenaar" Disassembly window - added by Michael Sartain 68882be4849SBram Moolenaar" 68982be4849SBram Moolenaar" - CommOutput: disassemble $pc 69082be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n" 69182be4849SBram Moolenaar" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n" 69282be4849SBram Moolenaar" - CommOutput: ~" 0x0000555556466f69 <+0>:\tpush rbp\n" 69382be4849SBram Moolenaar" ... 69482be4849SBram Moolenaar" - CommOutput: ~" 0x0000555556467cd0:\tpop rbp\n" 69582be4849SBram Moolenaar" - CommOutput: ~" 0x0000555556467cd1:\tret \n" 69682be4849SBram Moolenaar" - CommOutput: ~"End of assembler dump.\n" 69782be4849SBram Moolenaar" - CommOutput: ^done 69882be4849SBram Moolenaar 69982be4849SBram Moolenaar" - CommOutput: disassemble $pc 70082be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n" 70182be4849SBram Moolenaar" - CommOutput: &"No function contains specified address.\n" 70282be4849SBram Moolenaar" - CommOutput: ^error,msg="No function contains specified address." 70382be4849SBram Moolenaarfunc s:HandleDisasmMsg(msg) 70482be4849SBram Moolenaar if a:msg =~ '^\^done' 70582be4849SBram Moolenaar let curwinid = win_getid(winnr()) 70682be4849SBram Moolenaar if win_gotoid(s:asmwin) 70782be4849SBram Moolenaar silent normal! gg0"_dG 70882be4849SBram Moolenaar call setline(1, s:asm_lines) 70982be4849SBram Moolenaar set nomodified 71082be4849SBram Moolenaar set filetype=asm 71182be4849SBram Moolenaar 71282be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 71382be4849SBram Moolenaar if lnum != 0 71482be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 71582be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 71682be4849SBram Moolenaar endif 71782be4849SBram Moolenaar 71882be4849SBram Moolenaar call win_gotoid(curwinid) 71982be4849SBram Moolenaar endif 72082be4849SBram Moolenaar 72182be4849SBram Moolenaar let s:parsing_disasm_msg = 0 72282be4849SBram Moolenaar let s:asm_lines = [] 72382be4849SBram Moolenaar elseif a:msg =~ '^\^error,msg=' 72482be4849SBram Moolenaar if s:parsing_disasm_msg == 1 72582be4849SBram Moolenaar " Disassemble call ran into an error. This can happen when gdb can't 72682be4849SBram Moolenaar " find the function frame address, so let's try to disassemble starting 72782be4849SBram Moolenaar " at current PC 72882be4849SBram Moolenaar call s:SendCommand('disassemble $pc,+100') 72982be4849SBram Moolenaar endif 73082be4849SBram Moolenaar let s:parsing_disasm_msg = 0 73182be4849SBram Moolenaar elseif a:msg =~ '\&\"disassemble \$pc' 73282be4849SBram Moolenaar if a:msg =~ '+100' 73382be4849SBram Moolenaar " This is our second disasm attempt 73482be4849SBram Moolenaar let s:parsing_disasm_msg = 2 73582be4849SBram Moolenaar endif 73682be4849SBram Moolenaar else 73782be4849SBram Moolenaar let value = substitute(a:msg, '^\~\"[ ]*', '', '') 73882be4849SBram Moolenaar let value = substitute(value, '^=>[ ]*', '', '') 739113cb513SBram Moolenaar let value = substitute(value, '\\n\"\r$', '', '') 74082be4849SBram Moolenaar let value = substitute(value, '\\n\"$', '', '') 741113cb513SBram Moolenaar let value = substitute(value, '\r', '', '') 74282be4849SBram Moolenaar let value = substitute(value, '\\t', ' ', 'g') 74382be4849SBram Moolenaar 74482be4849SBram Moolenaar if value != '' || !empty(s:asm_lines) 74582be4849SBram Moolenaar call add(s:asm_lines, value) 74682be4849SBram Moolenaar endif 74782be4849SBram Moolenaar endif 74882be4849SBram Moolenaarendfunc 74982be4849SBram Moolenaar 750fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 751fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 752fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 753fe386641SBram Moolenaar 754fe386641SBram Moolenaar for msg in msgs 755fe386641SBram Moolenaar " remove prefixed NL 756fe386641SBram Moolenaar if msg[0] == "\n" 757fe386641SBram Moolenaar let msg = msg[1:] 758fe386641SBram Moolenaar endif 75982be4849SBram Moolenaar 76082be4849SBram Moolenaar if s:parsing_disasm_msg 76182be4849SBram Moolenaar call s:HandleDisasmMsg(msg) 76282be4849SBram Moolenaar elseif msg != '' 7631b9645deSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 764e09ba7baSBram Moolenaar call s:HandleCursor(msg) 76545d5f26dSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 766e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 767e09ba7baSBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 768e09ba7baSBram Moolenaar call s:HandleBreakpointDelete(msg) 7694551c0a9SBram Moolenaar elseif msg =~ '^=thread-group-started' 7704551c0a9SBram Moolenaar call s:HandleProgramRun(msg) 77145d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 77245d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 77345d5f26dSBram Moolenaar elseif msg =~ '^\^error,msg=' 77445d5f26dSBram Moolenaar call s:HandleError(msg) 77582be4849SBram Moolenaar elseif msg =~ '^disassemble' 77682be4849SBram Moolenaar let s:parsing_disasm_msg = 1 77782be4849SBram Moolenaar let s:asm_lines = [] 778e09ba7baSBram Moolenaar endif 779e09ba7baSBram Moolenaar endif 780e09ba7baSBram Moolenaar endfor 781e09ba7baSBram Moolenaarendfunc 782e09ba7baSBram Moolenaar 783589edb34SBram Moolenaarfunc s:GotoProgram() 784589edb34SBram Moolenaar if has('win32') 785589edb34SBram Moolenaar if executable('powershell') 786589edb34SBram Moolenaar call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid)) 787589edb34SBram Moolenaar endif 788589edb34SBram Moolenaar else 789469bdbdeSBram Moolenaar call win_gotoid(s:ptywin) 790589edb34SBram Moolenaar endif 791589edb34SBram Moolenaarendfunc 792589edb34SBram Moolenaar 793e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger. 794e09ba7baSBram Moolenaarfunc s:InstallCommands() 795963c1ad5SBram Moolenaar let save_cpo = &cpo 796963c1ad5SBram Moolenaar set cpo&vim 797963c1ad5SBram Moolenaar 798589edb34SBram Moolenaar command -nargs=? Break call s:SetBreakpoint(<q-args>) 79971137fedSBram Moolenaar command Clear call s:ClearBreakpoint() 800e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 80145d5f26dSBram Moolenaar command Over call s:SendCommand('-exec-next') 802e09ba7baSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 80360e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 80460e73f2aSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 80560e73f2aSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 806b3307b5eSBram Moolenaar 807b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 808b3307b5eSBram Moolenaar if s:way == 'prompt' 809b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 810b3307b5eSBram Moolenaar else 811b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 812b3307b5eSBram Moolenaar endif 813b3307b5eSBram Moolenaar 81445d5f26dSBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 81545d5f26dSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 816589edb34SBram Moolenaar command Program call s:GotoProgram() 817b3307b5eSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 81882be4849SBram Moolenaar command Asm call s:GotoAsmwinOrCreateIt() 81971137fedSBram Moolenaar command Winbar call s:InstallWinbar() 82045d5f26dSBram Moolenaar 821388a5d4fSBram Moolenaar if !exists('g:termdebug_map_K') || g:termdebug_map_K 822388a5d4fSBram Moolenaar let s:k_map_saved = maparg('K', 'n', 0, 1) 82345d5f26dSBram Moolenaar nnoremap K :Evaluate<CR> 824388a5d4fSBram Moolenaar endif 8251b9645deSBram Moolenaar 826f0b03c4eSBram Moolenaar if has('menu') && &mouse != '' 82771137fedSBram Moolenaar call s:InstallWinbar() 82871137fedSBram Moolenaar 82971137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 83071137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 83171137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 83271137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 83371137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 83471137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 83571137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 83671137fedSBram Moolenaar endif 83771137fedSBram Moolenaar endif 838963c1ad5SBram Moolenaar 839963c1ad5SBram Moolenaar let &cpo = save_cpo 84071137fedSBram Moolenaarendfunc 84171137fedSBram Moolenaar 84271137fedSBram Moolenaarlet s:winbar_winids = [] 84371137fedSBram Moolenaar 84471137fedSBram Moolenaar" Install the window toolbar in the current window. 84571137fedSBram Moolenaarfunc s:InstallWinbar() 846c4b533e1SBram Moolenaar if has('menu') && &mouse != '' 84724a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 84824a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 84924a98a0eSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 85024a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 85160e73f2aSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 85224a98a0eSBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 85371137fedSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 854c4b533e1SBram Moolenaar endif 855e09ba7baSBram Moolenaarendfunc 856e09ba7baSBram Moolenaar 857e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 858e09ba7baSBram Moolenaarfunc s:DeleteCommands() 859e09ba7baSBram Moolenaar delcommand Break 86071137fedSBram Moolenaar delcommand Clear 861e09ba7baSBram Moolenaar delcommand Step 86245d5f26dSBram Moolenaar delcommand Over 863e09ba7baSBram Moolenaar delcommand Finish 86460e73f2aSBram Moolenaar delcommand Run 86560e73f2aSBram Moolenaar delcommand Arguments 86660e73f2aSBram Moolenaar delcommand Stop 867e09ba7baSBram Moolenaar delcommand Continue 86845d5f26dSBram Moolenaar delcommand Evaluate 86945d5f26dSBram Moolenaar delcommand Gdb 87045d5f26dSBram Moolenaar delcommand Program 871b3623a38SBram Moolenaar delcommand Source 87282be4849SBram Moolenaar delcommand Asm 87371137fedSBram Moolenaar delcommand Winbar 87445d5f26dSBram Moolenaar 8751b884a00SBram Moolenaar if exists('s:k_map_saved') 8761b884a00SBram Moolenaar if empty(s:k_map_saved) 8771b884a00SBram Moolenaar nunmap K 8781b884a00SBram Moolenaar else 879388a5d4fSBram Moolenaar call mapset('n', 0, s:k_map_saved) 8801b884a00SBram Moolenaar endif 881388a5d4fSBram Moolenaar unlet s:k_map_saved 882388a5d4fSBram Moolenaar endif 8831b9645deSBram Moolenaar 8841b9645deSBram Moolenaar if has('menu') 88571137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 88671137fedSBram Moolenaar let curwinid = win_getid(winnr()) 88771137fedSBram Moolenaar for winid in s:winbar_winids 88871137fedSBram Moolenaar if win_gotoid(winid) 8891b9645deSBram Moolenaar aunmenu WinBar.Step 8901b9645deSBram Moolenaar aunmenu WinBar.Next 8911b9645deSBram Moolenaar aunmenu WinBar.Finish 8921b9645deSBram Moolenaar aunmenu WinBar.Cont 89360e73f2aSBram Moolenaar aunmenu WinBar.Stop 8941b9645deSBram Moolenaar aunmenu WinBar.Eval 8951b9645deSBram Moolenaar endif 89671137fedSBram Moolenaar endfor 89771137fedSBram Moolenaar call win_gotoid(curwinid) 89871137fedSBram Moolenaar let s:winbar_winids = [] 89971137fedSBram Moolenaar 90071137fedSBram Moolenaar if exists('s:saved_mousemodel') 90171137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 90271137fedSBram Moolenaar unlet s:saved_mousemodel 90371137fedSBram Moolenaar aunmenu PopUp.-SEP3- 90471137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 90571137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 90671137fedSBram Moolenaar aunmenu PopUp.Evaluate 90771137fedSBram Moolenaar endif 90871137fedSBram Moolenaar endif 9091b9645deSBram Moolenaar 91045d5f26dSBram Moolenaar exe 'sign unplace ' . s:pc_id 91137402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 91237402ed5SBram Moolenaar for subid in keys(entries) 91337402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 91437402ed5SBram Moolenaar endfor 91545d5f26dSBram Moolenaar endfor 91645d5f26dSBram Moolenaar unlet s:breakpoints 91737402ed5SBram Moolenaar unlet s:breakpoint_locations 918a15b0a93SBram Moolenaar 919a15b0a93SBram Moolenaar sign undefine debugPC 920a15b0a93SBram Moolenaar for val in s:BreakpointSigns 921a15b0a93SBram Moolenaar exe "sign undefine debugBreakpoint" . val 922a15b0a93SBram Moolenaar endfor 9234551c0a9SBram Moolenaar let s:BreakpointSigns = [] 924e09ba7baSBram Moolenaarendfunc 925e09ba7baSBram Moolenaar 926e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 927589edb34SBram Moolenaarfunc s:SetBreakpoint(at) 92860e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 92960e73f2aSBram Moolenaar " Interrupt to make it work. 93060e73f2aSBram Moolenaar let do_continue = 0 93160e73f2aSBram Moolenaar if !s:stopped 93260e73f2aSBram Moolenaar let do_continue = 1 933b3307b5eSBram Moolenaar if s:way == 'prompt' 9344551c0a9SBram Moolenaar call s:PromptInterrupt() 935b3307b5eSBram Moolenaar else 93660e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 937b3307b5eSBram Moolenaar endif 93860e73f2aSBram Moolenaar sleep 10m 93960e73f2aSBram Moolenaar endif 940589edb34SBram Moolenaar 941a15b0a93SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 942589edb34SBram Moolenaar let at = empty(a:at) ? 943589edb34SBram Moolenaar \ fnameescape(expand('%:p')) . ':' . line('.') : a:at 944589edb34SBram Moolenaar call s:SendCommand('-break-insert ' . at) 94560e73f2aSBram Moolenaar if do_continue 94660e73f2aSBram Moolenaar call s:SendCommand('-exec-continue') 94760e73f2aSBram Moolenaar endif 948e09ba7baSBram Moolenaarendfunc 949e09ba7baSBram Moolenaar 95071137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 95171137fedSBram Moolenaarfunc s:ClearBreakpoint() 952e09ba7baSBram Moolenaar let fname = fnameescape(expand('%:p')) 953e09ba7baSBram Moolenaar let lnum = line('.') 95437402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 95537402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 95637402ed5SBram Moolenaar let idx = 0 95737402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 95837402ed5SBram Moolenaar if has_key(s:breakpoints, id) 95937402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 96037402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 96137402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 96237402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 96337402ed5SBram Moolenaar endfor 96437402ed5SBram Moolenaar unlet s:breakpoints[id] 96537402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 966e09ba7baSBram Moolenaar break 96737402ed5SBram Moolenaar else 96837402ed5SBram Moolenaar let idx += 1 969e09ba7baSBram Moolenaar endif 970e09ba7baSBram Moolenaar endfor 97137402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 97237402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 97337402ed5SBram Moolenaar endif 97437402ed5SBram Moolenaar endif 975e09ba7baSBram Moolenaarendfunc 976e09ba7baSBram Moolenaar 97760e73f2aSBram Moolenaarfunc s:Run(args) 97860e73f2aSBram Moolenaar if a:args != '' 97960e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 98060e73f2aSBram Moolenaar endif 98160e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 98260e73f2aSBram Moolenaarendfunc 98360e73f2aSBram Moolenaar 98451b0f370SBram Moolenaarfunc s:SendEval(expr) 985113cb513SBram Moolenaar " clean up expression that may got in because of range 986113cb513SBram Moolenaar " (newlines and surrounding spaces) 987113cb513SBram Moolenaar let expr = a:expr 988113cb513SBram Moolenaar if &filetype ==# 'cobol' 989113cb513SBram Moolenaar " extra cleanup for COBOL: _every: expression ends with a period, 990113cb513SBram Moolenaar " a trailing comma is ignored as it commonly separates multiple expr. 991113cb513SBram Moolenaar let expr = substitute(expr, '\..*', '', '') 992113cb513SBram Moolenaar let expr = substitute(expr, '[;\n]', ' ', 'g') 993113cb513SBram Moolenaar let expr = substitute(expr, ',*$', '', '') 994113cb513SBram Moolenaar else 995113cb513SBram Moolenaar let expr = substitute(expr, '\n', ' ', 'g') 996113cb513SBram Moolenaar endif 997113cb513SBram Moolenaar let expr = substitute(expr, '^ *\(.*\) *', '\1', '') 998113cb513SBram Moolenaar 999113cb513SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . expr . '"') 1000113cb513SBram Moolenaar let s:evalexpr = expr 100151b0f370SBram Moolenaarendfunc 100251b0f370SBram Moolenaar 100345d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 100445d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 100545d5f26dSBram Moolenaar if a:arg != '' 100645d5f26dSBram Moolenaar let expr = a:arg 100745d5f26dSBram Moolenaar elseif a:range == 2 100845d5f26dSBram Moolenaar let pos = getcurpos() 100945d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 101045d5f26dSBram Moolenaar let regt = getregtype('v') 101145d5f26dSBram Moolenaar normal! gv"vy 101245d5f26dSBram Moolenaar let expr = @v 101345d5f26dSBram Moolenaar call setpos('.', pos) 101445d5f26dSBram Moolenaar call setreg('v', reg, regt) 101545d5f26dSBram Moolenaar else 101645d5f26dSBram Moolenaar let expr = expand('<cexpr>') 101745d5f26dSBram Moolenaar endif 101822f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 101951b0f370SBram Moolenaar call s:SendEval(expr) 102045d5f26dSBram Moolenaarendfunc 102145d5f26dSBram Moolenaar 102222f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0 102351b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0 102451b0f370SBram Moolenaar 102545d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression 102645d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg) 10271b9645deSBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 10281b9645deSBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 102951b0f370SBram Moolenaar if s:evalFromBalloonExpr 103051b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 103151b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 103251b0f370SBram Moolenaar else 103351b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 103451b0f370SBram Moolenaar endif 103551b0f370SBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 103651b0f370SBram Moolenaar else 10371b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 103851b0f370SBram Moolenaar endif 10391b9645deSBram Moolenaar 10407f2e9d7cSBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 10411b9645deSBram Moolenaar " Looks like a pointer, also display what it points to. 104222f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 104351b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 104451b0f370SBram Moolenaar else 104551b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 10461b9645deSBram Moolenaar endif 104745d5f26dSBram Moolenaarendfunc 104845d5f26dSBram Moolenaar 104951b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 105051b0f370SBram Moolenaar" if there is any. 105151b0f370SBram Moolenaarfunc TermDebugBalloonExpr() 1052b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 1053396e829fSBram Moolenaar return '' 1054b3307b5eSBram Moolenaar endif 1055b3307b5eSBram Moolenaar if !s:stopped 1056b3307b5eSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 1057b3307b5eSBram Moolenaar " mouse triggers a balloon. 1058396e829fSBram Moolenaar return '' 105951b0f370SBram Moolenaar endif 106051b0f370SBram Moolenaar let s:evalFromBalloonExpr = 1 106151b0f370SBram Moolenaar let s:evalFromBalloonExprResult = '' 106222f1d0e3SBram Moolenaar let s:ignoreEvalError = 1 106322f1d0e3SBram Moolenaar call s:SendEval(v:beval_text) 106451b0f370SBram Moolenaar return '' 106551b0f370SBram Moolenaarendfunc 106651b0f370SBram Moolenaar 106745d5f26dSBram Moolenaar" Handle an error. 106845d5f26dSBram Moolenaarfunc s:HandleError(msg) 106922f1d0e3SBram Moolenaar if s:ignoreEvalError 107051b0f370SBram Moolenaar " Result of s:SendEval() failed, ignore. 107122f1d0e3SBram Moolenaar let s:ignoreEvalError = 0 107222f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 0 107351b0f370SBram Moolenaar return 107451b0f370SBram Moolenaar endif 1075113cb513SBram Moolenaar let msgVal = substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 1076113cb513SBram Moolenaar echoerr substitute(msgVal, '\\"', '"', 'g') 107745d5f26dSBram Moolenaarendfunc 107845d5f26dSBram Moolenaar 1079b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 1080b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 1081c4b533e1SBram Moolenaar new 1082b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 1083c4b533e1SBram Moolenaar call s:InstallWinbar() 1084c4b533e1SBram Moolenaar endif 1085c4b533e1SBram Moolenaarendfunc 1086c4b533e1SBram Moolenaar 108782be4849SBram Moolenaarfunc s:GotoAsmwinOrCreateIt() 108882be4849SBram Moolenaar if !win_gotoid(s:asmwin) 108982be4849SBram Moolenaar if win_gotoid(s:sourcewin) 109082be4849SBram Moolenaar exe 'rightbelow new' 109182be4849SBram Moolenaar else 109282be4849SBram Moolenaar exe 'new' 109382be4849SBram Moolenaar endif 109482be4849SBram Moolenaar 109582be4849SBram Moolenaar let s:asmwin = win_getid(winnr()) 109682be4849SBram Moolenaar 109782be4849SBram Moolenaar setlocal nowrap 109882be4849SBram Moolenaar setlocal number 109982be4849SBram Moolenaar setlocal noswapfile 110082be4849SBram Moolenaar setlocal buftype=nofile 1101*519cc559SBram Moolenaar setlocal modifiable 110282be4849SBram Moolenaar 110382be4849SBram Moolenaar let asmbuf = bufnr('Termdebug-asm-listing') 110482be4849SBram Moolenaar if asmbuf > 0 110582be4849SBram Moolenaar exe 'buffer' . asmbuf 110682be4849SBram Moolenaar else 110782be4849SBram Moolenaar exe 'file Termdebug-asm-listing' 110882be4849SBram Moolenaar endif 110982be4849SBram Moolenaar 111082be4849SBram Moolenaar if exists('g:termdebug_disasm_window') 111182be4849SBram Moolenaar if g:termdebug_disasm_window > 1 111282be4849SBram Moolenaar exe 'resize ' . g:termdebug_disasm_window 111382be4849SBram Moolenaar endif 111482be4849SBram Moolenaar endif 111582be4849SBram Moolenaar endif 111682be4849SBram Moolenaar 111782be4849SBram Moolenaar if s:asm_addr != '' 111882be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 111982be4849SBram Moolenaar if lnum == 0 112082be4849SBram Moolenaar if s:stopped 112182be4849SBram Moolenaar call s:SendCommand('disassemble $pc') 112282be4849SBram Moolenaar endif 112382be4849SBram Moolenaar else 112482be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 112582be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 112682be4849SBram Moolenaar endif 112782be4849SBram Moolenaar endif 112882be4849SBram Moolenaarendfunc 112982be4849SBram Moolenaar 1130e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 1131e09ba7baSBram Moolenaar" Will update the sign that shows the current position. 1132e09ba7baSBram Moolenaarfunc s:HandleCursor(msg) 1133fe386641SBram Moolenaar let wid = win_getid(winnr()) 1134fe386641SBram Moolenaar 113560e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 11364551c0a9SBram Moolenaar call ch_log('program stopped') 113760e73f2aSBram Moolenaar let s:stopped = 1 113860e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 11394551c0a9SBram Moolenaar call ch_log('program running') 114060e73f2aSBram Moolenaar let s:stopped = 0 114160e73f2aSBram Moolenaar endif 114260e73f2aSBram Moolenaar 1143a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 1144a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 1145a15b0a93SBram Moolenaar else 1146a15b0a93SBram Moolenaar let fname = '' 1147a15b0a93SBram Moolenaar endif 114882be4849SBram Moolenaar 114982be4849SBram Moolenaar if a:msg =~ 'addr=' 115082be4849SBram Moolenaar let asm_addr = s:GetAsmAddr(a:msg) 115182be4849SBram Moolenaar if asm_addr != '' 115282be4849SBram Moolenaar let s:asm_addr = asm_addr 115382be4849SBram Moolenaar 115482be4849SBram Moolenaar let curwinid = win_getid(winnr()) 115582be4849SBram Moolenaar if win_gotoid(s:asmwin) 115682be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 115782be4849SBram Moolenaar if lnum == 0 115882be4849SBram Moolenaar call s:SendCommand('disassemble $pc') 115982be4849SBram Moolenaar else 116082be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 116182be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 116282be4849SBram Moolenaar endif 116382be4849SBram Moolenaar 116482be4849SBram Moolenaar call win_gotoid(curwinid) 116582be4849SBram Moolenaar endif 116682be4849SBram Moolenaar endif 116782be4849SBram Moolenaar endif 116882be4849SBram Moolenaar 11691b9645deSBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 1170e09ba7baSBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 1171fe386641SBram Moolenaar if lnum =~ '^[0-9]*$' 11724551c0a9SBram Moolenaar call s:GotoSourcewinOrCreateIt() 11731b9645deSBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 1174fe386641SBram Moolenaar if &modified 1175fe386641SBram Moolenaar " TODO: find existing window 1176fe386641SBram Moolenaar exe 'split ' . fnameescape(fname) 1177b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 1178c4b533e1SBram Moolenaar call s:InstallWinbar() 1179fe386641SBram Moolenaar else 1180fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 1181fe386641SBram Moolenaar endif 1182fe386641SBram Moolenaar endif 1183fe386641SBram Moolenaar exe lnum 1184*519cc559SBram Moolenaar normal! zv 118501164a65SBram Moolenaar exe 'sign unplace ' . s:pc_id 118639f7aa3cSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname 1187cb80aa2dSBram Moolenaar if !exists('b:save_signcolumn') 1188cb80aa2dSBram Moolenaar let b:save_signcolumn = &signcolumn 1189cb80aa2dSBram Moolenaar call add(s:signcolumn_buflist, bufnr()) 1190cb80aa2dSBram Moolenaar endif 1191fe386641SBram Moolenaar setlocal signcolumn=yes 1192fe386641SBram Moolenaar endif 11934551c0a9SBram Moolenaar elseif !s:stopped || fname != '' 1194fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 1195fe386641SBram Moolenaar endif 1196fe386641SBram Moolenaar 1197fe386641SBram Moolenaar call win_gotoid(wid) 1198e09ba7baSBram Moolenaarendfunc 1199e09ba7baSBram Moolenaar 1200de1a8314SBram Moolenaarlet s:BreakpointSigns = [] 1201a15b0a93SBram Moolenaar 120237402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 120337402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 120437402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 120537402ed5SBram Moolenaar call add(s:BreakpointSigns, nr) 120637402ed5SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 1207de1a8314SBram Moolenaar endif 1208de1a8314SBram Moolenaarendfunc 1209de1a8314SBram Moolenaar 121037402ed5SBram Moolenaarfunc! s:SplitMsg(s) 121137402ed5SBram Moolenaar return split(a:s, '{.\{-}}\zs') 12125378e1cfSBram Moolenaarendfunction 12135378e1cfSBram Moolenaar 1214e09ba7baSBram Moolenaar" Handle setting a breakpoint 1215e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint 1216e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg) 12176dccc962SBram Moolenaar if a:msg !~ 'fullname=' 12186dccc962SBram Moolenaar " a watch does not have a file name 12196dccc962SBram Moolenaar return 12206dccc962SBram Moolenaar endif 12215378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 12225378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 12235378e1cfSBram Moolenaar if empty(fname) 12245378e1cfSBram Moolenaar continue 12255378e1cfSBram Moolenaar endif 12265378e1cfSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 12275378e1cfSBram Moolenaar if empty(nr) 1228e09ba7baSBram Moolenaar return 1229fe386641SBram Moolenaar endif 1230e09ba7baSBram Moolenaar 123137402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 123237402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 123337402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 123437402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 123537402ed5SBram Moolenaar 123637402ed5SBram Moolenaar if has_key(s:breakpoints, id) 123737402ed5SBram Moolenaar let entries = s:breakpoints[id] 123837402ed5SBram Moolenaar else 123937402ed5SBram Moolenaar let entries = {} 124037402ed5SBram Moolenaar let s:breakpoints[id] = entries 124137402ed5SBram Moolenaar endif 124237402ed5SBram Moolenaar if has_key(entries, subid) 124337402ed5SBram Moolenaar let entry = entries[subid] 1244e09ba7baSBram Moolenaar else 1245e09ba7baSBram Moolenaar let entry = {} 124637402ed5SBram Moolenaar let entries[subid] = entry 1247fe386641SBram Moolenaar endif 1248e09ba7baSBram Moolenaar 12495378e1cfSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 1250e09ba7baSBram Moolenaar let entry['fname'] = fname 1251e09ba7baSBram Moolenaar let entry['lnum'] = lnum 12521b9645deSBram Moolenaar 125337402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 125437402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 125537402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 125637402ed5SBram Moolenaar endif 125737402ed5SBram Moolenaar let s:breakpoint_locations[bploc] += [id] 125837402ed5SBram Moolenaar 12591b9645deSBram Moolenaar if bufloaded(fname) 126037402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 12611b9645deSBram Moolenaar endif 12625378e1cfSBram Moolenaar endfor 12631b9645deSBram Moolenaarendfunc 12641b9645deSBram Moolenaar 126537402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry) 126637402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 12673132cdddSBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' priority=110 file=' . a:entry['fname'] 12681b9645deSBram Moolenaar let a:entry['placed'] = 1 1269e09ba7baSBram Moolenaarendfunc 1270e09ba7baSBram Moolenaar 1271e09ba7baSBram Moolenaar" Handle deleting a breakpoint 1272e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint 1273e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg) 127437402ed5SBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 127537402ed5SBram Moolenaar if empty(id) 1276e09ba7baSBram Moolenaar return 1277e09ba7baSBram Moolenaar endif 127837402ed5SBram Moolenaar if has_key(s:breakpoints, id) 127937402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 12801b9645deSBram Moolenaar if has_key(entry, 'placed') 128137402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 12821b9645deSBram Moolenaar unlet entry['placed'] 12831b9645deSBram Moolenaar endif 12845378e1cfSBram Moolenaar endfor 128537402ed5SBram Moolenaar unlet s:breakpoints[id] 128637402ed5SBram Moolenaar endif 1287c572da5fSBram Moolenaarendfunc 12881b9645deSBram Moolenaar 12894551c0a9SBram Moolenaar" Handle the debugged program starting to run. 12904551c0a9SBram Moolenaar" Will store the process ID in s:pid 12914551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 12924551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 12934551c0a9SBram Moolenaar if nr == 0 12944551c0a9SBram Moolenaar return 12954551c0a9SBram Moolenaar endif 12964551c0a9SBram Moolenaar let s:pid = nr 12974551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 12984551c0a9SBram Moolenaarendfunc 12994551c0a9SBram Moolenaar 13001b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 13011b9645deSBram Moolenaarfunc s:BufRead() 13021b9645deSBram Moolenaar let fname = expand('<afile>:p') 130337402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 130437402ed5SBram Moolenaar for [subid, entry] in items(entries) 13051b9645deSBram Moolenaar if entry['fname'] == fname 130637402ed5SBram Moolenaar call s:PlaceSign(id, subid, entry) 13071b9645deSBram Moolenaar endif 13081b9645deSBram Moolenaar endfor 130937402ed5SBram Moolenaar endfor 13101b9645deSBram Moolenaarendfunc 13111b9645deSBram Moolenaar 13121b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 13131b9645deSBram Moolenaarfunc s:BufUnloaded() 13141b9645deSBram Moolenaar let fname = expand('<afile>:p') 131537402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 131637402ed5SBram Moolenaar for [subid, entry] in items(entries) 13171b9645deSBram Moolenaar if entry['fname'] == fname 13181b9645deSBram Moolenaar let entry['placed'] = 0 13191b9645deSBram Moolenaar endif 13201b9645deSBram Moolenaar endfor 132137402ed5SBram Moolenaar endfor 13221b9645deSBram Moolenaarendfunc 1323ca4cc018SBram Moolenaar 1324ca4cc018SBram Moolenaarlet &cpo = s:keepcpo 1325ca4cc018SBram Moolenaarunlet s:keepcpo 1326