1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3b3307b5eSBram Moolenaar" Author: Bram Moolenaar 4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license" 5*89a9c159SBram Moolenaar" Last Change: 2021 Aug 23 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')}) 330*89a9c159SBram Moolenaar 331*89a9c159SBram Moolenaar " Set the filetype, this can be used to add mappings. 332*89a9c159SBram Moolenaar set filetype=termdebug 333*89a9c159SBram 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 537b3307b5eSBram 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, '^=>[ ]*', '', '') 73982be4849SBram Moolenaar let value = substitute(value, '\\n\" 74082be4849SBram Moolenaar$', '', '') 74182be4849SBram Moolenaar let value = substitute(value, '\\n\"$', '', '') 74282be4849SBram Moolenaar let value = substitute(value, ' 74382be4849SBram Moolenaar', '', '') 74482be4849SBram Moolenaar let value = substitute(value, '\\t', ' ', 'g') 74582be4849SBram Moolenaar 74682be4849SBram Moolenaar if value != '' || !empty(s:asm_lines) 74782be4849SBram Moolenaar call add(s:asm_lines, value) 74882be4849SBram Moolenaar endif 74982be4849SBram Moolenaar endif 750fe386641SBram Moolenaarendfunc 751fe386641SBram Moolenaar 752fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 753fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 754fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 755fe386641SBram Moolenaar 756fe386641SBram Moolenaar for msg in msgs 757fe386641SBram Moolenaar " remove prefixed NL 758fe386641SBram Moolenaar if msg[0] == "\n" 75982be4849SBram Moolenaar let msg = msg[1:] 76082be4849SBram Moolenaar endif 76182be4849SBram Moolenaar 76282be4849SBram Moolenaar if s:parsing_disasm_msg 7631b9645deSBram Moolenaar call s:HandleDisasmMsg(msg) 764e09ba7baSBram Moolenaar elseif msg != '' 76545d5f26dSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 766e09ba7baSBram Moolenaar call s:HandleCursor(msg) 767e09ba7baSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 768e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 7694551c0a9SBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 7704551c0a9SBram Moolenaar call s:HandleBreakpointDelete(msg) 77145d5f26dSBram Moolenaar elseif msg =~ '^=thread-group-started' 77245d5f26dSBram Moolenaar call s:HandleProgramRun(msg) 77345d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 77445d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 77582be4849SBram Moolenaar elseif msg =~ '^\^error,msg=' 77682be4849SBram Moolenaar call s:HandleError(msg) 77782be4849SBram Moolenaar elseif msg =~ '^disassemble' 778e09ba7baSBram Moolenaar let s:parsing_disasm_msg = 1 779e09ba7baSBram Moolenaar let s:asm_lines = [] 780e09ba7baSBram Moolenaar endif 781e09ba7baSBram Moolenaar endif 782e09ba7baSBram Moolenaar endfor 783589edb34SBram Moolenaarendfunc 784589edb34SBram Moolenaar 785589edb34SBram Moolenaarfunc s:GotoProgram() 786589edb34SBram Moolenaar if has('win32') 787589edb34SBram Moolenaar if executable('powershell') 788589edb34SBram Moolenaar call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid)) 789469bdbdeSBram Moolenaar endif 790589edb34SBram Moolenaar else 791589edb34SBram Moolenaar call win_gotoid(s:ptywin) 792589edb34SBram Moolenaar endif 793e09ba7baSBram Moolenaarendfunc 794e09ba7baSBram Moolenaar 795963c1ad5SBram Moolenaar" Install commands in the current window to control the debugger. 796963c1ad5SBram Moolenaarfunc s:InstallCommands() 797963c1ad5SBram Moolenaar let save_cpo = &cpo 798589edb34SBram Moolenaar set cpo&vim 79971137fedSBram Moolenaar 800e09ba7baSBram Moolenaar command -nargs=? Break call s:SetBreakpoint(<q-args>) 80145d5f26dSBram Moolenaar command Clear call s:ClearBreakpoint() 802e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 80360e73f2aSBram Moolenaar command Over call s:SendCommand('-exec-next') 80460e73f2aSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 80560e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 806b3307b5eSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 807b3307b5eSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 808b3307b5eSBram Moolenaar 809b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 810b3307b5eSBram Moolenaar if s:way == 'prompt' 811b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 812b3307b5eSBram Moolenaar else 813b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 81445d5f26dSBram Moolenaar endif 81545d5f26dSBram Moolenaar 816589edb34SBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 817b3307b5eSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 81882be4849SBram Moolenaar command Program call s:GotoProgram() 81971137fedSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 82045d5f26dSBram Moolenaar command Asm call s:GotoAsmwinOrCreateIt() 821388a5d4fSBram Moolenaar command Winbar call s:InstallWinbar() 822388a5d4fSBram Moolenaar 82345d5f26dSBram Moolenaar if !exists('g:termdebug_map_K') || g:termdebug_map_K 824388a5d4fSBram Moolenaar let s:k_map_saved = maparg('K', 'n', 0, 1) 8251b9645deSBram Moolenaar nnoremap K :Evaluate<CR> 826f0b03c4eSBram Moolenaar endif 82771137fedSBram Moolenaar 82871137fedSBram Moolenaar if has('menu') && &mouse != '' 82971137fedSBram Moolenaar call s:InstallWinbar() 83071137fedSBram Moolenaar 83171137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 83271137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 83371137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 83471137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 83571137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 83671137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 83771137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 838963c1ad5SBram Moolenaar endif 839963c1ad5SBram Moolenaar endif 84071137fedSBram Moolenaar 84171137fedSBram Moolenaar let &cpo = save_cpo 84271137fedSBram Moolenaarendfunc 84371137fedSBram Moolenaar 84471137fedSBram Moolenaarlet s:winbar_winids = [] 84571137fedSBram Moolenaar 846c4b533e1SBram Moolenaar" Install the window toolbar in the current window. 84724a98a0eSBram Moolenaarfunc s:InstallWinbar() 84824a98a0eSBram Moolenaar if has('menu') && &mouse != '' 84924a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 85024a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 85160e73f2aSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 85224a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 85371137fedSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 854c4b533e1SBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 855e09ba7baSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 856e09ba7baSBram Moolenaar endif 857e09ba7baSBram Moolenaarendfunc 858e09ba7baSBram Moolenaar 859e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 86071137fedSBram Moolenaarfunc s:DeleteCommands() 861e09ba7baSBram Moolenaar delcommand Break 86245d5f26dSBram Moolenaar delcommand Clear 863e09ba7baSBram Moolenaar delcommand Step 86460e73f2aSBram Moolenaar delcommand Over 86560e73f2aSBram Moolenaar delcommand Finish 86660e73f2aSBram Moolenaar delcommand Run 867e09ba7baSBram Moolenaar delcommand Arguments 86845d5f26dSBram Moolenaar delcommand Stop 86945d5f26dSBram Moolenaar delcommand Continue 87045d5f26dSBram Moolenaar delcommand Evaluate 871b3623a38SBram Moolenaar delcommand Gdb 87282be4849SBram Moolenaar delcommand Program 87371137fedSBram Moolenaar delcommand Source 87445d5f26dSBram Moolenaar delcommand Asm 8751b884a00SBram Moolenaar delcommand Winbar 8761b884a00SBram Moolenaar 8771b884a00SBram Moolenaar if exists('s:k_map_saved') 8781b884a00SBram Moolenaar if empty(s:k_map_saved) 879388a5d4fSBram Moolenaar nunmap K 8801b884a00SBram Moolenaar else 881388a5d4fSBram Moolenaar call mapset('n', 0, s:k_map_saved) 882388a5d4fSBram Moolenaar endif 8831b9645deSBram Moolenaar unlet s:k_map_saved 8841b9645deSBram Moolenaar endif 88571137fedSBram Moolenaar 88671137fedSBram Moolenaar if has('menu') 88771137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 88871137fedSBram Moolenaar let curwinid = win_getid(winnr()) 8891b9645deSBram Moolenaar for winid in s:winbar_winids 8901b9645deSBram Moolenaar if win_gotoid(winid) 8911b9645deSBram Moolenaar aunmenu WinBar.Step 8921b9645deSBram Moolenaar aunmenu WinBar.Next 89360e73f2aSBram Moolenaar aunmenu WinBar.Finish 8941b9645deSBram Moolenaar aunmenu WinBar.Cont 8951b9645deSBram Moolenaar aunmenu WinBar.Stop 89671137fedSBram Moolenaar aunmenu WinBar.Eval 89771137fedSBram Moolenaar endif 89871137fedSBram Moolenaar endfor 89971137fedSBram Moolenaar call win_gotoid(curwinid) 90071137fedSBram Moolenaar let s:winbar_winids = [] 90171137fedSBram Moolenaar 90271137fedSBram Moolenaar if exists('s:saved_mousemodel') 90371137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 90471137fedSBram Moolenaar unlet s:saved_mousemodel 90571137fedSBram Moolenaar aunmenu PopUp.-SEP3- 90671137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 90771137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 90871137fedSBram Moolenaar aunmenu PopUp.Evaluate 9091b9645deSBram Moolenaar endif 91045d5f26dSBram Moolenaar endif 91137402ed5SBram Moolenaar 91237402ed5SBram Moolenaar exe 'sign unplace ' . s:pc_id 91337402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 91437402ed5SBram Moolenaar for subid in keys(entries) 91545d5f26dSBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 91645d5f26dSBram Moolenaar endfor 91737402ed5SBram Moolenaar endfor 918a15b0a93SBram Moolenaar unlet s:breakpoints 919a15b0a93SBram Moolenaar unlet s:breakpoint_locations 920a15b0a93SBram Moolenaar 921a15b0a93SBram Moolenaar sign undefine debugPC 922a15b0a93SBram Moolenaar for val in s:BreakpointSigns 9234551c0a9SBram Moolenaar exe "sign undefine debugBreakpoint" . val 924e09ba7baSBram Moolenaar endfor 925e09ba7baSBram Moolenaar let s:BreakpointSigns = [] 926e09ba7baSBram Moolenaarendfunc 927589edb34SBram Moolenaar 92860e73f2aSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 92960e73f2aSBram Moolenaarfunc s:SetBreakpoint(at) 93060e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 93160e73f2aSBram Moolenaar " Interrupt to make it work. 93260e73f2aSBram Moolenaar let do_continue = 0 933b3307b5eSBram Moolenaar if !s:stopped 9344551c0a9SBram Moolenaar let do_continue = 1 935b3307b5eSBram Moolenaar if s:way == 'prompt' 93660e73f2aSBram Moolenaar call s:PromptInterrupt() 937b3307b5eSBram Moolenaar else 93860e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 93960e73f2aSBram Moolenaar endif 940589edb34SBram Moolenaar sleep 10m 941a15b0a93SBram Moolenaar endif 942589edb34SBram Moolenaar 943589edb34SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 944589edb34SBram Moolenaar let at = empty(a:at) ? 94560e73f2aSBram Moolenaar \ fnameescape(expand('%:p')) . ':' . line('.') : a:at 94660e73f2aSBram Moolenaar call s:SendCommand('-break-insert ' . at) 94760e73f2aSBram Moolenaar if do_continue 948e09ba7baSBram Moolenaar call s:SendCommand('-exec-continue') 949e09ba7baSBram Moolenaar endif 95071137fedSBram Moolenaarendfunc 95171137fedSBram Moolenaar 952e09ba7baSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 953e09ba7baSBram Moolenaarfunc s:ClearBreakpoint() 95437402ed5SBram Moolenaar let fname = fnameescape(expand('%:p')) 95537402ed5SBram Moolenaar let lnum = line('.') 95637402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 95737402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 95837402ed5SBram Moolenaar let idx = 0 95937402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 96037402ed5SBram Moolenaar if has_key(s:breakpoints, id) 96137402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 96237402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 96337402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 96437402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 96537402ed5SBram Moolenaar endfor 966e09ba7baSBram Moolenaar unlet s:breakpoints[id] 96737402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 96837402ed5SBram Moolenaar break 969e09ba7baSBram Moolenaar else 970e09ba7baSBram Moolenaar let idx += 1 97137402ed5SBram Moolenaar endif 97237402ed5SBram Moolenaar endfor 97337402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 97437402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 975e09ba7baSBram Moolenaar endif 976e09ba7baSBram Moolenaar endif 97760e73f2aSBram Moolenaarendfunc 97860e73f2aSBram Moolenaar 97960e73f2aSBram Moolenaarfunc s:Run(args) 98060e73f2aSBram Moolenaar if a:args != '' 98160e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 98260e73f2aSBram Moolenaar endif 98360e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 98451b0f370SBram Moolenaarendfunc 98551b0f370SBram Moolenaar 98651b0f370SBram Moolenaarfunc s:SendEval(expr) 98751b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 98851b0f370SBram Moolenaar let s:evalexpr = a:expr 98945d5f26dSBram Moolenaarendfunc 99045d5f26dSBram Moolenaar 99145d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 99245d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 99345d5f26dSBram Moolenaar if a:arg != '' 99445d5f26dSBram Moolenaar let expr = a:arg 99545d5f26dSBram Moolenaar elseif a:range == 2 99645d5f26dSBram Moolenaar let pos = getcurpos() 99745d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 99845d5f26dSBram Moolenaar let regt = getregtype('v') 99945d5f26dSBram Moolenaar normal! gv"vy 100045d5f26dSBram Moolenaar let expr = @v 100145d5f26dSBram Moolenaar call setpos('.', pos) 100245d5f26dSBram Moolenaar call setreg('v', reg, regt) 100345d5f26dSBram Moolenaar else 100422f1d0e3SBram Moolenaar let expr = expand('<cexpr>') 100551b0f370SBram Moolenaar endif 100645d5f26dSBram Moolenaar let s:ignoreEvalError = 0 100745d5f26dSBram Moolenaar call s:SendEval(expr) 100822f1d0e3SBram Moolenaarendfunc 100951b0f370SBram Moolenaar 101051b0f370SBram Moolenaarlet s:ignoreEvalError = 0 101145d5f26dSBram Moolenaarlet s:evalFromBalloonExpr = 0 101245d5f26dSBram Moolenaar 10131b9645deSBram Moolenaar" Handle the result of data-evaluate-expression 10141b9645deSBram Moolenaarfunc s:HandleEvaluate(msg) 101551b0f370SBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 101651b0f370SBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 101751b0f370SBram Moolenaar if s:evalFromBalloonExpr 101851b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 101951b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 102051b0f370SBram Moolenaar else 102151b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 102251b0f370SBram Moolenaar endif 10231b9645deSBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 102451b0f370SBram Moolenaar else 10251b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 10267f2e9d7cSBram Moolenaar endif 10271b9645deSBram Moolenaar 102822f1d0e3SBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 102951b0f370SBram Moolenaar " Looks like a pointer, also display what it points to. 103051b0f370SBram Moolenaar let s:ignoreEvalError = 1 103151b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 10321b9645deSBram Moolenaar else 103345d5f26dSBram Moolenaar let s:evalFromBalloonExpr = 0 103445d5f26dSBram Moolenaar endif 103551b0f370SBram Moolenaarendfunc 103651b0f370SBram Moolenaar 103751b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 1038b3307b5eSBram Moolenaar" if there is any. 1039396e829fSBram Moolenaarfunc TermDebugBalloonExpr() 1040b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 1041b3307b5eSBram Moolenaar return '' 1042b3307b5eSBram Moolenaar endif 1043b3307b5eSBram Moolenaar if !s:stopped 1044396e829fSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 104551b0f370SBram Moolenaar " mouse triggers a balloon. 104651b0f370SBram Moolenaar return '' 104751b0f370SBram Moolenaar endif 104822f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 1 104922f1d0e3SBram Moolenaar let s:evalFromBalloonExprResult = '' 105051b0f370SBram Moolenaar let s:ignoreEvalError = 1 105151b0f370SBram Moolenaar call s:SendEval(v:beval_text) 105251b0f370SBram Moolenaar return '' 105345d5f26dSBram Moolenaarendfunc 105445d5f26dSBram Moolenaar 105522f1d0e3SBram Moolenaar" Handle an error. 105651b0f370SBram Moolenaarfunc s:HandleError(msg) 105722f1d0e3SBram Moolenaar if s:ignoreEvalError 105822f1d0e3SBram Moolenaar " Result of s:SendEval() failed, ignore. 105951b0f370SBram Moolenaar let s:ignoreEvalError = 0 106051b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 106145d5f26dSBram Moolenaar return 106245d5f26dSBram Moolenaar endif 106345d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 1064b3307b5eSBram Moolenaarendfunc 1065b3307b5eSBram Moolenaar 1066c4b533e1SBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 1067b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 1068c4b533e1SBram Moolenaar new 1069c4b533e1SBram Moolenaar let s:sourcewin = win_getid(winnr()) 1070c4b533e1SBram Moolenaar call s:InstallWinbar() 1071c4b533e1SBram Moolenaar endif 107282be4849SBram Moolenaarendfunc 107382be4849SBram Moolenaar 107482be4849SBram Moolenaarfunc s:GotoAsmwinOrCreateIt() 107582be4849SBram Moolenaar if !win_gotoid(s:asmwin) 107682be4849SBram Moolenaar if win_gotoid(s:sourcewin) 107782be4849SBram Moolenaar exe 'rightbelow new' 107882be4849SBram Moolenaar else 107982be4849SBram Moolenaar exe 'new' 108082be4849SBram Moolenaar endif 108182be4849SBram Moolenaar 108282be4849SBram Moolenaar let s:asmwin = win_getid(winnr()) 108382be4849SBram Moolenaar 108482be4849SBram Moolenaar setlocal nowrap 108582be4849SBram Moolenaar setlocal number 108682be4849SBram Moolenaar setlocal noswapfile 108782be4849SBram Moolenaar setlocal buftype=nofile 108882be4849SBram Moolenaar 108982be4849SBram Moolenaar let asmbuf = bufnr('Termdebug-asm-listing') 109082be4849SBram Moolenaar if asmbuf > 0 109182be4849SBram Moolenaar exe 'buffer' . asmbuf 109282be4849SBram Moolenaar else 109382be4849SBram Moolenaar exe 'file Termdebug-asm-listing' 109482be4849SBram Moolenaar endif 109582be4849SBram Moolenaar 109682be4849SBram Moolenaar if exists('g:termdebug_disasm_window') 109782be4849SBram Moolenaar if g:termdebug_disasm_window > 1 109882be4849SBram Moolenaar exe 'resize ' . g:termdebug_disasm_window 109982be4849SBram Moolenaar endif 110082be4849SBram Moolenaar endif 110182be4849SBram Moolenaar endif 110282be4849SBram Moolenaar 110382be4849SBram Moolenaar if s:asm_addr != '' 110482be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 110582be4849SBram Moolenaar if lnum == 0 110682be4849SBram Moolenaar if s:stopped 110782be4849SBram Moolenaar call s:SendCommand('disassemble $pc') 110882be4849SBram Moolenaar endif 110982be4849SBram Moolenaar else 111082be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 111182be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 111282be4849SBram Moolenaar endif 111382be4849SBram Moolenaar endif 1114e09ba7baSBram Moolenaarendfunc 1115e09ba7baSBram Moolenaar 1116e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 1117fe386641SBram Moolenaar" Will update the sign that shows the current position. 1118fe386641SBram Moolenaarfunc s:HandleCursor(msg) 111960e73f2aSBram Moolenaar let wid = win_getid(winnr()) 11204551c0a9SBram Moolenaar 112160e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 112260e73f2aSBram Moolenaar call ch_log('program stopped') 11234551c0a9SBram Moolenaar let s:stopped = 1 112460e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 112560e73f2aSBram Moolenaar call ch_log('program running') 112660e73f2aSBram Moolenaar let s:stopped = 0 1127a15b0a93SBram Moolenaar endif 1128a15b0a93SBram Moolenaar 1129a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 1130a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 1131a15b0a93SBram Moolenaar else 113282be4849SBram Moolenaar let fname = '' 113382be4849SBram Moolenaar endif 113482be4849SBram Moolenaar 113582be4849SBram Moolenaar if a:msg =~ 'addr=' 113682be4849SBram Moolenaar let asm_addr = s:GetAsmAddr(a:msg) 113782be4849SBram Moolenaar if asm_addr != '' 113882be4849SBram Moolenaar let s:asm_addr = asm_addr 113982be4849SBram Moolenaar 114082be4849SBram Moolenaar let curwinid = win_getid(winnr()) 114182be4849SBram Moolenaar if win_gotoid(s:asmwin) 114282be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 114382be4849SBram Moolenaar if lnum == 0 114482be4849SBram Moolenaar call s:SendCommand('disassemble $pc') 114582be4849SBram Moolenaar else 114682be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 114782be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 114882be4849SBram Moolenaar endif 114982be4849SBram Moolenaar 115082be4849SBram Moolenaar call win_gotoid(curwinid) 115182be4849SBram Moolenaar endif 115282be4849SBram Moolenaar endif 11531b9645deSBram Moolenaar endif 1154e09ba7baSBram Moolenaar 1155fe386641SBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 11564551c0a9SBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 11571b9645deSBram Moolenaar if lnum =~ '^[0-9]*$' 1158fe386641SBram Moolenaar call s:GotoSourcewinOrCreateIt() 1159fe386641SBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 1160fe386641SBram Moolenaar if &modified 1161b3307b5eSBram Moolenaar " TODO: find existing window 1162c4b533e1SBram Moolenaar exe 'split ' . fnameescape(fname) 1163fe386641SBram Moolenaar let s:sourcewin = win_getid(winnr()) 1164fe386641SBram Moolenaar call s:InstallWinbar() 1165fe386641SBram Moolenaar else 1166fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 1167fe386641SBram Moolenaar endif 116801164a65SBram Moolenaar endif 116939f7aa3cSBram Moolenaar exe lnum 1170cb80aa2dSBram Moolenaar exe 'sign unplace ' . s:pc_id 1171cb80aa2dSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname 1172cb80aa2dSBram Moolenaar if !exists('b:save_signcolumn') 1173cb80aa2dSBram Moolenaar let b:save_signcolumn = &signcolumn 1174fe386641SBram Moolenaar call add(s:signcolumn_buflist, bufnr()) 1175fe386641SBram Moolenaar endif 11764551c0a9SBram Moolenaar setlocal signcolumn=yes 1177fe386641SBram Moolenaar endif 1178fe386641SBram Moolenaar elseif !s:stopped || fname != '' 1179fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 1180fe386641SBram Moolenaar endif 1181e09ba7baSBram Moolenaar 1182e09ba7baSBram Moolenaar call win_gotoid(wid) 1183de1a8314SBram Moolenaarendfunc 1184a15b0a93SBram Moolenaar 118537402ed5SBram Moolenaarlet s:BreakpointSigns = [] 118637402ed5SBram Moolenaar 118737402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 118837402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 118937402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 1190de1a8314SBram Moolenaar call add(s:BreakpointSigns, nr) 1191de1a8314SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 1192de1a8314SBram Moolenaar endif 119337402ed5SBram Moolenaarendfunc 119437402ed5SBram Moolenaar 11955378e1cfSBram Moolenaarfunc! s:SplitMsg(s) 11965378e1cfSBram Moolenaar return split(a:s, '{.\{-}}\zs') 1197e09ba7baSBram Moolenaarendfunction 1198e09ba7baSBram Moolenaar 1199e09ba7baSBram Moolenaar" Handle setting a breakpoint 12006dccc962SBram Moolenaar" Will update the sign that shows the breakpoint 12016dccc962SBram Moolenaarfunc s:HandleNewBreakpoint(msg) 12026dccc962SBram Moolenaar if a:msg !~ 'fullname=' 12036dccc962SBram Moolenaar " a watch does not have a file name 12045378e1cfSBram Moolenaar return 12055378e1cfSBram Moolenaar endif 12065378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 12075378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 12085378e1cfSBram Moolenaar if empty(fname) 12095378e1cfSBram Moolenaar continue 12105378e1cfSBram Moolenaar endif 1211e09ba7baSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 1212fe386641SBram Moolenaar if empty(nr) 1213e09ba7baSBram Moolenaar return 121437402ed5SBram Moolenaar endif 121537402ed5SBram Moolenaar 121637402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 121737402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 121837402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 121937402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 122037402ed5SBram Moolenaar 122137402ed5SBram Moolenaar if has_key(s:breakpoints, id) 122237402ed5SBram Moolenaar let entries = s:breakpoints[id] 122337402ed5SBram Moolenaar else 122437402ed5SBram Moolenaar let entries = {} 122537402ed5SBram Moolenaar let s:breakpoints[id] = entries 122637402ed5SBram Moolenaar endif 1227e09ba7baSBram Moolenaar if has_key(entries, subid) 1228e09ba7baSBram Moolenaar let entry = entries[subid] 122937402ed5SBram Moolenaar else 1230fe386641SBram Moolenaar let entry = {} 1231e09ba7baSBram Moolenaar let entries[subid] = entry 12325378e1cfSBram Moolenaar endif 1233e09ba7baSBram Moolenaar 1234e09ba7baSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 12351b9645deSBram Moolenaar let entry['fname'] = fname 123637402ed5SBram Moolenaar let entry['lnum'] = lnum 123737402ed5SBram Moolenaar 123837402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 123937402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 124037402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 124137402ed5SBram Moolenaar endif 12421b9645deSBram Moolenaar let s:breakpoint_locations[bploc] += [id] 124337402ed5SBram Moolenaar 12441b9645deSBram Moolenaar if bufloaded(fname) 12455378e1cfSBram Moolenaar call s:PlaceSign(id, subid, entry) 12461b9645deSBram Moolenaar endif 12471b9645deSBram Moolenaar endfor 124837402ed5SBram Moolenaarendfunc 124937402ed5SBram Moolenaar 12503132cdddSBram Moolenaarfunc s:PlaceSign(id, subid, entry) 12511b9645deSBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 1252e09ba7baSBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' priority=110 file=' . a:entry['fname'] 1253e09ba7baSBram Moolenaar let a:entry['placed'] = 1 1254e09ba7baSBram Moolenaarendfunc 1255e09ba7baSBram Moolenaar 1256e09ba7baSBram Moolenaar" Handle deleting a breakpoint 125737402ed5SBram Moolenaar" Will remove the sign that shows the breakpoint 125837402ed5SBram Moolenaarfunc s:HandleBreakpointDelete(msg) 1259e09ba7baSBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 1260e09ba7baSBram Moolenaar if empty(id) 126137402ed5SBram Moolenaar return 126237402ed5SBram Moolenaar endif 12631b9645deSBram Moolenaar if has_key(s:breakpoints, id) 126437402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 12651b9645deSBram Moolenaar if has_key(entry, 'placed') 12661b9645deSBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 12675378e1cfSBram Moolenaar unlet entry['placed'] 126837402ed5SBram Moolenaar endif 126937402ed5SBram Moolenaar endfor 1270c572da5fSBram Moolenaar unlet s:breakpoints[id] 12711b9645deSBram Moolenaar endif 12724551c0a9SBram Moolenaarendfunc 12734551c0a9SBram Moolenaar 12744551c0a9SBram Moolenaar" Handle the debugged program starting to run. 12754551c0a9SBram Moolenaar" Will store the process ID in s:pid 12764551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 12774551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 12784551c0a9SBram Moolenaar if nr == 0 12794551c0a9SBram Moolenaar return 12804551c0a9SBram Moolenaar endif 12814551c0a9SBram Moolenaar let s:pid = nr 12824551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 12831b9645deSBram Moolenaarendfunc 12841b9645deSBram Moolenaar 12851b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 128637402ed5SBram Moolenaarfunc s:BufRead() 128737402ed5SBram Moolenaar let fname = expand('<afile>:p') 12881b9645deSBram Moolenaar for [id, entries] in items(s:breakpoints) 128937402ed5SBram Moolenaar for [subid, entry] in items(entries) 12901b9645deSBram Moolenaar if entry['fname'] == fname 12911b9645deSBram Moolenaar call s:PlaceSign(id, subid, entry) 129237402ed5SBram Moolenaar endif 12931b9645deSBram Moolenaar endfor 12941b9645deSBram Moolenaar endfor 12951b9645deSBram Moolenaarendfunc 12961b9645deSBram Moolenaar 12971b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 129837402ed5SBram Moolenaarfunc s:BufUnloaded() 129937402ed5SBram Moolenaar let fname = expand('<afile>:p') 13001b9645deSBram Moolenaar for [id, entries] in items(s:breakpoints) 13011b9645deSBram Moolenaar for [subid, entry] in items(entries) 13021b9645deSBram Moolenaar if entry['fname'] == fname 13031b9645deSBram Moolenaar let entry['placed'] = 0 130437402ed5SBram Moolenaar endif 13051b9645deSBram Moolenaar endfor 1306ca4cc018SBram Moolenaar endfor 1307ca4cc018SBram Moolenaarendfunc 1308ca4cc018SBram Moolenaar 1309let &cpo = s:keepcpo 1310unlet s:keepcpo 1311