1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3b3307b5eSBram Moolenaar" Author: Bram Moolenaar
4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license"
5*519cc559SBram Moolenaar" Last Change: 2021 Nov 14
6c572da5fSBram Moolenaar"
7b3307b5eSBram Moolenaar" WORK IN PROGRESS - Only the basics work
8b3307b5eSBram Moolenaar" Note: On MS-Windows you need a recent version of gdb.  The one included with
9b3307b5eSBram Moolenaar" MingW is too old (7.6.1).
10b3307b5eSBram Moolenaar" I used version 7.12 from http://www.equation.com/servlet/equation.cmd?fa=gdb
11fe386641SBram Moolenaar"
12b3307b5eSBram Moolenaar" There are two ways to run gdb:
13b3307b5eSBram Moolenaar" - In a terminal window; used if possible, does not work on MS-Windows
14b3307b5eSBram Moolenaar"   Not used when g:termdebug_use_prompt is set to 1.
15b3307b5eSBram Moolenaar" - Using a "prompt" buffer; may use a terminal window for the program
16b3307b5eSBram Moolenaar"
17b3307b5eSBram Moolenaar" For both the current window is used to view source code and shows the
18b3307b5eSBram Moolenaar" current statement from gdb.
19b3307b5eSBram Moolenaar"
20b3307b5eSBram Moolenaar" USING A TERMINAL WINDOW
21b3307b5eSBram Moolenaar"
22b3307b5eSBram Moolenaar" Opens two visible terminal windows:
23b3307b5eSBram Moolenaar" 1. runs a pty for the debugged program, as with ":term NONE"
24b3307b5eSBram Moolenaar" 2. runs gdb, passing the pty of the debugged program
25fe386641SBram Moolenaar" A third terminal window is hidden, it is used for communication with gdb.
26fe386641SBram Moolenaar"
27b3307b5eSBram Moolenaar" USING A PROMPT BUFFER
28b3307b5eSBram Moolenaar"
29b3307b5eSBram Moolenaar" Opens a window with a prompt buffer to communicate with gdb.
30b3307b5eSBram Moolenaar" Gdb is run as a job with callbacks for I/O.
31b3307b5eSBram Moolenaar" On Unix another terminal window is opened to run the debugged program
32b3307b5eSBram Moolenaar" On MS-Windows a separate console is opened to run the debugged program
33b3307b5eSBram Moolenaar"
34fe386641SBram Moolenaar" The communication with gdb uses GDB/MI.  See:
35fe386641SBram Moolenaar" https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html
36c572da5fSBram Moolenaar
37b3307b5eSBram Moolenaar" In case this gets sourced twice.
3837c64c78SBram Moolenaarif exists(':Termdebug')
3937c64c78SBram Moolenaar  finish
4037c64c78SBram Moolenaarendif
4137c64c78SBram Moolenaar
42b3307b5eSBram Moolenaar" Need either the +terminal feature or +channel and the prompt buffer.
43b3307b5eSBram Moolenaar" The terminal feature does not work with gdb on win32.
44b3307b5eSBram Moolenaarif has('terminal') && !has('win32')
45b3307b5eSBram Moolenaar  let s:way = 'terminal'
46b3307b5eSBram Moolenaarelseif has('channel') && exists('*prompt_setprompt')
47b3307b5eSBram Moolenaar  let s:way = 'prompt'
48b3307b5eSBram Moolenaarelse
49b3307b5eSBram Moolenaar  if has('terminal')
50b3307b5eSBram Moolenaar    let s:err = 'Cannot debug, missing prompt buffer support'
51b3307b5eSBram Moolenaar  else
52b3307b5eSBram Moolenaar    let s:err = 'Cannot debug, +channel feature is not supported'
53b3307b5eSBram Moolenaar  endif
54b3307b5eSBram Moolenaar  command -nargs=* -complete=file -bang Termdebug echoerr s:err
55b3307b5eSBram Moolenaar  command -nargs=+ -complete=file -bang TermdebugCommand echoerr s:err
56b3307b5eSBram Moolenaar  finish
57b3307b5eSBram Moolenaarendif
5860e73f2aSBram Moolenaar
59ca4cc018SBram Moolenaarlet s:keepcpo = &cpo
60ca4cc018SBram Moolenaarset cpo&vim
61ca4cc018SBram Moolenaar
62fe386641SBram Moolenaar" The command that starts debugging, e.g. ":Termdebug vim".
63fe386641SBram Moolenaar" To end type "quit" in the gdb window.
6432c67ba7SBram Moolenaarcommand -nargs=* -complete=file -bang Termdebug call s:StartDebug(<bang>0, <f-args>)
6532c67ba7SBram Moolenaarcommand -nargs=+ -complete=file -bang TermdebugCommand call s:StartDebugCommand(<bang>0, <f-args>)
66c572da5fSBram Moolenaar
67fe386641SBram Moolenaar" Name of the gdb command, defaults to "gdb".
6818223a59SBram Moolenaarif !exists('g:termdebugger')
6918223a59SBram Moolenaar  let g:termdebugger = 'gdb'
70c572da5fSBram Moolenaarendif
71c572da5fSBram Moolenaar
72fe386641SBram Moolenaarlet s:pc_id = 12
7382be4849SBram Moolenaarlet s:asm_id = 13
7482be4849SBram Moolenaarlet s:break_id = 14  " breakpoint number is added to this
7560e73f2aSBram Moolenaarlet s:stopped = 1
76e09ba7baSBram Moolenaar
7782be4849SBram Moolenaarlet s:parsing_disasm_msg = 0
7882be4849SBram Moolenaarlet s:asm_lines = []
7982be4849SBram Moolenaarlet s:asm_addr = ''
8082be4849SBram Moolenaar
815378e1cfSBram Moolenaar" Take a breakpoint number as used by GDB and turn it into an integer.
8237402ed5SBram Moolenaar" The breakpoint may contain a dot: 123.4 -> 123004
8337402ed5SBram Moolenaar" The main breakpoint has a zero subid.
8437402ed5SBram Moolenaarfunc s:Breakpoint2SignNumber(id, subid)
8537402ed5SBram Moolenaar  return s:break_id + a:id * 1000 + a:subid
865378e1cfSBram Moolenaarendfunction
875378e1cfSBram Moolenaar
88f07f9e73SBram Moolenaarfunc s:Highlight(init, old, new)
89f07f9e73SBram Moolenaar  let default = a:init ? 'default ' : ''
90f07f9e73SBram Moolenaar  if a:new ==# 'light' && a:old !=# 'light'
91f07f9e73SBram Moolenaar    exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue"
92f07f9e73SBram Moolenaar  elseif a:new ==# 'dark' && a:old !=# 'dark'
93f07f9e73SBram Moolenaar    exe "hi " . default . "debugPC term=reverse ctermbg=darkblue guibg=darkblue"
94e09ba7baSBram Moolenaar  endif
95f07f9e73SBram Moolenaarendfunc
96f07f9e73SBram Moolenaar
97f07f9e73SBram Moolenaarcall s:Highlight(1, '', &background)
98e09ba7baSBram Moolenaarhi default debugBreakpoint term=reverse ctermbg=red guibg=red
99fe386641SBram Moolenaar
10032c67ba7SBram Moolenaarfunc s:StartDebug(bang, ...)
10132c67ba7SBram Moolenaar  " First argument is the command to debug, second core file or process ID.
10232c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': a:000, 'bang': a:bang})
10332c67ba7SBram Moolenaarendfunc
10432c67ba7SBram Moolenaar
10532c67ba7SBram Moolenaarfunc s:StartDebugCommand(bang, ...)
10632c67ba7SBram Moolenaar  " First argument is the command to debug, rest are run arguments.
10732c67ba7SBram Moolenaar  call s:StartDebug_internal({'gdb_args': [a:1], 'proc_args': a:000[1:], 'bang': a:bang})
10832c67ba7SBram Moolenaarendfunc
10932c67ba7SBram Moolenaar
11032c67ba7SBram Moolenaarfunc s:StartDebug_internal(dict)
111b3623a38SBram Moolenaar  if exists('s:gdbwin')
11218223a59SBram Moolenaar    echoerr 'Terminal debugger already running, cannot run two'
113b3623a38SBram Moolenaar    return
114b3623a38SBram Moolenaar  endif
11518223a59SBram Moolenaar  if !executable(g:termdebugger)
11618223a59SBram Moolenaar    echoerr 'Cannot execute debugger program "' .. g:termdebugger .. '"'
11718223a59SBram Moolenaar    return
11818223a59SBram Moolenaar  endif
11918223a59SBram Moolenaar
120b3307b5eSBram Moolenaar  let s:ptywin = 0
1214551c0a9SBram Moolenaar  let s:pid = 0
12282be4849SBram Moolenaar  let s:asmwin = 0
123b3623a38SBram Moolenaar
1246aa57295SBram Moolenaar  if exists('#User#TermdebugStartPre')
1256aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStartPre
1266aa57295SBram Moolenaar  endif
1276aa57295SBram Moolenaar
128b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
129b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
130b3307b5eSBram Moolenaar
131b3307b5eSBram Moolenaar  let s:sourcewin = win_getid(winnr())
132cb80aa2dSBram Moolenaar
133cb80aa2dSBram Moolenaar  " Remember the old value of 'signcolumn' for each buffer that it's set in, so
134cb80aa2dSBram Moolenaar  " that we can restore the value for all buffers.
135cb80aa2dSBram Moolenaar  let b:save_signcolumn = &signcolumn
136cb80aa2dSBram Moolenaar  let s:signcolumn_buflist = [bufnr()]
137fe386641SBram Moolenaar
13824a98a0eSBram Moolenaar  let s:save_columns = 0
13968e6560bSBram Moolenaar  let s:allleft = 0
14024a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
14124a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
14238baa3e6SBram Moolenaar      let s:save_columns = &columns
14338baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
14468e6560bSBram Moolenaar      " If we make the Vim window wider, use the whole left halve for the debug
14568e6560bSBram Moolenaar      " windows.
14668e6560bSBram Moolenaar      let s:allleft = 1
14724a98a0eSBram Moolenaar    endif
148b3307b5eSBram Moolenaar    let s:vertical = 1
14938baa3e6SBram Moolenaar  else
150b3307b5eSBram Moolenaar    let s:vertical = 0
15138baa3e6SBram Moolenaar  endif
15238baa3e6SBram Moolenaar
153b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
154b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
155b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
156b3307b5eSBram Moolenaar    let s:way = 'terminal'
157b3307b5eSBram Moolenaar  else
158b3307b5eSBram Moolenaar    let s:way = 'prompt'
159b3307b5eSBram Moolenaar  endif
160b3307b5eSBram Moolenaar
161b3307b5eSBram Moolenaar  if s:way == 'prompt'
162b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
163b3307b5eSBram Moolenaar  else
164b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
165b3307b5eSBram Moolenaar  endif
16682be4849SBram Moolenaar
16782be4849SBram Moolenaar  if exists('g:termdebug_disasm_window')
16882be4849SBram Moolenaar    if g:termdebug_disasm_window
16982be4849SBram Moolenaar      let curwinid = win_getid(winnr())
17082be4849SBram Moolenaar      call s:GotoAsmwinOrCreateIt()
17182be4849SBram Moolenaar      call win_gotoid(curwinid)
17282be4849SBram Moolenaar    endif
17382be4849SBram Moolenaar  endif
1746aa57295SBram Moolenaar
1756aa57295SBram Moolenaar  if exists('#User#TermdebugStartPost')
1766aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStartPost
1776aa57295SBram Moolenaar  endif
178b3307b5eSBram Moolenaarendfunc
179b3307b5eSBram Moolenaar
180ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended.
181ef3c6a5bSBram Moolenaarfunc s:CloseBuffers()
182ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
183ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:commbuf
184ef3c6a5bSBram Moolenaar  unlet! s:gdbwin
185ef3c6a5bSBram Moolenaarendfunc
186ef3c6a5bSBram Moolenaar
187d2ea7cf1SBram Moolenaarfunc s:CheckGdbRunning()
188d2ea7cf1SBram Moolenaar  let gdbproc = term_getjob(s:gdbbuf)
189d2ea7cf1SBram Moolenaar  if gdbproc == v:null || job_status(gdbproc) !=# 'run'
190d2ea7cf1SBram Moolenaar    echoerr string(g:termdebugger) . ' exited unexpectedly'
191d2ea7cf1SBram Moolenaar    call s:CloseBuffers()
192d2ea7cf1SBram Moolenaar    return ''
193d2ea7cf1SBram Moolenaar  endif
194d2ea7cf1SBram Moolenaar  return 'ok'
195d2ea7cf1SBram Moolenaarendfunc
196d2ea7cf1SBram Moolenaar
197b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
198b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
199fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
200b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
201b3307b5eSBram Moolenaar	\ 'vertical': s:vertical,
202fe386641SBram Moolenaar	\ })
203fe386641SBram Moolenaar  if s:ptybuf == 0
204fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
205fe386641SBram Moolenaar    return
206fe386641SBram Moolenaar  endif
207fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
20845d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
209b3307b5eSBram Moolenaar  if s:vertical
21051b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
21151b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
21251b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
21368e6560bSBram Moolenaar    if s:allleft
21468e6560bSBram Moolenaar      " use the whole left column
21568e6560bSBram Moolenaar      wincmd H
21668e6560bSBram Moolenaar    endif
21751b0f370SBram Moolenaar  endif
218fe386641SBram Moolenaar
219fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
220fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
221fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
222fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
223fe386641SBram Moolenaar	\ 'hidden': 1,
224fe386641SBram Moolenaar	\ })
225fe386641SBram Moolenaar  if s:commbuf == 0
226fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
227fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
228fe386641SBram Moolenaar    return
229fe386641SBram Moolenaar  endif
230fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
231c572da5fSBram Moolenaar
232c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
233c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
23432c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
23532c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
23632c67ba7SBram Moolenaar
237d2ea7cf1SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty, '--eval-command', 'echo startupdone\n'] + gdb_args
238b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
23960e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
240fe386641SBram Moolenaar	\ 'term_finish': 'close',
241c572da5fSBram Moolenaar	\ })
24260e73f2aSBram Moolenaar  if s:gdbbuf == 0
243fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
244ef3c6a5bSBram Moolenaar    call s:CloseBuffers()
245fe386641SBram Moolenaar    return
246fe386641SBram Moolenaar  endif
24745d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
248fe386641SBram Moolenaar
249d2ea7cf1SBram Moolenaar  " Wait for the "startupdone" message before sending any commands.
250d2ea7cf1SBram Moolenaar  let try_count = 0
251d2ea7cf1SBram Moolenaar  while 1
252d2ea7cf1SBram Moolenaar    if s:CheckGdbRunning() != 'ok'
253d2ea7cf1SBram Moolenaar      return
254d2ea7cf1SBram Moolenaar    endif
255d2ea7cf1SBram Moolenaar
256d2ea7cf1SBram Moolenaar    for lnum in range(1, 200)
257d2ea7cf1SBram Moolenaar      if term_getline(s:gdbbuf, lnum) =~ 'startupdone'
258d2ea7cf1SBram Moolenaar	let try_count = 9999
259d2ea7cf1SBram Moolenaar	break
260d2ea7cf1SBram Moolenaar      endif
261d2ea7cf1SBram Moolenaar    endfor
262d2ea7cf1SBram Moolenaar    let try_count += 1
263d2ea7cf1SBram Moolenaar    if try_count > 300
264d2ea7cf1SBram Moolenaar      " done or give up after five seconds
265d2ea7cf1SBram Moolenaar      break
266d2ea7cf1SBram Moolenaar    endif
267d2ea7cf1SBram Moolenaar    sleep 10m
268d2ea7cf1SBram Moolenaar  endwhile
269d2ea7cf1SBram Moolenaar
270d2ea7cf1SBram Moolenaar  " Set arguments to be run.
27132c67ba7SBram Moolenaar  if len(proc_args)
27232c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
27332c67ba7SBram Moolenaar  endif
27432c67ba7SBram Moolenaar
275fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
27660e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
27760e73f2aSBram Moolenaar
2783e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
2793e4b84d0SBram Moolenaar  " why the debugger doesn't work.
2803e4b84d0SBram Moolenaar  let try_count = 0
2813e4b84d0SBram Moolenaar  while 1
282d2ea7cf1SBram Moolenaar    if s:CheckGdbRunning() != 'ok'
283ef3c6a5bSBram Moolenaar      return
284ef3c6a5bSBram Moolenaar    endif
285ef3c6a5bSBram Moolenaar
2863e4b84d0SBram Moolenaar    let response = ''
287b3623a38SBram Moolenaar    for lnum in range(1, 200)
28819c8fe19SBram Moolenaar      let line1 = term_getline(s:gdbbuf, lnum)
28919c8fe19SBram Moolenaar      let line2 = term_getline(s:gdbbuf, lnum + 1)
29019c8fe19SBram Moolenaar      if line1 =~ 'new-ui mi '
291f63db65bSBram Moolenaar	" response can be in the same line or the next line
29219c8fe19SBram Moolenaar	let response = line1 . line2
2933e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
294f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
295ef3c6a5bSBram Moolenaar	  call s:CloseBuffers()
2963e4b84d0SBram Moolenaar	  return
2973e4b84d0SBram Moolenaar	endif
2983e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2993e4b84d0SBram Moolenaar	  " Success!
3003e4b84d0SBram Moolenaar	  break
3013e4b84d0SBram Moolenaar	endif
30219c8fe19SBram Moolenaar      elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi '
30319c8fe19SBram Moolenaar	" Reading symbols might take a while, try more times
30406fe74aeSBram Moolenaar	let try_count -= 1
30506fe74aeSBram Moolenaar      endif
3063e4b84d0SBram Moolenaar    endfor
3073e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
3083e4b84d0SBram Moolenaar      break
3093e4b84d0SBram Moolenaar    endif
3103e4b84d0SBram Moolenaar    let try_count += 1
3113e4b84d0SBram Moolenaar    if try_count > 100
3123e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
3133e4b84d0SBram Moolenaar      break
3143e4b84d0SBram Moolenaar    endif
3153e4b84d0SBram Moolenaar    sleep 10m
3163e4b84d0SBram Moolenaar  endwhile
3173e4b84d0SBram Moolenaar
31891359014SBram Moolenaar  " Interpret commands while the target is running.  This should usually only be
31960e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
32060e73f2aSBram Moolenaar  " running.
32160e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
322b3307b5eSBram Moolenaar  " Older gdb uses a different command.
323b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
324e09ba7baSBram Moolenaar
325f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
326f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
327b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
328f3ba14ffSBram Moolenaar
329d2ea7cf1SBram Moolenaar  call job_setoptions(term_getjob(s:gdbbuf), {'exit_cb': function('s:EndTermDebug')})
33089a9c159SBram Moolenaar
33189a9c159SBram Moolenaar  " Set the filetype, this can be used to add mappings.
33289a9c159SBram Moolenaar  set filetype=termdebug
33389a9c159SBram Moolenaar
334b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
335b3307b5eSBram Moolenaarendfunc
336b3307b5eSBram Moolenaar
337b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
338b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
339b3307b5eSBram Moolenaar  if s:vertical
340b3307b5eSBram Moolenaar    vertical new
341b3307b5eSBram Moolenaar  else
342b3307b5eSBram Moolenaar    new
343b3307b5eSBram Moolenaar  endif
344b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
345b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
346b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
347b3307b5eSBram Moolenaar  set buftype=prompt
348b3307b5eSBram Moolenaar  file gdb
349b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
350b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
351b3307b5eSBram Moolenaar
352b3307b5eSBram Moolenaar  if s:vertical
353b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
354b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
355b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
356b3307b5eSBram Moolenaar  endif
357b3307b5eSBram Moolenaar
358b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
359b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
360b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
361b3307b5eSBram Moolenaar
362b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
363b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
364b3307b5eSBram Moolenaar
365b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
366b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
367b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
368b3307b5eSBram Moolenaar	\ })
369b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
370b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
371b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
372b3307b5eSBram Moolenaar    return
373b3307b5eSBram Moolenaar  endif
3744551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
3754551c0a9SBram Moolenaar  set modified
376b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
377b3307b5eSBram Moolenaar
37891359014SBram Moolenaar  " Interpret commands while the target is running.  This should usually only
379b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
380b3307b5eSBram Moolenaar  " target is running.
381b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
382b3307b5eSBram Moolenaar  " Older gdb uses a different command.
383b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
384b3307b5eSBram Moolenaar
385b3307b5eSBram Moolenaar  let s:ptybuf = 0
386b3307b5eSBram Moolenaar  if has('win32')
387b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
388b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
389b3307b5eSBram Moolenaar  elseif has('terminal')
390b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
391b3307b5eSBram Moolenaar    " gdb window.
392b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
393b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
394b3307b5eSBram Moolenaar	  \ })
395b3307b5eSBram Moolenaar    if s:ptybuf == 0
396b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
397b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
398b3307b5eSBram Moolenaar      return
399b3307b5eSBram Moolenaar    endif
400b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
401b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
402b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
403b3307b5eSBram Moolenaar
404b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
405b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
406b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
407b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
408b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
409b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
410b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
411b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
412b3307b5eSBram Moolenaar  else
413b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
414b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
415b3307b5eSBram Moolenaar  endif
416b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
417b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
418b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
419b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
420b3307b5eSBram Moolenaar
421b3307b5eSBram Moolenaar  " Set arguments to be run
422b3307b5eSBram Moolenaar  if len(proc_args)
423b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
424b3307b5eSBram Moolenaar  endif
425b3307b5eSBram Moolenaar
426b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
427b3307b5eSBram Moolenaar  startinsert
428b3307b5eSBram Moolenaarendfunc
429b3307b5eSBram Moolenaar
430b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
43138baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
43238baa3e6SBram Moolenaar  " There can be only one.
43338baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
43438baa3e6SBram Moolenaar
43545d5f26dSBram Moolenaar  " Install debugger commands in the text window.
436b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
437e09ba7baSBram Moolenaar  call s:InstallCommands()
43845d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
439e09ba7baSBram Moolenaar
44051b0f370SBram Moolenaar  " Enable showing a balloon with eval info
441246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
442246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
44351b0f370SBram Moolenaar    if has("balloon_eval")
44451b0f370SBram Moolenaar      set ballooneval
445246fe03dSBram Moolenaar    endif
44651b0f370SBram Moolenaar    if has("balloon_eval_term")
44751b0f370SBram Moolenaar      set balloonevalterm
44851b0f370SBram Moolenaar    endif
44951b0f370SBram Moolenaar  endif
45051b0f370SBram Moolenaar
4515378e1cfSBram Moolenaar  " Contains breakpoints that have been placed, key is a string with the GDB
4525378e1cfSBram Moolenaar  " breakpoint number.
45337402ed5SBram Moolenaar  " Each entry is a dict, containing the sub-breakpoints.  Key is the subid.
45437402ed5SBram Moolenaar  " For a breakpoint that is just a number the subid is zero.
45537402ed5SBram Moolenaar  " For a breakpoint "123.4" the id is "123" and subid is "4".
45637402ed5SBram Moolenaar  " Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
45737402ed5SBram Moolenaar  " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
458e09ba7baSBram Moolenaar  let s:breakpoints = {}
4591b9645deSBram Moolenaar
46037402ed5SBram Moolenaar  " Contains breakpoints by file/lnum.  The key is "fname:lnum".
46137402ed5SBram Moolenaar  " Each entry is a list of breakpoint IDs at that position.
46237402ed5SBram Moolenaar  let s:breakpoint_locations = {}
46337402ed5SBram Moolenaar
4641b9645deSBram Moolenaar  augroup TermDebug
4651b9645deSBram Moolenaar    au BufRead * call s:BufRead()
4661b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
467f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
4681b9645deSBram Moolenaar  augroup END
46932c67ba7SBram Moolenaar
470b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
471b3307b5eSBram Moolenaar  " window.
47232c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
47332c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
47432c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
47532c67ba7SBram Moolenaar  endif
476c572da5fSBram Moolenaarendfunc
477c572da5fSBram Moolenaar
478b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
479b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
480b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
481b3307b5eSBram Moolenaar  if s:way == 'prompt'
482b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
483b3307b5eSBram Moolenaar  else
484b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
485b3307b5eSBram Moolenaar  endif
486b3307b5eSBram Moolenaarendfunc
487b3307b5eSBram Moolenaar
488b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
489b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
490b3307b5eSBram Moolenaar  if s:way == 'prompt'
491b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
492b3307b5eSBram Moolenaar  else
493b3307b5eSBram Moolenaar    let do_continue = 0
494b3307b5eSBram Moolenaar    if !s:stopped
495b3307b5eSBram Moolenaar      let do_continue = 1
496b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
497b3307b5eSBram Moolenaar      sleep 10m
498b3307b5eSBram Moolenaar    endif
499b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
500b3307b5eSBram Moolenaar    if do_continue
501b3307b5eSBram Moolenaar      Continue
502b3307b5eSBram Moolenaar    endif
503b3307b5eSBram Moolenaar  endif
504b3307b5eSBram Moolenaarendfunc
505b3307b5eSBram Moolenaar
506b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
507b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
508b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
509b3307b5eSBram Moolenaarendfunc
510b3307b5eSBram Moolenaar
5114551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
5124551c0a9SBram Moolenaar" breakpoint.
513b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
5142ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
5152ed890f1SBram Moolenaar  if has('win32')
5162ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
5172ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
5184551c0a9SBram Moolenaar    if s:pid == 0
5194551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
5204551c0a9SBram Moolenaar    else
5214551c0a9SBram Moolenaar      call debugbreak(s:pid)
5224551c0a9SBram Moolenaar    endif
5232ed890f1SBram Moolenaar  else
5242ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
5252ed890f1SBram Moolenaar  endif
526b3307b5eSBram Moolenaarendfunc
527b3307b5eSBram Moolenaar
528b3307b5eSBram Moolenaar" Function called when gdb outputs text.
529b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
530b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
531b3307b5eSBram Moolenaar
532b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
533b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
534a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
535b3307b5eSBram Moolenaar    return
536b3307b5eSBram Moolenaar  endif
537113cb513SBram Moolenaar  if a:text =~ '^\^error,msg='
538b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
539b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
540b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
541b3307b5eSBram Moolenaar      unlet s:evalexpr
542b3307b5eSBram Moolenaar      return
543b3307b5eSBram Moolenaar    endif
544b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
545b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
546b3307b5eSBram Moolenaar  else
547b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
548b3307b5eSBram Moolenaar    return
549b3307b5eSBram Moolenaar  endif
550b3307b5eSBram Moolenaar
551b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
552b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
553b3307b5eSBram Moolenaar
554b3307b5eSBram Moolenaar  " Add the output above the current prompt.
555b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
5564551c0a9SBram Moolenaar  set modified
557b3307b5eSBram Moolenaar
558b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
559b3307b5eSBram Moolenaarendfunc
560b3307b5eSBram Moolenaar
561b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
562b3307b5eSBram Moolenaar" to the next ", unescaping characters.
563b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
564b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
565a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
566b3307b5eSBram Moolenaar    return
567b3307b5eSBram Moolenaar  endif
568b3307b5eSBram Moolenaar  let result = ''
569b3307b5eSBram Moolenaar  let i = 1
570b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
571b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
572b3307b5eSBram Moolenaar      let i += 1
573b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
574b3307b5eSBram Moolenaar	" drop \n
575b3307b5eSBram Moolenaar	let i += 1
576b3307b5eSBram Moolenaar	continue
577589edb34SBram Moolenaar      elseif a:quotedText[i] == 't'
578589edb34SBram Moolenaar	" append \t
579589edb34SBram Moolenaar	let i += 1
580589edb34SBram Moolenaar	let result .= "\t"
581589edb34SBram Moolenaar	continue
582b3307b5eSBram Moolenaar      endif
583b3307b5eSBram Moolenaar    endif
584b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
585b3307b5eSBram Moolenaar    let i += 1
586b3307b5eSBram Moolenaar  endwhile
587b3307b5eSBram Moolenaar  return result
588b3307b5eSBram Moolenaarendfunc
589b3307b5eSBram Moolenaar
590a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
591a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
5925378e1cfSBram Moolenaar  if a:msg !~ 'fullname'
5935378e1cfSBram Moolenaar    return ''
5945378e1cfSBram Moolenaar  endif
595a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
596a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
597a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
598a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
599a15b0a93SBram Moolenaar  endif
600a15b0a93SBram Moolenaar  return name
601a15b0a93SBram Moolenaarendfunc
602a15b0a93SBram Moolenaar
60382be4849SBram Moolenaar" Extract the "addr" value from a gdb message with addr="0x0001234".
60482be4849SBram Moolenaarfunc s:GetAsmAddr(msg)
60582be4849SBram Moolenaar  if a:msg !~ 'addr='
60682be4849SBram Moolenaar    return ''
60782be4849SBram Moolenaar  endif
60882be4849SBram Moolenaar  let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', ''))
60982be4849SBram Moolenaar  return addr
61082be4849SBram Moolenaarendfunc
6116aa57295SBram Moolenaar
612b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
6136aa57295SBram Moolenaar  if exists('#User#TermdebugStopPre')
6146aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStopPre
6156aa57295SBram Moolenaar  endif
6166aa57295SBram Moolenaar
617fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
618b3623a38SBram Moolenaar  unlet s:gdbwin
619e09ba7baSBram Moolenaar
620b3307b5eSBram Moolenaar  call s:EndDebugCommon()
621b3307b5eSBram Moolenaarendfunc
622b3307b5eSBram Moolenaar
623b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
624e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
625e09ba7baSBram Moolenaar
626b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
627b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
628b3307b5eSBram Moolenaar  endif
629b3307b5eSBram Moolenaar
630cb80aa2dSBram Moolenaar  " Restore 'signcolumn' in all buffers for which it was set.
631b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
632cb80aa2dSBram Moolenaar  let was_buf = bufnr()
633cb80aa2dSBram Moolenaar  for bufnr in s:signcolumn_buflist
634cb80aa2dSBram Moolenaar    if bufexists(bufnr)
635cb80aa2dSBram Moolenaar      exe bufnr .. "buf"
636cb80aa2dSBram Moolenaar      if exists('b:save_signcolumn')
637cb80aa2dSBram Moolenaar	let &signcolumn = b:save_signcolumn
638cb80aa2dSBram Moolenaar	unlet b:save_signcolumn
639cb80aa2dSBram Moolenaar      endif
640cb80aa2dSBram Moolenaar    endif
641cb80aa2dSBram Moolenaar  endfor
642cb80aa2dSBram Moolenaar  exe was_buf .. "buf"
643cb80aa2dSBram Moolenaar
644e09ba7baSBram Moolenaar  call s:DeleteCommands()
645e09ba7baSBram Moolenaar
646e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
647b3307b5eSBram Moolenaar
64838baa3e6SBram Moolenaar  if s:save_columns > 0
64938baa3e6SBram Moolenaar    let &columns = s:save_columns
65038baa3e6SBram Moolenaar  endif
6511b9645deSBram Moolenaar
652246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
653246fe03dSBram Moolenaar    set balloonexpr=
65451b0f370SBram Moolenaar    if has("balloon_eval")
65551b0f370SBram Moolenaar      set noballooneval
656246fe03dSBram Moolenaar    endif
65751b0f370SBram Moolenaar    if has("balloon_eval_term")
65851b0f370SBram Moolenaar      set noballoonevalterm
65951b0f370SBram Moolenaar    endif
66051b0f370SBram Moolenaar  endif
66151b0f370SBram Moolenaar
6626aa57295SBram Moolenaar  if exists('#User#TermdebugStopPost')
6636aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStopPost
6646aa57295SBram Moolenaar  endif
6656aa57295SBram Moolenaar
6661b9645deSBram Moolenaar  au! TermDebug
667fe386641SBram Moolenaarendfunc
668fe386641SBram Moolenaar
669b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
6706aa57295SBram Moolenaar  if exists('#User#TermdebugStopPre')
6716aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStopPre
6726aa57295SBram Moolenaar  endif
6736aa57295SBram Moolenaar
674b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
675b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
6764551c0a9SBram Moolenaar  set nomodified
677b3307b5eSBram Moolenaar  close
678b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
679b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
680b3307b5eSBram Moolenaar  endif
681b3307b5eSBram Moolenaar
682b3307b5eSBram Moolenaar  call s:EndDebugCommon()
683b3307b5eSBram Moolenaar  unlet s:gdbwin
684b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
685b3307b5eSBram Moolenaarendfunc
686b3307b5eSBram Moolenaar
68782be4849SBram Moolenaar" Disassembly window - added by Michael Sartain
68882be4849SBram Moolenaar"
68982be4849SBram Moolenaar" - CommOutput: disassemble $pc
69082be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n"
69182be4849SBram Moolenaar" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n"
69282be4849SBram Moolenaar" - CommOutput: ~"   0x0000555556466f69 <+0>:\tpush   rbp\n"
69382be4849SBram Moolenaar" ...
69482be4849SBram Moolenaar" - CommOutput: ~"   0x0000555556467cd0:\tpop    rbp\n"
69582be4849SBram Moolenaar" - CommOutput: ~"   0x0000555556467cd1:\tret    \n"
69682be4849SBram Moolenaar" - CommOutput: ~"End of assembler dump.\n"
69782be4849SBram Moolenaar" - CommOutput: ^done
69882be4849SBram Moolenaar
69982be4849SBram Moolenaar" - CommOutput: disassemble $pc
70082be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n"
70182be4849SBram Moolenaar" - CommOutput: &"No function contains specified address.\n"
70282be4849SBram Moolenaar" - CommOutput: ^error,msg="No function contains specified address."
70382be4849SBram Moolenaarfunc s:HandleDisasmMsg(msg)
70482be4849SBram Moolenaar  if a:msg =~ '^\^done'
70582be4849SBram Moolenaar    let curwinid = win_getid(winnr())
70682be4849SBram Moolenaar    if win_gotoid(s:asmwin)
70782be4849SBram Moolenaar      silent normal! gg0"_dG
70882be4849SBram Moolenaar      call setline(1, s:asm_lines)
70982be4849SBram Moolenaar      set nomodified
71082be4849SBram Moolenaar      set filetype=asm
71182be4849SBram Moolenaar
71282be4849SBram Moolenaar      let lnum = search('^' . s:asm_addr)
71382be4849SBram Moolenaar      if lnum != 0
71482be4849SBram Moolenaar        exe 'sign unplace ' . s:asm_id
71582be4849SBram Moolenaar        exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
71682be4849SBram Moolenaar      endif
71782be4849SBram Moolenaar
71882be4849SBram Moolenaar      call win_gotoid(curwinid)
71982be4849SBram Moolenaar    endif
72082be4849SBram Moolenaar
72182be4849SBram Moolenaar    let s:parsing_disasm_msg = 0
72282be4849SBram Moolenaar    let s:asm_lines = []
72382be4849SBram Moolenaar  elseif a:msg =~ '^\^error,msg='
72482be4849SBram Moolenaar    if s:parsing_disasm_msg == 1
72582be4849SBram Moolenaar      " Disassemble call ran into an error. This can happen when gdb can't
72682be4849SBram Moolenaar      " find the function frame address, so let's try to disassemble starting
72782be4849SBram Moolenaar      " at current PC
72882be4849SBram Moolenaar      call s:SendCommand('disassemble $pc,+100')
72982be4849SBram Moolenaar    endif
73082be4849SBram Moolenaar    let s:parsing_disasm_msg = 0
73182be4849SBram Moolenaar  elseif a:msg =~ '\&\"disassemble \$pc'
73282be4849SBram Moolenaar    if a:msg =~ '+100'
73382be4849SBram Moolenaar      " This is our second disasm attempt
73482be4849SBram Moolenaar      let s:parsing_disasm_msg = 2
73582be4849SBram Moolenaar    endif
73682be4849SBram Moolenaar  else
73782be4849SBram Moolenaar    let value = substitute(a:msg, '^\~\"[ ]*', '', '')
73882be4849SBram Moolenaar    let value = substitute(value, '^=>[ ]*', '', '')
739113cb513SBram Moolenaar    let value = substitute(value, '\\n\"\r$', '', '')
74082be4849SBram Moolenaar    let value = substitute(value, '\\n\"$', '', '')
741113cb513SBram Moolenaar    let value = substitute(value, '\r', '', '')
74282be4849SBram Moolenaar    let value = substitute(value, '\\t', ' ', 'g')
74382be4849SBram Moolenaar
74482be4849SBram Moolenaar    if value != '' || !empty(s:asm_lines)
74582be4849SBram Moolenaar      call add(s:asm_lines, value)
74682be4849SBram Moolenaar    endif
74782be4849SBram Moolenaar  endif
74882be4849SBram Moolenaarendfunc
74982be4849SBram Moolenaar
750fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
751fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
752fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
753fe386641SBram Moolenaar
754fe386641SBram Moolenaar  for msg in msgs
755fe386641SBram Moolenaar    " remove prefixed NL
756fe386641SBram Moolenaar    if msg[0] == "\n"
757fe386641SBram Moolenaar      let msg = msg[1:]
758fe386641SBram Moolenaar    endif
75982be4849SBram Moolenaar
76082be4849SBram Moolenaar    if s:parsing_disasm_msg
76182be4849SBram Moolenaar      call s:HandleDisasmMsg(msg)
76282be4849SBram Moolenaar    elseif msg != ''
7631b9645deSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
764e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
76545d5f26dSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
766e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
767e09ba7baSBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
768e09ba7baSBram Moolenaar	call s:HandleBreakpointDelete(msg)
7694551c0a9SBram Moolenaar      elseif msg =~ '^=thread-group-started'
7704551c0a9SBram Moolenaar	call s:HandleProgramRun(msg)
77145d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
77245d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
77345d5f26dSBram Moolenaar      elseif msg =~ '^\^error,msg='
77445d5f26dSBram Moolenaar	call s:HandleError(msg)
77582be4849SBram Moolenaar      elseif msg =~ '^disassemble'
77682be4849SBram Moolenaar        let s:parsing_disasm_msg = 1
77782be4849SBram Moolenaar        let s:asm_lines = []
778e09ba7baSBram Moolenaar      endif
779e09ba7baSBram Moolenaar    endif
780e09ba7baSBram Moolenaar  endfor
781e09ba7baSBram Moolenaarendfunc
782e09ba7baSBram Moolenaar
783589edb34SBram Moolenaarfunc s:GotoProgram()
784589edb34SBram Moolenaar  if has('win32')
785589edb34SBram Moolenaar    if executable('powershell')
786589edb34SBram Moolenaar      call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid))
787589edb34SBram Moolenaar    endif
788589edb34SBram Moolenaar  else
789469bdbdeSBram Moolenaar    call win_gotoid(s:ptywin)
790589edb34SBram Moolenaar  endif
791589edb34SBram Moolenaarendfunc
792589edb34SBram Moolenaar
793e09ba7baSBram Moolenaar" Install commands in the current window to control the debugger.
794e09ba7baSBram Moolenaarfunc s:InstallCommands()
795963c1ad5SBram Moolenaar  let save_cpo = &cpo
796963c1ad5SBram Moolenaar  set cpo&vim
797963c1ad5SBram Moolenaar
798589edb34SBram Moolenaar  command -nargs=? Break call s:SetBreakpoint(<q-args>)
79971137fedSBram Moolenaar  command Clear call s:ClearBreakpoint()
800e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
80145d5f26dSBram Moolenaar  command Over call s:SendCommand('-exec-next')
802e09ba7baSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
80360e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
80460e73f2aSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
80560e73f2aSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
806b3307b5eSBram Moolenaar
807b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
808b3307b5eSBram Moolenaar  if s:way == 'prompt'
809b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
810b3307b5eSBram Moolenaar  else
811b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
812b3307b5eSBram Moolenaar  endif
813b3307b5eSBram Moolenaar
81445d5f26dSBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
81545d5f26dSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
816589edb34SBram Moolenaar  command Program call s:GotoProgram()
817b3307b5eSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
81882be4849SBram Moolenaar  command Asm call s:GotoAsmwinOrCreateIt()
81971137fedSBram Moolenaar  command Winbar call s:InstallWinbar()
82045d5f26dSBram Moolenaar
821388a5d4fSBram Moolenaar  if !exists('g:termdebug_map_K') || g:termdebug_map_K
822388a5d4fSBram Moolenaar    let s:k_map_saved = maparg('K', 'n', 0, 1)
82345d5f26dSBram Moolenaar    nnoremap K :Evaluate<CR>
824388a5d4fSBram Moolenaar  endif
8251b9645deSBram Moolenaar
826f0b03c4eSBram Moolenaar  if has('menu') && &mouse != ''
82771137fedSBram Moolenaar    call s:InstallWinbar()
82871137fedSBram Moolenaar
82971137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
83071137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
83171137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
83271137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
83371137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
83471137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
83571137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
83671137fedSBram Moolenaar    endif
83771137fedSBram Moolenaar  endif
838963c1ad5SBram Moolenaar
839963c1ad5SBram Moolenaar  let &cpo = save_cpo
84071137fedSBram Moolenaarendfunc
84171137fedSBram Moolenaar
84271137fedSBram Moolenaarlet s:winbar_winids = []
84371137fedSBram Moolenaar
84471137fedSBram Moolenaar" Install the window toolbar in the current window.
84571137fedSBram Moolenaarfunc s:InstallWinbar()
846c4b533e1SBram Moolenaar  if has('menu') && &mouse != ''
84724a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
84824a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
84924a98a0eSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
85024a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
85160e73f2aSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
85224a98a0eSBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
85371137fedSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
854c4b533e1SBram Moolenaar  endif
855e09ba7baSBram Moolenaarendfunc
856e09ba7baSBram Moolenaar
857e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
858e09ba7baSBram Moolenaarfunc s:DeleteCommands()
859e09ba7baSBram Moolenaar  delcommand Break
86071137fedSBram Moolenaar  delcommand Clear
861e09ba7baSBram Moolenaar  delcommand Step
86245d5f26dSBram Moolenaar  delcommand Over
863e09ba7baSBram Moolenaar  delcommand Finish
86460e73f2aSBram Moolenaar  delcommand Run
86560e73f2aSBram Moolenaar  delcommand Arguments
86660e73f2aSBram Moolenaar  delcommand Stop
867e09ba7baSBram Moolenaar  delcommand Continue
86845d5f26dSBram Moolenaar  delcommand Evaluate
86945d5f26dSBram Moolenaar  delcommand Gdb
87045d5f26dSBram Moolenaar  delcommand Program
871b3623a38SBram Moolenaar  delcommand Source
87282be4849SBram Moolenaar  delcommand Asm
87371137fedSBram Moolenaar  delcommand Winbar
87445d5f26dSBram Moolenaar
8751b884a00SBram Moolenaar  if exists('s:k_map_saved')
8761b884a00SBram Moolenaar    if empty(s:k_map_saved)
8771b884a00SBram Moolenaar      nunmap K
8781b884a00SBram Moolenaar    else
879388a5d4fSBram Moolenaar      call mapset('n', 0, s:k_map_saved)
8801b884a00SBram Moolenaar    endif
881388a5d4fSBram Moolenaar    unlet s:k_map_saved
882388a5d4fSBram Moolenaar  endif
8831b9645deSBram Moolenaar
8841b9645deSBram Moolenaar  if has('menu')
88571137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
88671137fedSBram Moolenaar    let curwinid = win_getid(winnr())
88771137fedSBram Moolenaar    for winid in s:winbar_winids
88871137fedSBram Moolenaar      if win_gotoid(winid)
8891b9645deSBram Moolenaar	aunmenu WinBar.Step
8901b9645deSBram Moolenaar	aunmenu WinBar.Next
8911b9645deSBram Moolenaar	aunmenu WinBar.Finish
8921b9645deSBram Moolenaar	aunmenu WinBar.Cont
89360e73f2aSBram Moolenaar	aunmenu WinBar.Stop
8941b9645deSBram Moolenaar	aunmenu WinBar.Eval
8951b9645deSBram Moolenaar      endif
89671137fedSBram Moolenaar    endfor
89771137fedSBram Moolenaar    call win_gotoid(curwinid)
89871137fedSBram Moolenaar    let s:winbar_winids = []
89971137fedSBram Moolenaar
90071137fedSBram Moolenaar    if exists('s:saved_mousemodel')
90171137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
90271137fedSBram Moolenaar      unlet s:saved_mousemodel
90371137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
90471137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
90571137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
90671137fedSBram Moolenaar      aunmenu PopUp.Evaluate
90771137fedSBram Moolenaar    endif
90871137fedSBram Moolenaar  endif
9091b9645deSBram Moolenaar
91045d5f26dSBram Moolenaar  exe 'sign unplace ' . s:pc_id
91137402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
91237402ed5SBram Moolenaar    for subid in keys(entries)
91337402ed5SBram Moolenaar      exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
91437402ed5SBram Moolenaar    endfor
91545d5f26dSBram Moolenaar  endfor
91645d5f26dSBram Moolenaar  unlet s:breakpoints
91737402ed5SBram Moolenaar  unlet s:breakpoint_locations
918a15b0a93SBram Moolenaar
919a15b0a93SBram Moolenaar  sign undefine debugPC
920a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
921a15b0a93SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
922a15b0a93SBram Moolenaar  endfor
9234551c0a9SBram Moolenaar  let s:BreakpointSigns = []
924e09ba7baSBram Moolenaarendfunc
925e09ba7baSBram Moolenaar
926e09ba7baSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
927589edb34SBram Moolenaarfunc s:SetBreakpoint(at)
92860e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
92960e73f2aSBram Moolenaar  " Interrupt to make it work.
93060e73f2aSBram Moolenaar  let do_continue = 0
93160e73f2aSBram Moolenaar  if !s:stopped
93260e73f2aSBram Moolenaar    let do_continue = 1
933b3307b5eSBram Moolenaar    if s:way == 'prompt'
9344551c0a9SBram Moolenaar      call s:PromptInterrupt()
935b3307b5eSBram Moolenaar    else
93660e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
937b3307b5eSBram Moolenaar    endif
93860e73f2aSBram Moolenaar    sleep 10m
93960e73f2aSBram Moolenaar  endif
940589edb34SBram Moolenaar
941a15b0a93SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
942589edb34SBram Moolenaar  let at = empty(a:at) ?
943589edb34SBram Moolenaar        \ fnameescape(expand('%:p')) . ':' . line('.') : a:at
944589edb34SBram Moolenaar  call s:SendCommand('-break-insert ' . at)
94560e73f2aSBram Moolenaar  if do_continue
94660e73f2aSBram Moolenaar    call s:SendCommand('-exec-continue')
94760e73f2aSBram Moolenaar  endif
948e09ba7baSBram Moolenaarendfunc
949e09ba7baSBram Moolenaar
95071137fedSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
95171137fedSBram Moolenaarfunc s:ClearBreakpoint()
952e09ba7baSBram Moolenaar  let fname = fnameescape(expand('%:p'))
953e09ba7baSBram Moolenaar  let lnum = line('.')
95437402ed5SBram Moolenaar  let bploc = printf('%s:%d', fname, lnum)
95537402ed5SBram Moolenaar  if has_key(s:breakpoint_locations, bploc)
95637402ed5SBram Moolenaar    let idx = 0
95737402ed5SBram Moolenaar    for id in s:breakpoint_locations[bploc]
95837402ed5SBram Moolenaar      if has_key(s:breakpoints, id)
95937402ed5SBram Moolenaar	" Assume this always works, the reply is simply "^done".
96037402ed5SBram Moolenaar	call s:SendCommand('-break-delete ' . id)
96137402ed5SBram Moolenaar	for subid in keys(s:breakpoints[id])
96237402ed5SBram Moolenaar	  exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
96337402ed5SBram Moolenaar	endfor
96437402ed5SBram Moolenaar	unlet s:breakpoints[id]
96537402ed5SBram Moolenaar	unlet s:breakpoint_locations[bploc][idx]
966e09ba7baSBram Moolenaar	break
96737402ed5SBram Moolenaar      else
96837402ed5SBram Moolenaar	let idx += 1
969e09ba7baSBram Moolenaar      endif
970e09ba7baSBram Moolenaar    endfor
97137402ed5SBram Moolenaar    if empty(s:breakpoint_locations[bploc])
97237402ed5SBram Moolenaar      unlet s:breakpoint_locations[bploc]
97337402ed5SBram Moolenaar    endif
97437402ed5SBram Moolenaar  endif
975e09ba7baSBram Moolenaarendfunc
976e09ba7baSBram Moolenaar
97760e73f2aSBram Moolenaarfunc s:Run(args)
97860e73f2aSBram Moolenaar  if a:args != ''
97960e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
98060e73f2aSBram Moolenaar  endif
98160e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
98260e73f2aSBram Moolenaarendfunc
98360e73f2aSBram Moolenaar
98451b0f370SBram Moolenaarfunc s:SendEval(expr)
985113cb513SBram Moolenaar  " clean up expression that may got in because of range
986113cb513SBram Moolenaar  " (newlines and surrounding spaces)
987113cb513SBram Moolenaar  let expr = a:expr
988113cb513SBram Moolenaar  if &filetype ==# 'cobol'
989113cb513SBram Moolenaar    " extra cleanup for COBOL: _every: expression ends with a period,
990113cb513SBram Moolenaar    " a trailing comma is ignored as it commonly separates multiple expr.
991113cb513SBram Moolenaar    let expr = substitute(expr, '\..*', '', '')
992113cb513SBram Moolenaar    let expr = substitute(expr, '[;\n]', ' ', 'g')
993113cb513SBram Moolenaar    let expr = substitute(expr, ',*$', '', '')
994113cb513SBram Moolenaar  else
995113cb513SBram Moolenaar    let expr = substitute(expr, '\n', ' ', 'g')
996113cb513SBram Moolenaar  endif
997113cb513SBram Moolenaar  let expr = substitute(expr, '^ *\(.*\) *', '\1', '')
998113cb513SBram Moolenaar
999113cb513SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . expr . '"')
1000113cb513SBram Moolenaar  let s:evalexpr = expr
100151b0f370SBram Moolenaarendfunc
100251b0f370SBram Moolenaar
100345d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
100445d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
100545d5f26dSBram Moolenaar  if a:arg != ''
100645d5f26dSBram Moolenaar    let expr = a:arg
100745d5f26dSBram Moolenaar  elseif a:range == 2
100845d5f26dSBram Moolenaar    let pos = getcurpos()
100945d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
101045d5f26dSBram Moolenaar    let regt = getregtype('v')
101145d5f26dSBram Moolenaar    normal! gv"vy
101245d5f26dSBram Moolenaar    let expr = @v
101345d5f26dSBram Moolenaar    call setpos('.', pos)
101445d5f26dSBram Moolenaar    call setreg('v', reg, regt)
101545d5f26dSBram Moolenaar  else
101645d5f26dSBram Moolenaar    let expr = expand('<cexpr>')
101745d5f26dSBram Moolenaar  endif
101822f1d0e3SBram Moolenaar  let s:ignoreEvalError = 0
101951b0f370SBram Moolenaar  call s:SendEval(expr)
102045d5f26dSBram Moolenaarendfunc
102145d5f26dSBram Moolenaar
102222f1d0e3SBram Moolenaarlet s:ignoreEvalError = 0
102351b0f370SBram Moolenaarlet s:evalFromBalloonExpr = 0
102451b0f370SBram Moolenaar
102545d5f26dSBram Moolenaar" Handle the result of data-evaluate-expression
102645d5f26dSBram Moolenaarfunc s:HandleEvaluate(msg)
10271b9645deSBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
10281b9645deSBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
102951b0f370SBram Moolenaar  if s:evalFromBalloonExpr
103051b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
103151b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
103251b0f370SBram Moolenaar    else
103351b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
103451b0f370SBram Moolenaar    endif
103551b0f370SBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
103651b0f370SBram Moolenaar  else
10371b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
103851b0f370SBram Moolenaar  endif
10391b9645deSBram Moolenaar
10407f2e9d7cSBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
10411b9645deSBram Moolenaar    " Looks like a pointer, also display what it points to.
104222f1d0e3SBram Moolenaar    let s:ignoreEvalError = 1
104351b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
104451b0f370SBram Moolenaar  else
104551b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
10461b9645deSBram Moolenaar  endif
104745d5f26dSBram Moolenaarendfunc
104845d5f26dSBram Moolenaar
104951b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
105051b0f370SBram Moolenaar" if there is any.
105151b0f370SBram Moolenaarfunc TermDebugBalloonExpr()
1052b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
1053396e829fSBram Moolenaar    return ''
1054b3307b5eSBram Moolenaar  endif
1055b3307b5eSBram Moolenaar  if !s:stopped
1056b3307b5eSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
1057b3307b5eSBram Moolenaar    " mouse triggers a balloon.
1058396e829fSBram Moolenaar    return ''
105951b0f370SBram Moolenaar  endif
106051b0f370SBram Moolenaar  let s:evalFromBalloonExpr = 1
106151b0f370SBram Moolenaar  let s:evalFromBalloonExprResult = ''
106222f1d0e3SBram Moolenaar  let s:ignoreEvalError = 1
106322f1d0e3SBram Moolenaar  call s:SendEval(v:beval_text)
106451b0f370SBram Moolenaar  return ''
106551b0f370SBram Moolenaarendfunc
106651b0f370SBram Moolenaar
106745d5f26dSBram Moolenaar" Handle an error.
106845d5f26dSBram Moolenaarfunc s:HandleError(msg)
106922f1d0e3SBram Moolenaar  if s:ignoreEvalError
107051b0f370SBram Moolenaar    " Result of s:SendEval() failed, ignore.
107122f1d0e3SBram Moolenaar    let s:ignoreEvalError = 0
107222f1d0e3SBram Moolenaar    let s:evalFromBalloonExpr = 0
107351b0f370SBram Moolenaar    return
107451b0f370SBram Moolenaar  endif
1075113cb513SBram Moolenaar  let msgVal = substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
1076113cb513SBram Moolenaar  echoerr substitute(msgVal, '\\"', '"', 'g')
107745d5f26dSBram Moolenaarendfunc
107845d5f26dSBram Moolenaar
1079b3307b5eSBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
1080b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
1081c4b533e1SBram Moolenaar    new
1082b3307b5eSBram Moolenaar    let s:sourcewin = win_getid(winnr())
1083c4b533e1SBram Moolenaar    call s:InstallWinbar()
1084c4b533e1SBram Moolenaar  endif
1085c4b533e1SBram Moolenaarendfunc
1086c4b533e1SBram Moolenaar
108782be4849SBram Moolenaarfunc s:GotoAsmwinOrCreateIt()
108882be4849SBram Moolenaar  if !win_gotoid(s:asmwin)
108982be4849SBram Moolenaar    if win_gotoid(s:sourcewin)
109082be4849SBram Moolenaar      exe 'rightbelow new'
109182be4849SBram Moolenaar    else
109282be4849SBram Moolenaar      exe 'new'
109382be4849SBram Moolenaar    endif
109482be4849SBram Moolenaar
109582be4849SBram Moolenaar    let s:asmwin = win_getid(winnr())
109682be4849SBram Moolenaar
109782be4849SBram Moolenaar    setlocal nowrap
109882be4849SBram Moolenaar    setlocal number
109982be4849SBram Moolenaar    setlocal noswapfile
110082be4849SBram Moolenaar    setlocal buftype=nofile
1101*519cc559SBram Moolenaar    setlocal modifiable
110282be4849SBram Moolenaar
110382be4849SBram Moolenaar    let asmbuf = bufnr('Termdebug-asm-listing')
110482be4849SBram Moolenaar    if asmbuf > 0
110582be4849SBram Moolenaar      exe 'buffer' . asmbuf
110682be4849SBram Moolenaar    else
110782be4849SBram Moolenaar      exe 'file Termdebug-asm-listing'
110882be4849SBram Moolenaar    endif
110982be4849SBram Moolenaar
111082be4849SBram Moolenaar    if exists('g:termdebug_disasm_window')
111182be4849SBram Moolenaar      if g:termdebug_disasm_window > 1
111282be4849SBram Moolenaar        exe 'resize ' . g:termdebug_disasm_window
111382be4849SBram Moolenaar      endif
111482be4849SBram Moolenaar    endif
111582be4849SBram Moolenaar  endif
111682be4849SBram Moolenaar
111782be4849SBram Moolenaar  if s:asm_addr != ''
111882be4849SBram Moolenaar    let lnum = search('^' . s:asm_addr)
111982be4849SBram Moolenaar    if lnum == 0
112082be4849SBram Moolenaar      if s:stopped
112182be4849SBram Moolenaar        call s:SendCommand('disassemble $pc')
112282be4849SBram Moolenaar      endif
112382be4849SBram Moolenaar    else
112482be4849SBram Moolenaar      exe 'sign unplace ' . s:asm_id
112582be4849SBram Moolenaar      exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
112682be4849SBram Moolenaar    endif
112782be4849SBram Moolenaar  endif
112882be4849SBram Moolenaarendfunc
112982be4849SBram Moolenaar
1130e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
1131e09ba7baSBram Moolenaar" Will update the sign that shows the current position.
1132e09ba7baSBram Moolenaarfunc s:HandleCursor(msg)
1133fe386641SBram Moolenaar  let wid = win_getid(winnr())
1134fe386641SBram Moolenaar
113560e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
11364551c0a9SBram Moolenaar    call ch_log('program stopped')
113760e73f2aSBram Moolenaar    let s:stopped = 1
113860e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
11394551c0a9SBram Moolenaar    call ch_log('program running')
114060e73f2aSBram Moolenaar    let s:stopped = 0
114160e73f2aSBram Moolenaar  endif
114260e73f2aSBram Moolenaar
1143a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
1144a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
1145a15b0a93SBram Moolenaar  else
1146a15b0a93SBram Moolenaar    let fname = ''
1147a15b0a93SBram Moolenaar  endif
114882be4849SBram Moolenaar
114982be4849SBram Moolenaar  if a:msg =~ 'addr='
115082be4849SBram Moolenaar    let asm_addr = s:GetAsmAddr(a:msg)
115182be4849SBram Moolenaar    if asm_addr != ''
115282be4849SBram Moolenaar      let s:asm_addr = asm_addr
115382be4849SBram Moolenaar
115482be4849SBram Moolenaar      let curwinid = win_getid(winnr())
115582be4849SBram Moolenaar      if win_gotoid(s:asmwin)
115682be4849SBram Moolenaar        let lnum = search('^' . s:asm_addr)
115782be4849SBram Moolenaar        if lnum == 0
115882be4849SBram Moolenaar          call s:SendCommand('disassemble $pc')
115982be4849SBram Moolenaar        else
116082be4849SBram Moolenaar          exe 'sign unplace ' . s:asm_id
116182be4849SBram Moolenaar          exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
116282be4849SBram Moolenaar        endif
116382be4849SBram Moolenaar
116482be4849SBram Moolenaar        call win_gotoid(curwinid)
116582be4849SBram Moolenaar      endif
116682be4849SBram Moolenaar    endif
116782be4849SBram Moolenaar  endif
116882be4849SBram Moolenaar
11691b9645deSBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
1170e09ba7baSBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
1171fe386641SBram Moolenaar    if lnum =~ '^[0-9]*$'
11724551c0a9SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
11731b9645deSBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
1174fe386641SBram Moolenaar	if &modified
1175fe386641SBram Moolenaar	  " TODO: find existing window
1176fe386641SBram Moolenaar	  exe 'split ' . fnameescape(fname)
1177b3307b5eSBram Moolenaar	  let s:sourcewin = win_getid(winnr())
1178c4b533e1SBram Moolenaar	  call s:InstallWinbar()
1179fe386641SBram Moolenaar	else
1180fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
1181fe386641SBram Moolenaar	endif
1182fe386641SBram Moolenaar      endif
1183fe386641SBram Moolenaar      exe lnum
1184*519cc559SBram Moolenaar      normal! zv
118501164a65SBram Moolenaar      exe 'sign unplace ' . s:pc_id
118639f7aa3cSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname
1187cb80aa2dSBram Moolenaar      if !exists('b:save_signcolumn')
1188cb80aa2dSBram Moolenaar	let b:save_signcolumn = &signcolumn
1189cb80aa2dSBram Moolenaar	call add(s:signcolumn_buflist, bufnr())
1190cb80aa2dSBram Moolenaar      endif
1191fe386641SBram Moolenaar      setlocal signcolumn=yes
1192fe386641SBram Moolenaar    endif
11934551c0a9SBram Moolenaar  elseif !s:stopped || fname != ''
1194fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
1195fe386641SBram Moolenaar  endif
1196fe386641SBram Moolenaar
1197fe386641SBram Moolenaar  call win_gotoid(wid)
1198e09ba7baSBram Moolenaarendfunc
1199e09ba7baSBram Moolenaar
1200de1a8314SBram Moolenaarlet s:BreakpointSigns = []
1201a15b0a93SBram Moolenaar
120237402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid)
120337402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
120437402ed5SBram Moolenaar  if index(s:BreakpointSigns, nr) == -1
120537402ed5SBram Moolenaar    call add(s:BreakpointSigns, nr)
120637402ed5SBram Moolenaar    exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint"
1207de1a8314SBram Moolenaar  endif
1208de1a8314SBram Moolenaarendfunc
1209de1a8314SBram Moolenaar
121037402ed5SBram Moolenaarfunc! s:SplitMsg(s)
121137402ed5SBram Moolenaar  return split(a:s, '{.\{-}}\zs')
12125378e1cfSBram Moolenaarendfunction
12135378e1cfSBram Moolenaar
1214e09ba7baSBram Moolenaar" Handle setting a breakpoint
1215e09ba7baSBram Moolenaar" Will update the sign that shows the breakpoint
1216e09ba7baSBram Moolenaarfunc s:HandleNewBreakpoint(msg)
12176dccc962SBram Moolenaar  if a:msg !~ 'fullname='
12186dccc962SBram Moolenaar    " a watch does not have a file name
12196dccc962SBram Moolenaar    return
12206dccc962SBram Moolenaar  endif
12215378e1cfSBram Moolenaar  for msg in s:SplitMsg(a:msg)
12225378e1cfSBram Moolenaar    let fname = s:GetFullname(msg)
12235378e1cfSBram Moolenaar    if empty(fname)
12245378e1cfSBram Moolenaar      continue
12255378e1cfSBram Moolenaar    endif
12265378e1cfSBram Moolenaar    let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
12275378e1cfSBram Moolenaar    if empty(nr)
1228e09ba7baSBram Moolenaar      return
1229fe386641SBram Moolenaar    endif
1230e09ba7baSBram Moolenaar
123137402ed5SBram Moolenaar    " If "nr" is 123 it becomes "123.0" and subid is "0".
123237402ed5SBram Moolenaar    " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded.
123337402ed5SBram Moolenaar    let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0')
123437402ed5SBram Moolenaar    call s:CreateBreakpoint(id, subid)
123537402ed5SBram Moolenaar
123637402ed5SBram Moolenaar    if has_key(s:breakpoints, id)
123737402ed5SBram Moolenaar      let entries = s:breakpoints[id]
123837402ed5SBram Moolenaar    else
123937402ed5SBram Moolenaar      let entries = {}
124037402ed5SBram Moolenaar      let s:breakpoints[id] = entries
124137402ed5SBram Moolenaar    endif
124237402ed5SBram Moolenaar    if has_key(entries, subid)
124337402ed5SBram Moolenaar      let entry = entries[subid]
1244e09ba7baSBram Moolenaar    else
1245e09ba7baSBram Moolenaar      let entry = {}
124637402ed5SBram Moolenaar      let entries[subid] = entry
1247fe386641SBram Moolenaar    endif
1248e09ba7baSBram Moolenaar
12495378e1cfSBram Moolenaar    let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
1250e09ba7baSBram Moolenaar    let entry['fname'] = fname
1251e09ba7baSBram Moolenaar    let entry['lnum'] = lnum
12521b9645deSBram Moolenaar
125337402ed5SBram Moolenaar    let bploc = printf('%s:%d', fname, lnum)
125437402ed5SBram Moolenaar    if !has_key(s:breakpoint_locations, bploc)
125537402ed5SBram Moolenaar      let s:breakpoint_locations[bploc] = []
125637402ed5SBram Moolenaar    endif
125737402ed5SBram Moolenaar    let s:breakpoint_locations[bploc] += [id]
125837402ed5SBram Moolenaar
12591b9645deSBram Moolenaar    if bufloaded(fname)
126037402ed5SBram Moolenaar      call s:PlaceSign(id, subid, entry)
12611b9645deSBram Moolenaar    endif
12625378e1cfSBram Moolenaar  endfor
12631b9645deSBram Moolenaarendfunc
12641b9645deSBram Moolenaar
126537402ed5SBram Moolenaarfunc s:PlaceSign(id, subid, entry)
126637402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
12673132cdddSBram Moolenaar  exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' priority=110 file=' . a:entry['fname']
12681b9645deSBram Moolenaar  let a:entry['placed'] = 1
1269e09ba7baSBram Moolenaarendfunc
1270e09ba7baSBram Moolenaar
1271e09ba7baSBram Moolenaar" Handle deleting a breakpoint
1272e09ba7baSBram Moolenaar" Will remove the sign that shows the breakpoint
1273e09ba7baSBram Moolenaarfunc s:HandleBreakpointDelete(msg)
127437402ed5SBram Moolenaar  let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
127537402ed5SBram Moolenaar  if empty(id)
1276e09ba7baSBram Moolenaar    return
1277e09ba7baSBram Moolenaar  endif
127837402ed5SBram Moolenaar  if has_key(s:breakpoints, id)
127937402ed5SBram Moolenaar    for [subid, entry] in items(s:breakpoints[id])
12801b9645deSBram Moolenaar      if has_key(entry, 'placed')
128137402ed5SBram Moolenaar	exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
12821b9645deSBram Moolenaar	unlet entry['placed']
12831b9645deSBram Moolenaar      endif
12845378e1cfSBram Moolenaar    endfor
128537402ed5SBram Moolenaar    unlet s:breakpoints[id]
128637402ed5SBram Moolenaar  endif
1287c572da5fSBram Moolenaarendfunc
12881b9645deSBram Moolenaar
12894551c0a9SBram Moolenaar" Handle the debugged program starting to run.
12904551c0a9SBram Moolenaar" Will store the process ID in s:pid
12914551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
12924551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
12934551c0a9SBram Moolenaar  if nr == 0
12944551c0a9SBram Moolenaar    return
12954551c0a9SBram Moolenaar  endif
12964551c0a9SBram Moolenaar  let s:pid = nr
12974551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
12984551c0a9SBram Moolenaarendfunc
12994551c0a9SBram Moolenaar
13001b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
13011b9645deSBram Moolenaarfunc s:BufRead()
13021b9645deSBram Moolenaar  let fname = expand('<afile>:p')
130337402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
130437402ed5SBram Moolenaar    for [subid, entry] in items(entries)
13051b9645deSBram Moolenaar      if entry['fname'] == fname
130637402ed5SBram Moolenaar	call s:PlaceSign(id, subid, entry)
13071b9645deSBram Moolenaar      endif
13081b9645deSBram Moolenaar    endfor
130937402ed5SBram Moolenaar  endfor
13101b9645deSBram Moolenaarendfunc
13111b9645deSBram Moolenaar
13121b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
13131b9645deSBram Moolenaarfunc s:BufUnloaded()
13141b9645deSBram Moolenaar  let fname = expand('<afile>:p')
131537402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
131637402ed5SBram Moolenaar    for [subid, entry] in items(entries)
13171b9645deSBram Moolenaar      if entry['fname'] == fname
13181b9645deSBram Moolenaar	let entry['placed'] = 0
13191b9645deSBram Moolenaar      endif
13201b9645deSBram Moolenaar    endfor
132137402ed5SBram Moolenaar  endfor
13221b9645deSBram Moolenaarendfunc
1323ca4cc018SBram Moolenaar
1324ca4cc018SBram Moolenaarlet &cpo = s:keepcpo
1325ca4cc018SBram Moolenaarunlet s:keepcpo
1326