1fe386641SBram Moolenaar" Debugger plugin using gdb. 2c572da5fSBram Moolenaar" 3b3307b5eSBram Moolenaar" Author: Bram Moolenaar 4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license" 5*d2ea7cf1SBram Moolenaar" Last Change: 2021 May 18 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 124b3307b5eSBram Moolenaar " Uncomment this line to write logging in "debuglog". 125b3307b5eSBram Moolenaar " call ch_logfile('debuglog', 'w') 126b3307b5eSBram Moolenaar 127b3307b5eSBram Moolenaar let s:sourcewin = win_getid(winnr()) 128cb80aa2dSBram Moolenaar 129cb80aa2dSBram Moolenaar " Remember the old value of 'signcolumn' for each buffer that it's set in, so 130cb80aa2dSBram Moolenaar " that we can restore the value for all buffers. 131cb80aa2dSBram Moolenaar let b:save_signcolumn = &signcolumn 132cb80aa2dSBram Moolenaar let s:signcolumn_buflist = [bufnr()] 133fe386641SBram Moolenaar 13424a98a0eSBram Moolenaar let s:save_columns = 0 13568e6560bSBram Moolenaar let s:allleft = 0 13624a98a0eSBram Moolenaar if exists('g:termdebug_wide') 13724a98a0eSBram Moolenaar if &columns < g:termdebug_wide 13838baa3e6SBram Moolenaar let s:save_columns = &columns 13938baa3e6SBram Moolenaar let &columns = g:termdebug_wide 14068e6560bSBram Moolenaar " If we make the Vim window wider, use the whole left halve for the debug 14168e6560bSBram Moolenaar " windows. 14268e6560bSBram Moolenaar let s:allleft = 1 14324a98a0eSBram Moolenaar endif 144b3307b5eSBram Moolenaar let s:vertical = 1 14538baa3e6SBram Moolenaar else 146b3307b5eSBram Moolenaar let s:vertical = 0 14738baa3e6SBram Moolenaar endif 14838baa3e6SBram Moolenaar 149b3307b5eSBram Moolenaar " Override using a terminal window by setting g:termdebug_use_prompt to 1. 150b3307b5eSBram Moolenaar let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt 151b3307b5eSBram Moolenaar if has('terminal') && !has('win32') && !use_prompt 152b3307b5eSBram Moolenaar let s:way = 'terminal' 153b3307b5eSBram Moolenaar else 154b3307b5eSBram Moolenaar let s:way = 'prompt' 155b3307b5eSBram Moolenaar endif 156b3307b5eSBram Moolenaar 157b3307b5eSBram Moolenaar if s:way == 'prompt' 158b3307b5eSBram Moolenaar call s:StartDebug_prompt(a:dict) 159b3307b5eSBram Moolenaar else 160b3307b5eSBram Moolenaar call s:StartDebug_term(a:dict) 161b3307b5eSBram Moolenaar endif 16282be4849SBram Moolenaar 16382be4849SBram Moolenaar if exists('g:termdebug_disasm_window') 16482be4849SBram Moolenaar if g:termdebug_disasm_window 16582be4849SBram Moolenaar let curwinid = win_getid(winnr()) 16682be4849SBram Moolenaar call s:GotoAsmwinOrCreateIt() 16782be4849SBram Moolenaar call win_gotoid(curwinid) 16882be4849SBram Moolenaar endif 16982be4849SBram Moolenaar endif 170b3307b5eSBram Moolenaarendfunc 171b3307b5eSBram Moolenaar 172ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended. 173ef3c6a5bSBram Moolenaarfunc s:CloseBuffers() 174ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:ptybuf 175ef3c6a5bSBram Moolenaar exe 'bwipe! ' . s:commbuf 176ef3c6a5bSBram Moolenaar unlet! s:gdbwin 177ef3c6a5bSBram Moolenaarendfunc 178ef3c6a5bSBram Moolenaar 179*d2ea7cf1SBram Moolenaarfunc s:CheckGdbRunning() 180*d2ea7cf1SBram Moolenaar let gdbproc = term_getjob(s:gdbbuf) 181*d2ea7cf1SBram Moolenaar if gdbproc == v:null || job_status(gdbproc) !=# 'run' 182*d2ea7cf1SBram Moolenaar echoerr string(g:termdebugger) . ' exited unexpectedly' 183*d2ea7cf1SBram Moolenaar call s:CloseBuffers() 184*d2ea7cf1SBram Moolenaar return '' 185*d2ea7cf1SBram Moolenaar endif 186*d2ea7cf1SBram Moolenaar return 'ok' 187*d2ea7cf1SBram Moolenaarendfunc 188*d2ea7cf1SBram Moolenaar 189b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict) 190b3307b5eSBram Moolenaar " Open a terminal window without a job, to run the debugged program in. 191fe386641SBram Moolenaar let s:ptybuf = term_start('NONE', { 192b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 193b3307b5eSBram Moolenaar \ 'vertical': s:vertical, 194fe386641SBram Moolenaar \ }) 195fe386641SBram Moolenaar if s:ptybuf == 0 196fe386641SBram Moolenaar echoerr 'Failed to open the program terminal window' 197fe386641SBram Moolenaar return 198fe386641SBram Moolenaar endif 199fe386641SBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 20045d5f26dSBram Moolenaar let s:ptywin = win_getid(winnr()) 201b3307b5eSBram Moolenaar if s:vertical 20251b0f370SBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 20351b0f370SBram Moolenaar " columns for that, thus one less for the terminal window. 20451b0f370SBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 20568e6560bSBram Moolenaar if s:allleft 20668e6560bSBram Moolenaar " use the whole left column 20768e6560bSBram Moolenaar wincmd H 20868e6560bSBram Moolenaar endif 20951b0f370SBram Moolenaar endif 210fe386641SBram Moolenaar 211fe386641SBram Moolenaar " Create a hidden terminal window to communicate with gdb 212fe386641SBram Moolenaar let s:commbuf = term_start('NONE', { 213fe386641SBram Moolenaar \ 'term_name': 'gdb communication', 214fe386641SBram Moolenaar \ 'out_cb': function('s:CommOutput'), 215fe386641SBram Moolenaar \ 'hidden': 1, 216fe386641SBram Moolenaar \ }) 217fe386641SBram Moolenaar if s:commbuf == 0 218fe386641SBram Moolenaar echoerr 'Failed to open the communication terminal window' 219fe386641SBram Moolenaar exe 'bwipe! ' . s:ptybuf 220fe386641SBram Moolenaar return 221fe386641SBram Moolenaar endif 222fe386641SBram Moolenaar let commpty = job_info(term_getjob(s:commbuf))['tty_out'] 223c572da5fSBram Moolenaar 224c572da5fSBram Moolenaar " Open a terminal window to run the debugger. 225c3632516SBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 22632c67ba7SBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 22732c67ba7SBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 22832c67ba7SBram Moolenaar 229*d2ea7cf1SBram Moolenaar let cmd = [g:termdebugger, '-quiet', '-tty', pty, '--eval-command', 'echo startupdone\n'] + gdb_args 230b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 23160e73f2aSBram Moolenaar let s:gdbbuf = term_start(cmd, { 232fe386641SBram Moolenaar \ 'term_finish': 'close', 233c572da5fSBram Moolenaar \ }) 23460e73f2aSBram Moolenaar if s:gdbbuf == 0 235fe386641SBram Moolenaar echoerr 'Failed to open the gdb terminal window' 236ef3c6a5bSBram Moolenaar call s:CloseBuffers() 237fe386641SBram Moolenaar return 238fe386641SBram Moolenaar endif 23945d5f26dSBram Moolenaar let s:gdbwin = win_getid(winnr()) 240fe386641SBram Moolenaar 241*d2ea7cf1SBram Moolenaar " Wait for the "startupdone" message before sending any commands. 242*d2ea7cf1SBram Moolenaar let try_count = 0 243*d2ea7cf1SBram Moolenaar while 1 244*d2ea7cf1SBram Moolenaar if s:CheckGdbRunning() != 'ok' 245*d2ea7cf1SBram Moolenaar return 246*d2ea7cf1SBram Moolenaar endif 247*d2ea7cf1SBram Moolenaar 248*d2ea7cf1SBram Moolenaar for lnum in range(1, 200) 249*d2ea7cf1SBram Moolenaar if term_getline(s:gdbbuf, lnum) =~ 'startupdone' 250*d2ea7cf1SBram Moolenaar let try_count = 9999 251*d2ea7cf1SBram Moolenaar break 252*d2ea7cf1SBram Moolenaar endif 253*d2ea7cf1SBram Moolenaar endfor 254*d2ea7cf1SBram Moolenaar let try_count += 1 255*d2ea7cf1SBram Moolenaar if try_count > 300 256*d2ea7cf1SBram Moolenaar " done or give up after five seconds 257*d2ea7cf1SBram Moolenaar break 258*d2ea7cf1SBram Moolenaar endif 259*d2ea7cf1SBram Moolenaar sleep 10m 260*d2ea7cf1SBram Moolenaar endwhile 261*d2ea7cf1SBram Moolenaar 262*d2ea7cf1SBram Moolenaar " Set arguments to be run. 26332c67ba7SBram Moolenaar if len(proc_args) 26432c67ba7SBram Moolenaar call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r") 26532c67ba7SBram Moolenaar endif 26632c67ba7SBram Moolenaar 267fe386641SBram Moolenaar " Connect gdb to the communication pty, using the GDB/MI interface 26860e73f2aSBram Moolenaar call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r") 26960e73f2aSBram Moolenaar 2703e4b84d0SBram Moolenaar " Wait for the response to show up, users may not notice the error and wonder 2713e4b84d0SBram Moolenaar " why the debugger doesn't work. 2723e4b84d0SBram Moolenaar let try_count = 0 2733e4b84d0SBram Moolenaar while 1 274*d2ea7cf1SBram Moolenaar if s:CheckGdbRunning() != 'ok' 275ef3c6a5bSBram Moolenaar return 276ef3c6a5bSBram Moolenaar endif 277ef3c6a5bSBram Moolenaar 2783e4b84d0SBram Moolenaar let response = '' 279b3623a38SBram Moolenaar for lnum in range(1, 200) 28019c8fe19SBram Moolenaar let line1 = term_getline(s:gdbbuf, lnum) 28119c8fe19SBram Moolenaar let line2 = term_getline(s:gdbbuf, lnum + 1) 28219c8fe19SBram Moolenaar if line1 =~ 'new-ui mi ' 283f63db65bSBram Moolenaar " response can be in the same line or the next line 28419c8fe19SBram Moolenaar let response = line1 . line2 2853e4b84d0SBram Moolenaar if response =~ 'Undefined command' 286f3ba14ffSBram Moolenaar echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 287ef3c6a5bSBram Moolenaar call s:CloseBuffers() 2883e4b84d0SBram Moolenaar return 2893e4b84d0SBram Moolenaar endif 2903e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 2913e4b84d0SBram Moolenaar " Success! 2923e4b84d0SBram Moolenaar break 2933e4b84d0SBram Moolenaar endif 29419c8fe19SBram Moolenaar elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' 29519c8fe19SBram Moolenaar " Reading symbols might take a while, try more times 29606fe74aeSBram Moolenaar let try_count -= 1 29706fe74aeSBram Moolenaar endif 2983e4b84d0SBram Moolenaar endfor 2993e4b84d0SBram Moolenaar if response =~ 'New UI allocated' 3003e4b84d0SBram Moolenaar break 3013e4b84d0SBram Moolenaar endif 3023e4b84d0SBram Moolenaar let try_count += 1 3033e4b84d0SBram Moolenaar if try_count > 100 3043e4b84d0SBram Moolenaar echoerr 'Cannot check if your gdb works, continuing anyway' 3053e4b84d0SBram Moolenaar break 3063e4b84d0SBram Moolenaar endif 3073e4b84d0SBram Moolenaar sleep 10m 3083e4b84d0SBram Moolenaar endwhile 3093e4b84d0SBram Moolenaar 31091359014SBram Moolenaar " Interpret commands while the target is running. This should usually only be 31160e73f2aSBram Moolenaar " exec-interrupt, since many commands don't work properly while the target is 31260e73f2aSBram Moolenaar " running. 31360e73f2aSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 314b3307b5eSBram Moolenaar " Older gdb uses a different command. 315b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 316e09ba7baSBram Moolenaar 317f3ba14ffSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 318f3ba14ffSBram Moolenaar " "Type <return> to continue" prompt. 319b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 320f3ba14ffSBram Moolenaar 321*d2ea7cf1SBram Moolenaar call job_setoptions(term_getjob(s:gdbbuf), {'exit_cb': function('s:EndTermDebug')}) 322b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 323b3307b5eSBram Moolenaarendfunc 324b3307b5eSBram Moolenaar 325b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict) 326b3307b5eSBram Moolenaar " Open a window with a prompt buffer to run gdb in. 327b3307b5eSBram Moolenaar if s:vertical 328b3307b5eSBram Moolenaar vertical new 329b3307b5eSBram Moolenaar else 330b3307b5eSBram Moolenaar new 331b3307b5eSBram Moolenaar endif 332b3307b5eSBram Moolenaar let s:gdbwin = win_getid(winnr()) 333b3307b5eSBram Moolenaar let s:promptbuf = bufnr('') 334b3307b5eSBram Moolenaar call prompt_setprompt(s:promptbuf, 'gdb> ') 335b3307b5eSBram Moolenaar set buftype=prompt 336b3307b5eSBram Moolenaar file gdb 337b3307b5eSBram Moolenaar call prompt_setcallback(s:promptbuf, function('s:PromptCallback')) 338b3307b5eSBram Moolenaar call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt')) 339b3307b5eSBram Moolenaar 340b3307b5eSBram Moolenaar if s:vertical 341b3307b5eSBram Moolenaar " Assuming the source code window will get a signcolumn, use two more 342b3307b5eSBram Moolenaar " columns for that, thus one less for the terminal window. 343b3307b5eSBram Moolenaar exe (&columns / 2 - 1) . "wincmd |" 344b3307b5eSBram Moolenaar endif 345b3307b5eSBram Moolenaar 346b3307b5eSBram Moolenaar " Add -quiet to avoid the intro message causing a hit-enter prompt. 347b3307b5eSBram Moolenaar let gdb_args = get(a:dict, 'gdb_args', []) 348b3307b5eSBram Moolenaar let proc_args = get(a:dict, 'proc_args', []) 349b3307b5eSBram Moolenaar 350b3307b5eSBram Moolenaar let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 351b3307b5eSBram Moolenaar call ch_log('executing "' . join(cmd) . '"') 352b3307b5eSBram Moolenaar 353b3307b5eSBram Moolenaar let s:gdbjob = job_start(cmd, { 354b3307b5eSBram Moolenaar \ 'exit_cb': function('s:EndPromptDebug'), 355b3307b5eSBram Moolenaar \ 'out_cb': function('s:GdbOutCallback'), 356b3307b5eSBram Moolenaar \ }) 357b3307b5eSBram Moolenaar if job_status(s:gdbjob) != "run" 358b3307b5eSBram Moolenaar echoerr 'Failed to start gdb' 359b3307b5eSBram Moolenaar exe 'bwipe! ' . s:promptbuf 360b3307b5eSBram Moolenaar return 361b3307b5eSBram Moolenaar endif 3624551c0a9SBram Moolenaar " Mark the buffer modified so that it's not easy to close. 3634551c0a9SBram Moolenaar set modified 364b3307b5eSBram Moolenaar let s:gdb_channel = job_getchannel(s:gdbjob) 365b3307b5eSBram Moolenaar 36691359014SBram Moolenaar " Interpret commands while the target is running. This should usually only 367b3307b5eSBram Moolenaar " be exec-interrupt, since many commands don't work properly while the 368b3307b5eSBram Moolenaar " target is running. 369b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set mi-async on') 370b3307b5eSBram Moolenaar " Older gdb uses a different command. 371b3307b5eSBram Moolenaar call s:SendCommand('-gdb-set target-async on') 372b3307b5eSBram Moolenaar 373b3307b5eSBram Moolenaar let s:ptybuf = 0 374b3307b5eSBram Moolenaar if has('win32') 375b3307b5eSBram Moolenaar " MS-Windows: run in a new console window for maximum compatibility 376b3307b5eSBram Moolenaar call s:SendCommand('set new-console on') 377b3307b5eSBram Moolenaar elseif has('terminal') 378b3307b5eSBram Moolenaar " Unix: Run the debugged program in a terminal window. Open it below the 379b3307b5eSBram Moolenaar " gdb window. 380b3307b5eSBram Moolenaar belowright let s:ptybuf = term_start('NONE', { 381b3307b5eSBram Moolenaar \ 'term_name': 'debugged program', 382b3307b5eSBram Moolenaar \ }) 383b3307b5eSBram Moolenaar if s:ptybuf == 0 384b3307b5eSBram Moolenaar echoerr 'Failed to open the program terminal window' 385b3307b5eSBram Moolenaar call job_stop(s:gdbjob) 386b3307b5eSBram Moolenaar return 387b3307b5eSBram Moolenaar endif 388b3307b5eSBram Moolenaar let s:ptywin = win_getid(winnr()) 389b3307b5eSBram Moolenaar let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 390b3307b5eSBram Moolenaar call s:SendCommand('tty ' . pty) 391b3307b5eSBram Moolenaar 392b3307b5eSBram Moolenaar " Since GDB runs in a prompt window, the environment has not been set to 393b3307b5eSBram Moolenaar " match a terminal window, need to do that now. 394b3307b5eSBram Moolenaar call s:SendCommand('set env TERM = xterm-color') 395b3307b5eSBram Moolenaar call s:SendCommand('set env ROWS = ' . winheight(s:ptywin)) 396b3307b5eSBram Moolenaar call s:SendCommand('set env LINES = ' . winheight(s:ptywin)) 397b3307b5eSBram Moolenaar call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin)) 398b3307b5eSBram Moolenaar call s:SendCommand('set env COLORS = ' . &t_Co) 399b3307b5eSBram Moolenaar call s:SendCommand('set env VIM_TERMINAL = ' . v:version) 400b3307b5eSBram Moolenaar else 401b3307b5eSBram Moolenaar " TODO: open a new terminal get get the tty name, pass on to gdb 402b3307b5eSBram Moolenaar call s:SendCommand('show inferior-tty') 403b3307b5eSBram Moolenaar endif 404b3307b5eSBram Moolenaar call s:SendCommand('set print pretty on') 405b3307b5eSBram Moolenaar call s:SendCommand('set breakpoint pending on') 406b3307b5eSBram Moolenaar " Disable pagination, it causes everything to stop at the gdb 407b3307b5eSBram Moolenaar call s:SendCommand('set pagination off') 408b3307b5eSBram Moolenaar 409b3307b5eSBram Moolenaar " Set arguments to be run 410b3307b5eSBram Moolenaar if len(proc_args) 411b3307b5eSBram Moolenaar call s:SendCommand('set args ' . join(proc_args)) 412b3307b5eSBram Moolenaar endif 413b3307b5eSBram Moolenaar 414b3307b5eSBram Moolenaar call s:StartDebugCommon(a:dict) 415b3307b5eSBram Moolenaar startinsert 416b3307b5eSBram Moolenaarendfunc 417b3307b5eSBram Moolenaar 418b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict) 41938baa3e6SBram Moolenaar " Sign used to highlight the line where the program has stopped. 42038baa3e6SBram Moolenaar " There can be only one. 42138baa3e6SBram Moolenaar sign define debugPC linehl=debugPC 42238baa3e6SBram Moolenaar 42345d5f26dSBram Moolenaar " Install debugger commands in the text window. 424b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 425e09ba7baSBram Moolenaar call s:InstallCommands() 42645d5f26dSBram Moolenaar call win_gotoid(s:gdbwin) 427e09ba7baSBram Moolenaar 42851b0f370SBram Moolenaar " Enable showing a balloon with eval info 429246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 430246fe03dSBram Moolenaar set balloonexpr=TermDebugBalloonExpr() 43151b0f370SBram Moolenaar if has("balloon_eval") 43251b0f370SBram Moolenaar set ballooneval 433246fe03dSBram Moolenaar endif 43451b0f370SBram Moolenaar if has("balloon_eval_term") 43551b0f370SBram Moolenaar set balloonevalterm 43651b0f370SBram Moolenaar endif 43751b0f370SBram Moolenaar endif 43851b0f370SBram Moolenaar 4395378e1cfSBram Moolenaar " Contains breakpoints that have been placed, key is a string with the GDB 4405378e1cfSBram Moolenaar " breakpoint number. 44137402ed5SBram Moolenaar " Each entry is a dict, containing the sub-breakpoints. Key is the subid. 44237402ed5SBram Moolenaar " For a breakpoint that is just a number the subid is zero. 44337402ed5SBram Moolenaar " For a breakpoint "123.4" the id is "123" and subid is "4". 44437402ed5SBram Moolenaar " Example, when breakpoint "44", "123", "123.1" and "123.2" exist: 44537402ed5SBram Moolenaar " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} 446e09ba7baSBram Moolenaar let s:breakpoints = {} 4471b9645deSBram Moolenaar 44837402ed5SBram Moolenaar " Contains breakpoints by file/lnum. The key is "fname:lnum". 44937402ed5SBram Moolenaar " Each entry is a list of breakpoint IDs at that position. 45037402ed5SBram Moolenaar let s:breakpoint_locations = {} 45137402ed5SBram Moolenaar 4521b9645deSBram Moolenaar augroup TermDebug 4531b9645deSBram Moolenaar au BufRead * call s:BufRead() 4541b9645deSBram Moolenaar au BufUnload * call s:BufUnloaded() 455f07f9e73SBram Moolenaar au OptionSet background call s:Highlight(0, v:option_old, v:option_new) 4561b9645deSBram Moolenaar augroup END 45732c67ba7SBram Moolenaar 458b3307b5eSBram Moolenaar " Run the command if the bang attribute was given and got to the debug 459b3307b5eSBram Moolenaar " window. 46032c67ba7SBram Moolenaar if get(a:dict, 'bang', 0) 46132c67ba7SBram Moolenaar call s:SendCommand('-exec-run') 46232c67ba7SBram Moolenaar call win_gotoid(s:ptywin) 46332c67ba7SBram Moolenaar endif 464c572da5fSBram Moolenaarendfunc 465c572da5fSBram Moolenaar 466b3307b5eSBram Moolenaar" Send a command to gdb. "cmd" is the string without line terminator. 467b3307b5eSBram Moolenaarfunc s:SendCommand(cmd) 468b3307b5eSBram Moolenaar call ch_log('sending to gdb: ' . a:cmd) 469b3307b5eSBram Moolenaar if s:way == 'prompt' 470b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 471b3307b5eSBram Moolenaar else 472b3307b5eSBram Moolenaar call term_sendkeys(s:commbuf, a:cmd . "\r") 473b3307b5eSBram Moolenaar endif 474b3307b5eSBram Moolenaarendfunc 475b3307b5eSBram Moolenaar 476b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this. 477b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd) 478b3307b5eSBram Moolenaar if s:way == 'prompt' 479b3307b5eSBram Moolenaar call ch_sendraw(s:gdb_channel, a:cmd . "\n") 480b3307b5eSBram Moolenaar else 481b3307b5eSBram Moolenaar let do_continue = 0 482b3307b5eSBram Moolenaar if !s:stopped 483b3307b5eSBram Moolenaar let do_continue = 1 484b3307b5eSBram Moolenaar call s:SendCommand('-exec-interrupt') 485b3307b5eSBram Moolenaar sleep 10m 486b3307b5eSBram Moolenaar endif 487b3307b5eSBram Moolenaar call term_sendkeys(s:gdbbuf, a:cmd . "\r") 488b3307b5eSBram Moolenaar if do_continue 489b3307b5eSBram Moolenaar Continue 490b3307b5eSBram Moolenaar endif 491b3307b5eSBram Moolenaar endif 492b3307b5eSBram Moolenaarendfunc 493b3307b5eSBram Moolenaar 494b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer. 495b3307b5eSBram Moolenaarfunc s:PromptCallback(text) 496b3307b5eSBram Moolenaar call s:SendCommand(a:text) 497b3307b5eSBram Moolenaarendfunc 498b3307b5eSBram Moolenaar 4994551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a 5004551c0a9SBram Moolenaar" breakpoint. 501b3307b5eSBram Moolenaarfunc s:PromptInterrupt() 5022ed890f1SBram Moolenaar call ch_log('Interrupting gdb') 5032ed890f1SBram Moolenaar if has('win32') 5042ed890f1SBram Moolenaar " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to 5052ed890f1SBram Moolenaar " the debugger program so that gdb responds again. 5064551c0a9SBram Moolenaar if s:pid == 0 5074551c0a9SBram Moolenaar echoerr 'Cannot interrupt gdb, did not find a process ID' 5084551c0a9SBram Moolenaar else 5094551c0a9SBram Moolenaar call debugbreak(s:pid) 5104551c0a9SBram Moolenaar endif 5112ed890f1SBram Moolenaar else 5122ed890f1SBram Moolenaar call job_stop(s:gdbjob, 'int') 5132ed890f1SBram Moolenaar endif 514b3307b5eSBram Moolenaarendfunc 515b3307b5eSBram Moolenaar 516b3307b5eSBram Moolenaar" Function called when gdb outputs text. 517b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text) 518b3307b5eSBram Moolenaar call ch_log('received from gdb: ' . a:text) 519b3307b5eSBram Moolenaar 520b3307b5eSBram Moolenaar " Drop the gdb prompt, we have our own. 521b3307b5eSBram Moolenaar " Drop status and echo'd commands. 522a15b0a93SBram Moolenaar if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&' 523b3307b5eSBram Moolenaar return 524b3307b5eSBram Moolenaar endif 525b3307b5eSBram Moolenaar if a:text =~ '^^error,msg=' 526b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[11:]) 527b3307b5eSBram Moolenaar if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context' 528b3307b5eSBram Moolenaar " Silently drop evaluation errors. 529b3307b5eSBram Moolenaar unlet s:evalexpr 530b3307b5eSBram Moolenaar return 531b3307b5eSBram Moolenaar endif 532b3307b5eSBram Moolenaar elseif a:text[0] == '~' 533b3307b5eSBram Moolenaar let text = s:DecodeMessage(a:text[1:]) 534b3307b5eSBram Moolenaar else 535b3307b5eSBram Moolenaar call s:CommOutput(a:channel, a:text) 536b3307b5eSBram Moolenaar return 537b3307b5eSBram Moolenaar endif 538b3307b5eSBram Moolenaar 539b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 540b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 541b3307b5eSBram Moolenaar 542b3307b5eSBram Moolenaar " Add the output above the current prompt. 543b3307b5eSBram Moolenaar call append(line('$') - 1, text) 5444551c0a9SBram Moolenaar set modified 545b3307b5eSBram Moolenaar 546b3307b5eSBram Moolenaar call win_gotoid(curwinid) 547b3307b5eSBram Moolenaarendfunc 548b3307b5eSBram Moolenaar 549b3307b5eSBram Moolenaar" Decode a message from gdb. quotedText starts with a ", return the text up 550b3307b5eSBram Moolenaar" to the next ", unescaping characters. 551b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText) 552b3307b5eSBram Moolenaar if a:quotedText[0] != '"' 553a15b0a93SBram Moolenaar echoerr 'DecodeMessage(): missing quote in ' . a:quotedText 554b3307b5eSBram Moolenaar return 555b3307b5eSBram Moolenaar endif 556b3307b5eSBram Moolenaar let result = '' 557b3307b5eSBram Moolenaar let i = 1 558b3307b5eSBram Moolenaar while a:quotedText[i] != '"' && i < len(a:quotedText) 559b3307b5eSBram Moolenaar if a:quotedText[i] == '\' 560b3307b5eSBram Moolenaar let i += 1 561b3307b5eSBram Moolenaar if a:quotedText[i] == 'n' 562b3307b5eSBram Moolenaar " drop \n 563b3307b5eSBram Moolenaar let i += 1 564b3307b5eSBram Moolenaar continue 565589edb34SBram Moolenaar elseif a:quotedText[i] == 't' 566589edb34SBram Moolenaar " append \t 567589edb34SBram Moolenaar let i += 1 568589edb34SBram Moolenaar let result .= "\t" 569589edb34SBram Moolenaar continue 570b3307b5eSBram Moolenaar endif 571b3307b5eSBram Moolenaar endif 572b3307b5eSBram Moolenaar let result .= a:quotedText[i] 573b3307b5eSBram Moolenaar let i += 1 574b3307b5eSBram Moolenaar endwhile 575b3307b5eSBram Moolenaar return result 576b3307b5eSBram Moolenaarendfunc 577b3307b5eSBram Moolenaar 578a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name". 579a15b0a93SBram Moolenaarfunc s:GetFullname(msg) 5805378e1cfSBram Moolenaar if a:msg !~ 'fullname' 5815378e1cfSBram Moolenaar return '' 5825378e1cfSBram Moolenaar endif 583a15b0a93SBram Moolenaar let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 584a15b0a93SBram Moolenaar if has('win32') && name =~ ':\\\\' 585a15b0a93SBram Moolenaar " sometimes the name arrives double-escaped 586a15b0a93SBram Moolenaar let name = substitute(name, '\\\\', '\\', 'g') 587a15b0a93SBram Moolenaar endif 588a15b0a93SBram Moolenaar return name 589a15b0a93SBram Moolenaarendfunc 590a15b0a93SBram Moolenaar 59182be4849SBram Moolenaar" Extract the "addr" value from a gdb message with addr="0x0001234". 59282be4849SBram Moolenaarfunc s:GetAsmAddr(msg) 59382be4849SBram Moolenaar if a:msg !~ 'addr=' 59482be4849SBram Moolenaar return '' 59582be4849SBram Moolenaar endif 59682be4849SBram Moolenaar let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', '')) 59782be4849SBram Moolenaar return addr 59882be4849SBram Moolenaarendfunc 599b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status) 600fe386641SBram Moolenaar exe 'bwipe! ' . s:commbuf 601b3623a38SBram Moolenaar unlet s:gdbwin 602e09ba7baSBram Moolenaar 603b3307b5eSBram Moolenaar call s:EndDebugCommon() 604b3307b5eSBram Moolenaarendfunc 605b3307b5eSBram Moolenaar 606b3307b5eSBram Moolenaarfunc s:EndDebugCommon() 607e09ba7baSBram Moolenaar let curwinid = win_getid(winnr()) 608e09ba7baSBram Moolenaar 609b3307b5eSBram Moolenaar if exists('s:ptybuf') && s:ptybuf 610b3307b5eSBram Moolenaar exe 'bwipe! ' . s:ptybuf 611b3307b5eSBram Moolenaar endif 612b3307b5eSBram Moolenaar 613cb80aa2dSBram Moolenaar " Restore 'signcolumn' in all buffers for which it was set. 614b3307b5eSBram Moolenaar call win_gotoid(s:sourcewin) 615cb80aa2dSBram Moolenaar let was_buf = bufnr() 616cb80aa2dSBram Moolenaar for bufnr in s:signcolumn_buflist 617cb80aa2dSBram Moolenaar if bufexists(bufnr) 618cb80aa2dSBram Moolenaar exe bufnr .. "buf" 619cb80aa2dSBram Moolenaar if exists('b:save_signcolumn') 620cb80aa2dSBram Moolenaar let &signcolumn = b:save_signcolumn 621cb80aa2dSBram Moolenaar unlet b:save_signcolumn 622cb80aa2dSBram Moolenaar endif 623cb80aa2dSBram Moolenaar endif 624cb80aa2dSBram Moolenaar endfor 625cb80aa2dSBram Moolenaar exe was_buf .. "buf" 626cb80aa2dSBram Moolenaar 627e09ba7baSBram Moolenaar call s:DeleteCommands() 628e09ba7baSBram Moolenaar 629e09ba7baSBram Moolenaar call win_gotoid(curwinid) 630b3307b5eSBram Moolenaar 63138baa3e6SBram Moolenaar if s:save_columns > 0 63238baa3e6SBram Moolenaar let &columns = s:save_columns 63338baa3e6SBram Moolenaar endif 6341b9645deSBram Moolenaar 635246fe03dSBram Moolenaar if has("balloon_eval") || has("balloon_eval_term") 636246fe03dSBram Moolenaar set balloonexpr= 63751b0f370SBram Moolenaar if has("balloon_eval") 63851b0f370SBram Moolenaar set noballooneval 639246fe03dSBram Moolenaar endif 64051b0f370SBram Moolenaar if has("balloon_eval_term") 64151b0f370SBram Moolenaar set noballoonevalterm 64251b0f370SBram Moolenaar endif 64351b0f370SBram Moolenaar endif 64451b0f370SBram Moolenaar 6451b9645deSBram Moolenaar au! TermDebug 646fe386641SBram Moolenaarendfunc 647fe386641SBram Moolenaar 648b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status) 649b3307b5eSBram Moolenaar let curwinid = win_getid(winnr()) 650b3307b5eSBram Moolenaar call win_gotoid(s:gdbwin) 6514551c0a9SBram Moolenaar set nomodified 652b3307b5eSBram Moolenaar close 653b3307b5eSBram Moolenaar if curwinid != s:gdbwin 654b3307b5eSBram Moolenaar call win_gotoid(curwinid) 655b3307b5eSBram Moolenaar endif 656b3307b5eSBram Moolenaar 657b3307b5eSBram Moolenaar call s:EndDebugCommon() 658b3307b5eSBram Moolenaar unlet s:gdbwin 659b3307b5eSBram Moolenaar call ch_log("Returning from EndPromptDebug()") 660b3307b5eSBram Moolenaarendfunc 661b3307b5eSBram Moolenaar 66282be4849SBram Moolenaar" Disassembly window - added by Michael Sartain 66382be4849SBram Moolenaar" 66482be4849SBram Moolenaar" - CommOutput: disassemble $pc 66582be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n" 66682be4849SBram Moolenaar" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n" 66782be4849SBram Moolenaar" - CommOutput: ~" 0x0000555556466f69 <+0>:\tpush rbp\n" 66882be4849SBram Moolenaar" ... 66982be4849SBram Moolenaar" - CommOutput: ~" 0x0000555556467cd0:\tpop rbp\n" 67082be4849SBram Moolenaar" - CommOutput: ~" 0x0000555556467cd1:\tret \n" 67182be4849SBram Moolenaar" - CommOutput: ~"End of assembler dump.\n" 67282be4849SBram Moolenaar" - CommOutput: ^done 67382be4849SBram Moolenaar 67482be4849SBram Moolenaar" - CommOutput: disassemble $pc 67582be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n" 67682be4849SBram Moolenaar" - CommOutput: &"No function contains specified address.\n" 67782be4849SBram Moolenaar" - CommOutput: ^error,msg="No function contains specified address." 67882be4849SBram Moolenaarfunc s:HandleDisasmMsg(msg) 67982be4849SBram Moolenaar if a:msg =~ '^\^done' 68082be4849SBram Moolenaar let curwinid = win_getid(winnr()) 68182be4849SBram Moolenaar if win_gotoid(s:asmwin) 68282be4849SBram Moolenaar silent normal! gg0"_dG 68382be4849SBram Moolenaar call setline(1, s:asm_lines) 68482be4849SBram Moolenaar set nomodified 68582be4849SBram Moolenaar set filetype=asm 68682be4849SBram Moolenaar 68782be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 68882be4849SBram Moolenaar if lnum != 0 68982be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 69082be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 69182be4849SBram Moolenaar endif 69282be4849SBram Moolenaar 69382be4849SBram Moolenaar call win_gotoid(curwinid) 69482be4849SBram Moolenaar endif 69582be4849SBram Moolenaar 69682be4849SBram Moolenaar let s:parsing_disasm_msg = 0 69782be4849SBram Moolenaar let s:asm_lines = [] 69882be4849SBram Moolenaar elseif a:msg =~ '^\^error,msg=' 69982be4849SBram Moolenaar if s:parsing_disasm_msg == 1 70082be4849SBram Moolenaar " Disassemble call ran into an error. This can happen when gdb can't 70182be4849SBram Moolenaar " find the function frame address, so let's try to disassemble starting 70282be4849SBram Moolenaar " at current PC 70382be4849SBram Moolenaar call s:SendCommand('disassemble $pc,+100') 70482be4849SBram Moolenaar endif 70582be4849SBram Moolenaar let s:parsing_disasm_msg = 0 70682be4849SBram Moolenaar elseif a:msg =~ '\&\"disassemble \$pc' 70782be4849SBram Moolenaar if a:msg =~ '+100' 70882be4849SBram Moolenaar " This is our second disasm attempt 70982be4849SBram Moolenaar let s:parsing_disasm_msg = 2 71082be4849SBram Moolenaar endif 71182be4849SBram Moolenaar else 71282be4849SBram Moolenaar let value = substitute(a:msg, '^\~\"[ ]*', '', '') 71382be4849SBram Moolenaar let value = substitute(value, '^=>[ ]*', '', '') 71482be4849SBram Moolenaar let value = substitute(value, '\\n\" 71582be4849SBram Moolenaar$', '', '') 71682be4849SBram Moolenaar let value = substitute(value, '\\n\"$', '', '') 71782be4849SBram Moolenaar let value = substitute(value, ' 71882be4849SBram Moolenaar', '', '') 71982be4849SBram Moolenaar let value = substitute(value, '\\t', ' ', 'g') 72082be4849SBram Moolenaar 72182be4849SBram Moolenaar if value != '' || !empty(s:asm_lines) 72282be4849SBram Moolenaar call add(s:asm_lines, value) 72382be4849SBram Moolenaar endif 72482be4849SBram Moolenaar endif 725fe386641SBram Moolenaarendfunc 726fe386641SBram Moolenaar 727fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface. 728fe386641SBram Moolenaarfunc s:CommOutput(chan, msg) 729fe386641SBram Moolenaar let msgs = split(a:msg, "\r") 730fe386641SBram Moolenaar 731fe386641SBram Moolenaar for msg in msgs 732fe386641SBram Moolenaar " remove prefixed NL 733fe386641SBram Moolenaar if msg[0] == "\n" 73482be4849SBram Moolenaar let msg = msg[1:] 73582be4849SBram Moolenaar endif 73682be4849SBram Moolenaar 73782be4849SBram Moolenaar if s:parsing_disasm_msg 7381b9645deSBram Moolenaar call s:HandleDisasmMsg(msg) 739e09ba7baSBram Moolenaar elseif msg != '' 74045d5f26dSBram Moolenaar if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 741e09ba7baSBram Moolenaar call s:HandleCursor(msg) 742e09ba7baSBram Moolenaar elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 743e09ba7baSBram Moolenaar call s:HandleNewBreakpoint(msg) 7444551c0a9SBram Moolenaar elseif msg =~ '^=breakpoint-deleted,' 7454551c0a9SBram Moolenaar call s:HandleBreakpointDelete(msg) 74645d5f26dSBram Moolenaar elseif msg =~ '^=thread-group-started' 74745d5f26dSBram Moolenaar call s:HandleProgramRun(msg) 74845d5f26dSBram Moolenaar elseif msg =~ '^\^done,value=' 74945d5f26dSBram Moolenaar call s:HandleEvaluate(msg) 75082be4849SBram Moolenaar elseif msg =~ '^\^error,msg=' 75182be4849SBram Moolenaar call s:HandleError(msg) 75282be4849SBram Moolenaar elseif msg =~ '^disassemble' 753e09ba7baSBram Moolenaar let s:parsing_disasm_msg = 1 754e09ba7baSBram Moolenaar let s:asm_lines = [] 755e09ba7baSBram Moolenaar endif 756e09ba7baSBram Moolenaar endif 757e09ba7baSBram Moolenaar endfor 758589edb34SBram Moolenaarendfunc 759589edb34SBram Moolenaar 760589edb34SBram Moolenaarfunc s:GotoProgram() 761589edb34SBram Moolenaar if has('win32') 762589edb34SBram Moolenaar if executable('powershell') 763589edb34SBram Moolenaar call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid)) 764469bdbdeSBram Moolenaar endif 765589edb34SBram Moolenaar else 766589edb34SBram Moolenaar call win_gotoid(s:ptywin) 767589edb34SBram Moolenaar endif 768e09ba7baSBram Moolenaarendfunc 769e09ba7baSBram Moolenaar 770963c1ad5SBram Moolenaar" Install commands in the current window to control the debugger. 771963c1ad5SBram Moolenaarfunc s:InstallCommands() 772963c1ad5SBram Moolenaar let save_cpo = &cpo 773589edb34SBram Moolenaar set cpo&vim 77471137fedSBram Moolenaar 775e09ba7baSBram Moolenaar command -nargs=? Break call s:SetBreakpoint(<q-args>) 77645d5f26dSBram Moolenaar command Clear call s:ClearBreakpoint() 777e09ba7baSBram Moolenaar command Step call s:SendCommand('-exec-step') 77860e73f2aSBram Moolenaar command Over call s:SendCommand('-exec-next') 77960e73f2aSBram Moolenaar command Finish call s:SendCommand('-exec-finish') 78060e73f2aSBram Moolenaar command -nargs=* Run call s:Run(<q-args>) 781b3307b5eSBram Moolenaar command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>) 782b3307b5eSBram Moolenaar command Stop call s:SendCommand('-exec-interrupt') 783b3307b5eSBram Moolenaar 784b3307b5eSBram Moolenaar " using -exec-continue results in CTRL-C in gdb window not working 785b3307b5eSBram Moolenaar if s:way == 'prompt' 786b3307b5eSBram Moolenaar command Continue call s:SendCommand('continue') 787b3307b5eSBram Moolenaar else 788b3307b5eSBram Moolenaar command Continue call term_sendkeys(s:gdbbuf, "continue\r") 78945d5f26dSBram Moolenaar endif 79045d5f26dSBram Moolenaar 791589edb34SBram Moolenaar command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>) 792b3307b5eSBram Moolenaar command Gdb call win_gotoid(s:gdbwin) 79382be4849SBram Moolenaar command Program call s:GotoProgram() 79471137fedSBram Moolenaar command Source call s:GotoSourcewinOrCreateIt() 79545d5f26dSBram Moolenaar command Asm call s:GotoAsmwinOrCreateIt() 796388a5d4fSBram Moolenaar command Winbar call s:InstallWinbar() 797388a5d4fSBram Moolenaar 79845d5f26dSBram Moolenaar if !exists('g:termdebug_map_K') || g:termdebug_map_K 799388a5d4fSBram Moolenaar let s:k_map_saved = maparg('K', 'n', 0, 1) 8001b9645deSBram Moolenaar nnoremap K :Evaluate<CR> 801f0b03c4eSBram Moolenaar endif 80271137fedSBram Moolenaar 80371137fedSBram Moolenaar if has('menu') && &mouse != '' 80471137fedSBram Moolenaar call s:InstallWinbar() 80571137fedSBram Moolenaar 80671137fedSBram Moolenaar if !exists('g:termdebug_popup') || g:termdebug_popup != 0 80771137fedSBram Moolenaar let s:saved_mousemodel = &mousemodel 80871137fedSBram Moolenaar let &mousemodel = 'popup_setpos' 80971137fedSBram Moolenaar an 1.200 PopUp.-SEP3- <Nop> 81071137fedSBram Moolenaar an 1.210 PopUp.Set\ breakpoint :Break<CR> 81171137fedSBram Moolenaar an 1.220 PopUp.Clear\ breakpoint :Clear<CR> 81271137fedSBram Moolenaar an 1.230 PopUp.Evaluate :Evaluate<CR> 813963c1ad5SBram Moolenaar endif 814963c1ad5SBram Moolenaar endif 81571137fedSBram Moolenaar 81671137fedSBram Moolenaar let &cpo = save_cpo 81771137fedSBram Moolenaarendfunc 81871137fedSBram Moolenaar 81971137fedSBram Moolenaarlet s:winbar_winids = [] 82071137fedSBram Moolenaar 821c4b533e1SBram Moolenaar" Install the window toolbar in the current window. 82224a98a0eSBram Moolenaarfunc s:InstallWinbar() 82324a98a0eSBram Moolenaar if has('menu') && &mouse != '' 82424a98a0eSBram Moolenaar nnoremenu WinBar.Step :Step<CR> 82524a98a0eSBram Moolenaar nnoremenu WinBar.Next :Over<CR> 82660e73f2aSBram Moolenaar nnoremenu WinBar.Finish :Finish<CR> 82724a98a0eSBram Moolenaar nnoremenu WinBar.Cont :Continue<CR> 82871137fedSBram Moolenaar nnoremenu WinBar.Stop :Stop<CR> 829c4b533e1SBram Moolenaar nnoremenu WinBar.Eval :Evaluate<CR> 830e09ba7baSBram Moolenaar call add(s:winbar_winids, win_getid(winnr())) 831e09ba7baSBram Moolenaar endif 832e09ba7baSBram Moolenaarendfunc 833e09ba7baSBram Moolenaar 834e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window. 83571137fedSBram Moolenaarfunc s:DeleteCommands() 836e09ba7baSBram Moolenaar delcommand Break 83745d5f26dSBram Moolenaar delcommand Clear 838e09ba7baSBram Moolenaar delcommand Step 83960e73f2aSBram Moolenaar delcommand Over 84060e73f2aSBram Moolenaar delcommand Finish 84160e73f2aSBram Moolenaar delcommand Run 842e09ba7baSBram Moolenaar delcommand Arguments 84345d5f26dSBram Moolenaar delcommand Stop 84445d5f26dSBram Moolenaar delcommand Continue 84545d5f26dSBram Moolenaar delcommand Evaluate 846b3623a38SBram Moolenaar delcommand Gdb 84782be4849SBram Moolenaar delcommand Program 84871137fedSBram Moolenaar delcommand Source 84945d5f26dSBram Moolenaar delcommand Asm 8501b884a00SBram Moolenaar delcommand Winbar 8511b884a00SBram Moolenaar 8521b884a00SBram Moolenaar if exists('s:k_map_saved') 8531b884a00SBram Moolenaar if empty(s:k_map_saved) 854388a5d4fSBram Moolenaar nunmap K 8551b884a00SBram Moolenaar else 856388a5d4fSBram Moolenaar call mapset('n', 0, s:k_map_saved) 857388a5d4fSBram Moolenaar endif 8581b9645deSBram Moolenaar unlet s:k_map_saved 8591b9645deSBram Moolenaar endif 86071137fedSBram Moolenaar 86171137fedSBram Moolenaar if has('menu') 86271137fedSBram Moolenaar " Remove the WinBar entries from all windows where it was added. 86371137fedSBram Moolenaar let curwinid = win_getid(winnr()) 8641b9645deSBram Moolenaar for winid in s:winbar_winids 8651b9645deSBram Moolenaar if win_gotoid(winid) 8661b9645deSBram Moolenaar aunmenu WinBar.Step 8671b9645deSBram Moolenaar aunmenu WinBar.Next 86860e73f2aSBram Moolenaar aunmenu WinBar.Finish 8691b9645deSBram Moolenaar aunmenu WinBar.Cont 8701b9645deSBram Moolenaar aunmenu WinBar.Stop 87171137fedSBram Moolenaar aunmenu WinBar.Eval 87271137fedSBram Moolenaar endif 87371137fedSBram Moolenaar endfor 87471137fedSBram Moolenaar call win_gotoid(curwinid) 87571137fedSBram Moolenaar let s:winbar_winids = [] 87671137fedSBram Moolenaar 87771137fedSBram Moolenaar if exists('s:saved_mousemodel') 87871137fedSBram Moolenaar let &mousemodel = s:saved_mousemodel 87971137fedSBram Moolenaar unlet s:saved_mousemodel 88071137fedSBram Moolenaar aunmenu PopUp.-SEP3- 88171137fedSBram Moolenaar aunmenu PopUp.Set\ breakpoint 88271137fedSBram Moolenaar aunmenu PopUp.Clear\ breakpoint 88371137fedSBram Moolenaar aunmenu PopUp.Evaluate 8841b9645deSBram Moolenaar endif 88545d5f26dSBram Moolenaar endif 88637402ed5SBram Moolenaar 88737402ed5SBram Moolenaar exe 'sign unplace ' . s:pc_id 88837402ed5SBram Moolenaar for [id, entries] in items(s:breakpoints) 88937402ed5SBram Moolenaar for subid in keys(entries) 89045d5f26dSBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 89145d5f26dSBram Moolenaar endfor 89237402ed5SBram Moolenaar endfor 893a15b0a93SBram Moolenaar unlet s:breakpoints 894a15b0a93SBram Moolenaar unlet s:breakpoint_locations 895a15b0a93SBram Moolenaar 896a15b0a93SBram Moolenaar sign undefine debugPC 897a15b0a93SBram Moolenaar for val in s:BreakpointSigns 8984551c0a9SBram Moolenaar exe "sign undefine debugBreakpoint" . val 899e09ba7baSBram Moolenaar endfor 900e09ba7baSBram Moolenaar let s:BreakpointSigns = [] 901e09ba7baSBram Moolenaarendfunc 902589edb34SBram Moolenaar 90360e73f2aSBram Moolenaar" :Break - Set a breakpoint at the cursor position. 90460e73f2aSBram Moolenaarfunc s:SetBreakpoint(at) 90560e73f2aSBram Moolenaar " Setting a breakpoint may not work while the program is running. 90660e73f2aSBram Moolenaar " Interrupt to make it work. 90760e73f2aSBram Moolenaar let do_continue = 0 908b3307b5eSBram Moolenaar if !s:stopped 9094551c0a9SBram Moolenaar let do_continue = 1 910b3307b5eSBram Moolenaar if s:way == 'prompt' 91160e73f2aSBram Moolenaar call s:PromptInterrupt() 912b3307b5eSBram Moolenaar else 91360e73f2aSBram Moolenaar call s:SendCommand('-exec-interrupt') 91460e73f2aSBram Moolenaar endif 915589edb34SBram Moolenaar sleep 10m 916a15b0a93SBram Moolenaar endif 917589edb34SBram Moolenaar 918589edb34SBram Moolenaar " Use the fname:lnum format, older gdb can't handle --source. 919589edb34SBram Moolenaar let at = empty(a:at) ? 92060e73f2aSBram Moolenaar \ fnameescape(expand('%:p')) . ':' . line('.') : a:at 92160e73f2aSBram Moolenaar call s:SendCommand('-break-insert ' . at) 92260e73f2aSBram Moolenaar if do_continue 923e09ba7baSBram Moolenaar call s:SendCommand('-exec-continue') 924e09ba7baSBram Moolenaar endif 92571137fedSBram Moolenaarendfunc 92671137fedSBram Moolenaar 927e09ba7baSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position. 928e09ba7baSBram Moolenaarfunc s:ClearBreakpoint() 92937402ed5SBram Moolenaar let fname = fnameescape(expand('%:p')) 93037402ed5SBram Moolenaar let lnum = line('.') 93137402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 93237402ed5SBram Moolenaar if has_key(s:breakpoint_locations, bploc) 93337402ed5SBram Moolenaar let idx = 0 93437402ed5SBram Moolenaar for id in s:breakpoint_locations[bploc] 93537402ed5SBram Moolenaar if has_key(s:breakpoints, id) 93637402ed5SBram Moolenaar " Assume this always works, the reply is simply "^done". 93737402ed5SBram Moolenaar call s:SendCommand('-break-delete ' . id) 93837402ed5SBram Moolenaar for subid in keys(s:breakpoints[id]) 93937402ed5SBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 94037402ed5SBram Moolenaar endfor 941e09ba7baSBram Moolenaar unlet s:breakpoints[id] 94237402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc][idx] 94337402ed5SBram Moolenaar break 944e09ba7baSBram Moolenaar else 945e09ba7baSBram Moolenaar let idx += 1 94637402ed5SBram Moolenaar endif 94737402ed5SBram Moolenaar endfor 94837402ed5SBram Moolenaar if empty(s:breakpoint_locations[bploc]) 94937402ed5SBram Moolenaar unlet s:breakpoint_locations[bploc] 950e09ba7baSBram Moolenaar endif 951e09ba7baSBram Moolenaar endif 95260e73f2aSBram Moolenaarendfunc 95360e73f2aSBram Moolenaar 95460e73f2aSBram Moolenaarfunc s:Run(args) 95560e73f2aSBram Moolenaar if a:args != '' 95660e73f2aSBram Moolenaar call s:SendCommand('-exec-arguments ' . a:args) 95760e73f2aSBram Moolenaar endif 95860e73f2aSBram Moolenaar call s:SendCommand('-exec-run') 95951b0f370SBram Moolenaarendfunc 96051b0f370SBram Moolenaar 96151b0f370SBram Moolenaarfunc s:SendEval(expr) 96251b0f370SBram Moolenaar call s:SendCommand('-data-evaluate-expression "' . a:expr . '"') 96351b0f370SBram Moolenaar let s:evalexpr = a:expr 96445d5f26dSBram Moolenaarendfunc 96545d5f26dSBram Moolenaar 96645d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor 96745d5f26dSBram Moolenaarfunc s:Evaluate(range, arg) 96845d5f26dSBram Moolenaar if a:arg != '' 96945d5f26dSBram Moolenaar let expr = a:arg 97045d5f26dSBram Moolenaar elseif a:range == 2 97145d5f26dSBram Moolenaar let pos = getcurpos() 97245d5f26dSBram Moolenaar let reg = getreg('v', 1, 1) 97345d5f26dSBram Moolenaar let regt = getregtype('v') 97445d5f26dSBram Moolenaar normal! gv"vy 97545d5f26dSBram Moolenaar let expr = @v 97645d5f26dSBram Moolenaar call setpos('.', pos) 97745d5f26dSBram Moolenaar call setreg('v', reg, regt) 97845d5f26dSBram Moolenaar else 97922f1d0e3SBram Moolenaar let expr = expand('<cexpr>') 98051b0f370SBram Moolenaar endif 98145d5f26dSBram Moolenaar let s:ignoreEvalError = 0 98245d5f26dSBram Moolenaar call s:SendEval(expr) 98322f1d0e3SBram Moolenaarendfunc 98451b0f370SBram Moolenaar 98551b0f370SBram Moolenaarlet s:ignoreEvalError = 0 98645d5f26dSBram Moolenaarlet s:evalFromBalloonExpr = 0 98745d5f26dSBram Moolenaar 9881b9645deSBram Moolenaar" Handle the result of data-evaluate-expression 9891b9645deSBram Moolenaarfunc s:HandleEvaluate(msg) 99051b0f370SBram Moolenaar let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '') 99151b0f370SBram Moolenaar let value = substitute(value, '\\"', '"', 'g') 99251b0f370SBram Moolenaar if s:evalFromBalloonExpr 99351b0f370SBram Moolenaar if s:evalFromBalloonExprResult == '' 99451b0f370SBram Moolenaar let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value 99551b0f370SBram Moolenaar else 99651b0f370SBram Moolenaar let s:evalFromBalloonExprResult .= ' = ' . value 99751b0f370SBram Moolenaar endif 9981b9645deSBram Moolenaar call balloon_show(s:evalFromBalloonExprResult) 99951b0f370SBram Moolenaar else 10001b9645deSBram Moolenaar echomsg '"' . s:evalexpr . '": ' . value 10017f2e9d7cSBram Moolenaar endif 10021b9645deSBram Moolenaar 100322f1d0e3SBram Moolenaar if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' 100451b0f370SBram Moolenaar " Looks like a pointer, also display what it points to. 100551b0f370SBram Moolenaar let s:ignoreEvalError = 1 100651b0f370SBram Moolenaar call s:SendEval('*' . s:evalexpr) 10071b9645deSBram Moolenaar else 100845d5f26dSBram Moolenaar let s:evalFromBalloonExpr = 0 100945d5f26dSBram Moolenaar endif 101051b0f370SBram Moolenaarendfunc 101151b0f370SBram Moolenaar 101251b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer, 1013b3307b5eSBram Moolenaar" if there is any. 1014396e829fSBram Moolenaarfunc TermDebugBalloonExpr() 1015b3307b5eSBram Moolenaar if v:beval_winid != s:sourcewin 1016b3307b5eSBram Moolenaar return '' 1017b3307b5eSBram Moolenaar endif 1018b3307b5eSBram Moolenaar if !s:stopped 1019396e829fSBram Moolenaar " Only evaluate when stopped, otherwise setting a breakpoint using the 102051b0f370SBram Moolenaar " mouse triggers a balloon. 102151b0f370SBram Moolenaar return '' 102251b0f370SBram Moolenaar endif 102322f1d0e3SBram Moolenaar let s:evalFromBalloonExpr = 1 102422f1d0e3SBram Moolenaar let s:evalFromBalloonExprResult = '' 102551b0f370SBram Moolenaar let s:ignoreEvalError = 1 102651b0f370SBram Moolenaar call s:SendEval(v:beval_text) 102751b0f370SBram Moolenaar return '' 102845d5f26dSBram Moolenaarendfunc 102945d5f26dSBram Moolenaar 103022f1d0e3SBram Moolenaar" Handle an error. 103151b0f370SBram Moolenaarfunc s:HandleError(msg) 103222f1d0e3SBram Moolenaar if s:ignoreEvalError 103322f1d0e3SBram Moolenaar " Result of s:SendEval() failed, ignore. 103451b0f370SBram Moolenaar let s:ignoreEvalError = 0 103551b0f370SBram Moolenaar let s:evalFromBalloonExpr = 0 103645d5f26dSBram Moolenaar return 103745d5f26dSBram Moolenaar endif 103845d5f26dSBram Moolenaar echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '') 1039b3307b5eSBram Moolenaarendfunc 1040b3307b5eSBram Moolenaar 1041c4b533e1SBram Moolenaarfunc s:GotoSourcewinOrCreateIt() 1042b3307b5eSBram Moolenaar if !win_gotoid(s:sourcewin) 1043c4b533e1SBram Moolenaar new 1044c4b533e1SBram Moolenaar let s:sourcewin = win_getid(winnr()) 1045c4b533e1SBram Moolenaar call s:InstallWinbar() 1046c4b533e1SBram Moolenaar endif 104782be4849SBram Moolenaarendfunc 104882be4849SBram Moolenaar 104982be4849SBram Moolenaarfunc s:GotoAsmwinOrCreateIt() 105082be4849SBram Moolenaar if !win_gotoid(s:asmwin) 105182be4849SBram Moolenaar if win_gotoid(s:sourcewin) 105282be4849SBram Moolenaar exe 'rightbelow new' 105382be4849SBram Moolenaar else 105482be4849SBram Moolenaar exe 'new' 105582be4849SBram Moolenaar endif 105682be4849SBram Moolenaar 105782be4849SBram Moolenaar let s:asmwin = win_getid(winnr()) 105882be4849SBram Moolenaar 105982be4849SBram Moolenaar setlocal nowrap 106082be4849SBram Moolenaar setlocal number 106182be4849SBram Moolenaar setlocal noswapfile 106282be4849SBram Moolenaar setlocal buftype=nofile 106382be4849SBram Moolenaar 106482be4849SBram Moolenaar let asmbuf = bufnr('Termdebug-asm-listing') 106582be4849SBram Moolenaar if asmbuf > 0 106682be4849SBram Moolenaar exe 'buffer' . asmbuf 106782be4849SBram Moolenaar else 106882be4849SBram Moolenaar exe 'file Termdebug-asm-listing' 106982be4849SBram Moolenaar endif 107082be4849SBram Moolenaar 107182be4849SBram Moolenaar if exists('g:termdebug_disasm_window') 107282be4849SBram Moolenaar if g:termdebug_disasm_window > 1 107382be4849SBram Moolenaar exe 'resize ' . g:termdebug_disasm_window 107482be4849SBram Moolenaar endif 107582be4849SBram Moolenaar endif 107682be4849SBram Moolenaar endif 107782be4849SBram Moolenaar 107882be4849SBram Moolenaar if s:asm_addr != '' 107982be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 108082be4849SBram Moolenaar if lnum == 0 108182be4849SBram Moolenaar if s:stopped 108282be4849SBram Moolenaar call s:SendCommand('disassemble $pc') 108382be4849SBram Moolenaar endif 108482be4849SBram Moolenaar else 108582be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 108682be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 108782be4849SBram Moolenaar endif 108882be4849SBram Moolenaar endif 1089e09ba7baSBram Moolenaarendfunc 1090e09ba7baSBram Moolenaar 1091e09ba7baSBram Moolenaar" Handle stopping and running message from gdb. 1092fe386641SBram Moolenaar" Will update the sign that shows the current position. 1093fe386641SBram Moolenaarfunc s:HandleCursor(msg) 109460e73f2aSBram Moolenaar let wid = win_getid(winnr()) 10954551c0a9SBram Moolenaar 109660e73f2aSBram Moolenaar if a:msg =~ '^\*stopped' 109760e73f2aSBram Moolenaar call ch_log('program stopped') 10984551c0a9SBram Moolenaar let s:stopped = 1 109960e73f2aSBram Moolenaar elseif a:msg =~ '^\*running' 110060e73f2aSBram Moolenaar call ch_log('program running') 110160e73f2aSBram Moolenaar let s:stopped = 0 1102a15b0a93SBram Moolenaar endif 1103a15b0a93SBram Moolenaar 1104a15b0a93SBram Moolenaar if a:msg =~ 'fullname=' 1105a15b0a93SBram Moolenaar let fname = s:GetFullname(a:msg) 1106a15b0a93SBram Moolenaar else 110782be4849SBram Moolenaar let fname = '' 110882be4849SBram Moolenaar endif 110982be4849SBram Moolenaar 111082be4849SBram Moolenaar if a:msg =~ 'addr=' 111182be4849SBram Moolenaar let asm_addr = s:GetAsmAddr(a:msg) 111282be4849SBram Moolenaar if asm_addr != '' 111382be4849SBram Moolenaar let s:asm_addr = asm_addr 111482be4849SBram Moolenaar 111582be4849SBram Moolenaar let curwinid = win_getid(winnr()) 111682be4849SBram Moolenaar if win_gotoid(s:asmwin) 111782be4849SBram Moolenaar let lnum = search('^' . s:asm_addr) 111882be4849SBram Moolenaar if lnum == 0 111982be4849SBram Moolenaar call s:SendCommand('disassemble $pc') 112082be4849SBram Moolenaar else 112182be4849SBram Moolenaar exe 'sign unplace ' . s:asm_id 112282be4849SBram Moolenaar exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' 112382be4849SBram Moolenaar endif 112482be4849SBram Moolenaar 112582be4849SBram Moolenaar call win_gotoid(curwinid) 112682be4849SBram Moolenaar endif 112782be4849SBram Moolenaar endif 11281b9645deSBram Moolenaar endif 1129e09ba7baSBram Moolenaar 1130fe386641SBram Moolenaar if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 11314551c0a9SBram Moolenaar let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 11321b9645deSBram Moolenaar if lnum =~ '^[0-9]*$' 1133fe386641SBram Moolenaar call s:GotoSourcewinOrCreateIt() 1134fe386641SBram Moolenaar if expand('%:p') != fnamemodify(fname, ':p') 1135fe386641SBram Moolenaar if &modified 1136b3307b5eSBram Moolenaar " TODO: find existing window 1137c4b533e1SBram Moolenaar exe 'split ' . fnameescape(fname) 1138fe386641SBram Moolenaar let s:sourcewin = win_getid(winnr()) 1139fe386641SBram Moolenaar call s:InstallWinbar() 1140fe386641SBram Moolenaar else 1141fe386641SBram Moolenaar exe 'edit ' . fnameescape(fname) 1142fe386641SBram Moolenaar endif 114301164a65SBram Moolenaar endif 114439f7aa3cSBram Moolenaar exe lnum 1145cb80aa2dSBram Moolenaar exe 'sign unplace ' . s:pc_id 1146cb80aa2dSBram Moolenaar exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname 1147cb80aa2dSBram Moolenaar if !exists('b:save_signcolumn') 1148cb80aa2dSBram Moolenaar let b:save_signcolumn = &signcolumn 1149fe386641SBram Moolenaar call add(s:signcolumn_buflist, bufnr()) 1150fe386641SBram Moolenaar endif 11514551c0a9SBram Moolenaar setlocal signcolumn=yes 1152fe386641SBram Moolenaar endif 1153fe386641SBram Moolenaar elseif !s:stopped || fname != '' 1154fe386641SBram Moolenaar exe 'sign unplace ' . s:pc_id 1155fe386641SBram Moolenaar endif 1156e09ba7baSBram Moolenaar 1157e09ba7baSBram Moolenaar call win_gotoid(wid) 1158de1a8314SBram Moolenaarendfunc 1159a15b0a93SBram Moolenaar 116037402ed5SBram Moolenaarlet s:BreakpointSigns = [] 116137402ed5SBram Moolenaar 116237402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid) 116337402ed5SBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 116437402ed5SBram Moolenaar if index(s:BreakpointSigns, nr) == -1 1165de1a8314SBram Moolenaar call add(s:BreakpointSigns, nr) 1166de1a8314SBram Moolenaar exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint" 1167de1a8314SBram Moolenaar endif 116837402ed5SBram Moolenaarendfunc 116937402ed5SBram Moolenaar 11705378e1cfSBram Moolenaarfunc! s:SplitMsg(s) 11715378e1cfSBram Moolenaar return split(a:s, '{.\{-}}\zs') 1172e09ba7baSBram Moolenaarendfunction 1173e09ba7baSBram Moolenaar 1174e09ba7baSBram Moolenaar" Handle setting a breakpoint 11756dccc962SBram Moolenaar" Will update the sign that shows the breakpoint 11766dccc962SBram Moolenaarfunc s:HandleNewBreakpoint(msg) 11776dccc962SBram Moolenaar if a:msg !~ 'fullname=' 11786dccc962SBram Moolenaar " a watch does not have a file name 11795378e1cfSBram Moolenaar return 11805378e1cfSBram Moolenaar endif 11815378e1cfSBram Moolenaar for msg in s:SplitMsg(a:msg) 11825378e1cfSBram Moolenaar let fname = s:GetFullname(msg) 11835378e1cfSBram Moolenaar if empty(fname) 11845378e1cfSBram Moolenaar continue 11855378e1cfSBram Moolenaar endif 1186e09ba7baSBram Moolenaar let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '') 1187fe386641SBram Moolenaar if empty(nr) 1188e09ba7baSBram Moolenaar return 118937402ed5SBram Moolenaar endif 119037402ed5SBram Moolenaar 119137402ed5SBram Moolenaar " If "nr" is 123 it becomes "123.0" and subid is "0". 119237402ed5SBram Moolenaar " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. 119337402ed5SBram Moolenaar let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0') 119437402ed5SBram Moolenaar call s:CreateBreakpoint(id, subid) 119537402ed5SBram Moolenaar 119637402ed5SBram Moolenaar if has_key(s:breakpoints, id) 119737402ed5SBram Moolenaar let entries = s:breakpoints[id] 119837402ed5SBram Moolenaar else 119937402ed5SBram Moolenaar let entries = {} 120037402ed5SBram Moolenaar let s:breakpoints[id] = entries 120137402ed5SBram Moolenaar endif 1202e09ba7baSBram Moolenaar if has_key(entries, subid) 1203e09ba7baSBram Moolenaar let entry = entries[subid] 120437402ed5SBram Moolenaar else 1205fe386641SBram Moolenaar let entry = {} 1206e09ba7baSBram Moolenaar let entries[subid] = entry 12075378e1cfSBram Moolenaar endif 1208e09ba7baSBram Moolenaar 1209e09ba7baSBram Moolenaar let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') 12101b9645deSBram Moolenaar let entry['fname'] = fname 121137402ed5SBram Moolenaar let entry['lnum'] = lnum 121237402ed5SBram Moolenaar 121337402ed5SBram Moolenaar let bploc = printf('%s:%d', fname, lnum) 121437402ed5SBram Moolenaar if !has_key(s:breakpoint_locations, bploc) 121537402ed5SBram Moolenaar let s:breakpoint_locations[bploc] = [] 121637402ed5SBram Moolenaar endif 12171b9645deSBram Moolenaar let s:breakpoint_locations[bploc] += [id] 121837402ed5SBram Moolenaar 12191b9645deSBram Moolenaar if bufloaded(fname) 12205378e1cfSBram Moolenaar call s:PlaceSign(id, subid, entry) 12211b9645deSBram Moolenaar endif 12221b9645deSBram Moolenaar endfor 122337402ed5SBram Moolenaarendfunc 122437402ed5SBram Moolenaar 12253132cdddSBram Moolenaarfunc s:PlaceSign(id, subid, entry) 12261b9645deSBram Moolenaar let nr = printf('%d.%d', a:id, a:subid) 1227e09ba7baSBram Moolenaar exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' priority=110 file=' . a:entry['fname'] 1228e09ba7baSBram Moolenaar let a:entry['placed'] = 1 1229e09ba7baSBram Moolenaarendfunc 1230e09ba7baSBram Moolenaar 1231e09ba7baSBram Moolenaar" Handle deleting a breakpoint 123237402ed5SBram Moolenaar" Will remove the sign that shows the breakpoint 123337402ed5SBram Moolenaarfunc s:HandleBreakpointDelete(msg) 1234e09ba7baSBram Moolenaar let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 1235e09ba7baSBram Moolenaar if empty(id) 123637402ed5SBram Moolenaar return 123737402ed5SBram Moolenaar endif 12381b9645deSBram Moolenaar if has_key(s:breakpoints, id) 123937402ed5SBram Moolenaar for [subid, entry] in items(s:breakpoints[id]) 12401b9645deSBram Moolenaar if has_key(entry, 'placed') 12411b9645deSBram Moolenaar exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid) 12425378e1cfSBram Moolenaar unlet entry['placed'] 124337402ed5SBram Moolenaar endif 124437402ed5SBram Moolenaar endfor 1245c572da5fSBram Moolenaar unlet s:breakpoints[id] 12461b9645deSBram Moolenaar endif 12474551c0a9SBram Moolenaarendfunc 12484551c0a9SBram Moolenaar 12494551c0a9SBram Moolenaar" Handle the debugged program starting to run. 12504551c0a9SBram Moolenaar" Will store the process ID in s:pid 12514551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg) 12524551c0a9SBram Moolenaar let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0 12534551c0a9SBram Moolenaar if nr == 0 12544551c0a9SBram Moolenaar return 12554551c0a9SBram Moolenaar endif 12564551c0a9SBram Moolenaar let s:pid = nr 12574551c0a9SBram Moolenaar call ch_log('Detected process ID: ' . s:pid) 12581b9645deSBram Moolenaarendfunc 12591b9645deSBram Moolenaar 12601b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs. 126137402ed5SBram Moolenaarfunc s:BufRead() 126237402ed5SBram Moolenaar let fname = expand('<afile>:p') 12631b9645deSBram Moolenaar for [id, entries] in items(s:breakpoints) 126437402ed5SBram Moolenaar for [subid, entry] in items(entries) 12651b9645deSBram Moolenaar if entry['fname'] == fname 12661b9645deSBram Moolenaar call s:PlaceSign(id, subid, entry) 126737402ed5SBram Moolenaar endif 12681b9645deSBram Moolenaar endfor 12691b9645deSBram Moolenaar endfor 12701b9645deSBram Moolenaarendfunc 12711b9645deSBram Moolenaar 12721b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs. 127337402ed5SBram Moolenaarfunc s:BufUnloaded() 127437402ed5SBram Moolenaar let fname = expand('<afile>:p') 12751b9645deSBram Moolenaar for [id, entries] in items(s:breakpoints) 12761b9645deSBram Moolenaar for [subid, entry] in items(entries) 12771b9645deSBram Moolenaar if entry['fname'] == fname 12781b9645deSBram Moolenaar let entry['placed'] = 0 127937402ed5SBram Moolenaar endif 12801b9645deSBram Moolenaar endfor 1281ca4cc018SBram Moolenaar endfor 1282ca4cc018SBram Moolenaarendfunc 1283ca4cc018SBram Moolenaar 1284let &cpo = s:keepcpo 1285unlet s:keepcpo 1286