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