1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3b3307b5eSBram Moolenaar" Author: Bram Moolenaar
4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license"
5*6aa57295SBram Moolenaar" Last Change: 2021 Aug 06
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
124*6aa57295SBram Moolenaar  if exists('#User#TermdebugStartPre')
125*6aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStartPre
126*6aa57295SBram Moolenaar  endif
127*6aa57295SBram 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
174*6aa57295SBram Moolenaar
175*6aa57295SBram Moolenaar  if exists('#User#TermdebugStartPost')
176*6aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStartPost
177*6aa57295SBram 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')})
330b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
331b3307b5eSBram Moolenaarendfunc
332b3307b5eSBram Moolenaar
333b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
334b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
335b3307b5eSBram Moolenaar  if s:vertical
336b3307b5eSBram Moolenaar    vertical new
337b3307b5eSBram Moolenaar  else
338b3307b5eSBram Moolenaar    new
339b3307b5eSBram Moolenaar  endif
340b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
341b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
342b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
343b3307b5eSBram Moolenaar  set buftype=prompt
344b3307b5eSBram Moolenaar  file gdb
345b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
346b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
347b3307b5eSBram Moolenaar
348b3307b5eSBram Moolenaar  if s:vertical
349b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
350b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
351b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
352b3307b5eSBram Moolenaar  endif
353b3307b5eSBram Moolenaar
354b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
355b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
356b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
357b3307b5eSBram Moolenaar
358b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
359b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
360b3307b5eSBram Moolenaar
361b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
362b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
363b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
364b3307b5eSBram Moolenaar	\ })
365b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
366b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
367b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
368b3307b5eSBram Moolenaar    return
369b3307b5eSBram Moolenaar  endif
3704551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
3714551c0a9SBram Moolenaar  set modified
372b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
373b3307b5eSBram Moolenaar
37491359014SBram Moolenaar  " Interpret commands while the target is running.  This should usually only
375b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
376b3307b5eSBram Moolenaar  " target is running.
377b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
378b3307b5eSBram Moolenaar  " Older gdb uses a different command.
379b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
380b3307b5eSBram Moolenaar
381b3307b5eSBram Moolenaar  let s:ptybuf = 0
382b3307b5eSBram Moolenaar  if has('win32')
383b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
384b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
385b3307b5eSBram Moolenaar  elseif has('terminal')
386b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
387b3307b5eSBram Moolenaar    " gdb window.
388b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
389b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
390b3307b5eSBram Moolenaar	  \ })
391b3307b5eSBram Moolenaar    if s:ptybuf == 0
392b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
393b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
394b3307b5eSBram Moolenaar      return
395b3307b5eSBram Moolenaar    endif
396b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
397b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
398b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
399b3307b5eSBram Moolenaar
400b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
401b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
402b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
403b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
404b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
405b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
406b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
407b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
408b3307b5eSBram Moolenaar  else
409b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
410b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
411b3307b5eSBram Moolenaar  endif
412b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
413b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
414b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
415b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
416b3307b5eSBram Moolenaar
417b3307b5eSBram Moolenaar  " Set arguments to be run
418b3307b5eSBram Moolenaar  if len(proc_args)
419b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
420b3307b5eSBram Moolenaar  endif
421b3307b5eSBram Moolenaar
422b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
423b3307b5eSBram Moolenaar  startinsert
424b3307b5eSBram Moolenaarendfunc
425b3307b5eSBram Moolenaar
426b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
42738baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
42838baa3e6SBram Moolenaar  " There can be only one.
42938baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
43038baa3e6SBram Moolenaar
43145d5f26dSBram Moolenaar  " Install debugger commands in the text window.
432b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
433e09ba7baSBram Moolenaar  call s:InstallCommands()
43445d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
435e09ba7baSBram Moolenaar
43651b0f370SBram Moolenaar  " Enable showing a balloon with eval info
437246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
438246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
43951b0f370SBram Moolenaar    if has("balloon_eval")
44051b0f370SBram Moolenaar      set ballooneval
441246fe03dSBram Moolenaar    endif
44251b0f370SBram Moolenaar    if has("balloon_eval_term")
44351b0f370SBram Moolenaar      set balloonevalterm
44451b0f370SBram Moolenaar    endif
44551b0f370SBram Moolenaar  endif
44651b0f370SBram Moolenaar
4475378e1cfSBram Moolenaar  " Contains breakpoints that have been placed, key is a string with the GDB
4485378e1cfSBram Moolenaar  " breakpoint number.
44937402ed5SBram Moolenaar  " Each entry is a dict, containing the sub-breakpoints.  Key is the subid.
45037402ed5SBram Moolenaar  " For a breakpoint that is just a number the subid is zero.
45137402ed5SBram Moolenaar  " For a breakpoint "123.4" the id is "123" and subid is "4".
45237402ed5SBram Moolenaar  " Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
45337402ed5SBram Moolenaar  " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
454e09ba7baSBram Moolenaar  let s:breakpoints = {}
4551b9645deSBram Moolenaar
45637402ed5SBram Moolenaar  " Contains breakpoints by file/lnum.  The key is "fname:lnum".
45737402ed5SBram Moolenaar  " Each entry is a list of breakpoint IDs at that position.
45837402ed5SBram Moolenaar  let s:breakpoint_locations = {}
45937402ed5SBram Moolenaar
4601b9645deSBram Moolenaar  augroup TermDebug
4611b9645deSBram Moolenaar    au BufRead * call s:BufRead()
4621b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
463f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
4641b9645deSBram Moolenaar  augroup END
46532c67ba7SBram Moolenaar
466b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
467b3307b5eSBram Moolenaar  " window.
46832c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
46932c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
47032c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
47132c67ba7SBram Moolenaar  endif
472c572da5fSBram Moolenaarendfunc
473c572da5fSBram Moolenaar
474b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
475b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
476b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
477b3307b5eSBram Moolenaar  if s:way == 'prompt'
478b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
479b3307b5eSBram Moolenaar  else
480b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
481b3307b5eSBram Moolenaar  endif
482b3307b5eSBram Moolenaarendfunc
483b3307b5eSBram Moolenaar
484b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
485b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
486b3307b5eSBram Moolenaar  if s:way == 'prompt'
487b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
488b3307b5eSBram Moolenaar  else
489b3307b5eSBram Moolenaar    let do_continue = 0
490b3307b5eSBram Moolenaar    if !s:stopped
491b3307b5eSBram Moolenaar      let do_continue = 1
492b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
493b3307b5eSBram Moolenaar      sleep 10m
494b3307b5eSBram Moolenaar    endif
495b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
496b3307b5eSBram Moolenaar    if do_continue
497b3307b5eSBram Moolenaar      Continue
498b3307b5eSBram Moolenaar    endif
499b3307b5eSBram Moolenaar  endif
500b3307b5eSBram Moolenaarendfunc
501b3307b5eSBram Moolenaar
502b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
503b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
504b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
505b3307b5eSBram Moolenaarendfunc
506b3307b5eSBram Moolenaar
5074551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
5084551c0a9SBram Moolenaar" breakpoint.
509b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
5102ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
5112ed890f1SBram Moolenaar  if has('win32')
5122ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
5132ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
5144551c0a9SBram Moolenaar    if s:pid == 0
5154551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
5164551c0a9SBram Moolenaar    else
5174551c0a9SBram Moolenaar      call debugbreak(s:pid)
5184551c0a9SBram Moolenaar    endif
5192ed890f1SBram Moolenaar  else
5202ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
5212ed890f1SBram Moolenaar  endif
522b3307b5eSBram Moolenaarendfunc
523b3307b5eSBram Moolenaar
524b3307b5eSBram Moolenaar" Function called when gdb outputs text.
525b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
526b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
527b3307b5eSBram Moolenaar
528b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
529b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
530a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
531b3307b5eSBram Moolenaar    return
532b3307b5eSBram Moolenaar  endif
533b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
534b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
535b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
536b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
537b3307b5eSBram Moolenaar      unlet s:evalexpr
538b3307b5eSBram Moolenaar      return
539b3307b5eSBram Moolenaar    endif
540b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
541b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
542b3307b5eSBram Moolenaar  else
543b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
544b3307b5eSBram Moolenaar    return
545b3307b5eSBram Moolenaar  endif
546b3307b5eSBram Moolenaar
547b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
548b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
549b3307b5eSBram Moolenaar
550b3307b5eSBram Moolenaar  " Add the output above the current prompt.
551b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
5524551c0a9SBram Moolenaar  set modified
553b3307b5eSBram Moolenaar
554b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
555b3307b5eSBram Moolenaarendfunc
556b3307b5eSBram Moolenaar
557b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
558b3307b5eSBram Moolenaar" to the next ", unescaping characters.
559b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
560b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
561a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
562b3307b5eSBram Moolenaar    return
563b3307b5eSBram Moolenaar  endif
564b3307b5eSBram Moolenaar  let result = ''
565b3307b5eSBram Moolenaar  let i = 1
566b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
567b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
568b3307b5eSBram Moolenaar      let i += 1
569b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
570b3307b5eSBram Moolenaar	" drop \n
571b3307b5eSBram Moolenaar	let i += 1
572b3307b5eSBram Moolenaar	continue
573589edb34SBram Moolenaar      elseif a:quotedText[i] == 't'
574589edb34SBram Moolenaar	" append \t
575589edb34SBram Moolenaar	let i += 1
576589edb34SBram Moolenaar	let result .= "\t"
577589edb34SBram Moolenaar	continue
578b3307b5eSBram Moolenaar      endif
579b3307b5eSBram Moolenaar    endif
580b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
581b3307b5eSBram Moolenaar    let i += 1
582b3307b5eSBram Moolenaar  endwhile
583b3307b5eSBram Moolenaar  return result
584b3307b5eSBram Moolenaarendfunc
585b3307b5eSBram Moolenaar
586a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
587a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
5885378e1cfSBram Moolenaar  if a:msg !~ 'fullname'
5895378e1cfSBram Moolenaar    return ''
5905378e1cfSBram Moolenaar  endif
591a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
592a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
593a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
594a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
595a15b0a93SBram Moolenaar  endif
596a15b0a93SBram Moolenaar  return name
597a15b0a93SBram Moolenaarendfunc
598a15b0a93SBram Moolenaar
59982be4849SBram Moolenaar" Extract the "addr" value from a gdb message with addr="0x0001234".
60082be4849SBram Moolenaarfunc s:GetAsmAddr(msg)
60182be4849SBram Moolenaar  if a:msg !~ 'addr='
60282be4849SBram Moolenaar    return ''
60382be4849SBram Moolenaar  endif
60482be4849SBram Moolenaar  let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', ''))
60582be4849SBram Moolenaar  return addr
60682be4849SBram Moolenaarendfunc
607*6aa57295SBram Moolenaar
608b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
609*6aa57295SBram Moolenaar  if exists('#User#TermdebugStopPre')
610*6aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStopPre
611*6aa57295SBram Moolenaar  endif
612*6aa57295SBram Moolenaar
613fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
614b3623a38SBram Moolenaar  unlet s:gdbwin
615e09ba7baSBram Moolenaar
616b3307b5eSBram Moolenaar  call s:EndDebugCommon()
617b3307b5eSBram Moolenaarendfunc
618b3307b5eSBram Moolenaar
619b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
620e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
621e09ba7baSBram Moolenaar
622b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
623b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
624b3307b5eSBram Moolenaar  endif
625b3307b5eSBram Moolenaar
626cb80aa2dSBram Moolenaar  " Restore 'signcolumn' in all buffers for which it was set.
627b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
628cb80aa2dSBram Moolenaar  let was_buf = bufnr()
629cb80aa2dSBram Moolenaar  for bufnr in s:signcolumn_buflist
630cb80aa2dSBram Moolenaar    if bufexists(bufnr)
631cb80aa2dSBram Moolenaar      exe bufnr .. "buf"
632cb80aa2dSBram Moolenaar      if exists('b:save_signcolumn')
633cb80aa2dSBram Moolenaar	let &signcolumn = b:save_signcolumn
634cb80aa2dSBram Moolenaar	unlet b:save_signcolumn
635cb80aa2dSBram Moolenaar      endif
636cb80aa2dSBram Moolenaar    endif
637cb80aa2dSBram Moolenaar  endfor
638cb80aa2dSBram Moolenaar  exe was_buf .. "buf"
639cb80aa2dSBram Moolenaar
640e09ba7baSBram Moolenaar  call s:DeleteCommands()
641e09ba7baSBram Moolenaar
642e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
643b3307b5eSBram Moolenaar
64438baa3e6SBram Moolenaar  if s:save_columns > 0
64538baa3e6SBram Moolenaar    let &columns = s:save_columns
64638baa3e6SBram Moolenaar  endif
6471b9645deSBram Moolenaar
648246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
649246fe03dSBram Moolenaar    set balloonexpr=
65051b0f370SBram Moolenaar    if has("balloon_eval")
65151b0f370SBram Moolenaar      set noballooneval
652246fe03dSBram Moolenaar    endif
65351b0f370SBram Moolenaar    if has("balloon_eval_term")
65451b0f370SBram Moolenaar      set noballoonevalterm
65551b0f370SBram Moolenaar    endif
65651b0f370SBram Moolenaar  endif
65751b0f370SBram Moolenaar
658*6aa57295SBram Moolenaar  if exists('#User#TermdebugStopPost')
659*6aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStopPost
660*6aa57295SBram Moolenaar  endif
661*6aa57295SBram Moolenaar
6621b9645deSBram Moolenaar  au! TermDebug
663fe386641SBram Moolenaarendfunc
664fe386641SBram Moolenaar
665b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
666*6aa57295SBram Moolenaar  if exists('#User#TermdebugStopPre')
667*6aa57295SBram Moolenaar    doauto <nomodeline> User TermdebugStopPre
668*6aa57295SBram Moolenaar  endif
669*6aa57295SBram Moolenaar
670b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
671b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
6724551c0a9SBram Moolenaar  set nomodified
673b3307b5eSBram Moolenaar  close
674b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
675b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
676b3307b5eSBram Moolenaar  endif
677b3307b5eSBram Moolenaar
678b3307b5eSBram Moolenaar  call s:EndDebugCommon()
679b3307b5eSBram Moolenaar  unlet s:gdbwin
680b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
681b3307b5eSBram Moolenaarendfunc
682b3307b5eSBram Moolenaar
68382be4849SBram Moolenaar" Disassembly window - added by Michael Sartain
68482be4849SBram Moolenaar"
68582be4849SBram Moolenaar" - CommOutput: disassemble $pc
68682be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n"
68782be4849SBram Moolenaar" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n"
68882be4849SBram Moolenaar" - CommOutput: ~"   0x0000555556466f69 <+0>:\tpush   rbp\n"
68982be4849SBram Moolenaar" ...
69082be4849SBram Moolenaar" - CommOutput: ~"   0x0000555556467cd0:\tpop    rbp\n"
69182be4849SBram Moolenaar" - CommOutput: ~"   0x0000555556467cd1:\tret    \n"
69282be4849SBram Moolenaar" - CommOutput: ~"End of assembler dump.\n"
69382be4849SBram Moolenaar" - CommOutput: ^done
69482be4849SBram Moolenaar
69582be4849SBram Moolenaar" - CommOutput: disassemble $pc
69682be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n"
69782be4849SBram Moolenaar" - CommOutput: &"No function contains specified address.\n"
69882be4849SBram Moolenaar" - CommOutput: ^error,msg="No function contains specified address."
69982be4849SBram Moolenaarfunc s:HandleDisasmMsg(msg)
70082be4849SBram Moolenaar  if a:msg =~ '^\^done'
70182be4849SBram Moolenaar    let curwinid = win_getid(winnr())
70282be4849SBram Moolenaar    if win_gotoid(s:asmwin)
70382be4849SBram Moolenaar      silent normal! gg0"_dG
70482be4849SBram Moolenaar      call setline(1, s:asm_lines)
70582be4849SBram Moolenaar      set nomodified
70682be4849SBram Moolenaar      set filetype=asm
70782be4849SBram Moolenaar
70882be4849SBram Moolenaar      let lnum = search('^' . s:asm_addr)
70982be4849SBram Moolenaar      if lnum != 0
71082be4849SBram Moolenaar        exe 'sign unplace ' . s:asm_id
71182be4849SBram Moolenaar        exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
71282be4849SBram Moolenaar      endif
71382be4849SBram Moolenaar
71482be4849SBram Moolenaar      call win_gotoid(curwinid)
71582be4849SBram Moolenaar    endif
71682be4849SBram Moolenaar
71782be4849SBram Moolenaar    let s:parsing_disasm_msg = 0
71882be4849SBram Moolenaar    let s:asm_lines = []
71982be4849SBram Moolenaar  elseif a:msg =~ '^\^error,msg='
72082be4849SBram Moolenaar    if s:parsing_disasm_msg == 1
72182be4849SBram Moolenaar      " Disassemble call ran into an error. This can happen when gdb can't
72282be4849SBram Moolenaar      " find the function frame address, so let's try to disassemble starting
72382be4849SBram Moolenaar      " at current PC
72482be4849SBram Moolenaar      call s:SendCommand('disassemble $pc,+100')
72582be4849SBram Moolenaar    endif
72682be4849SBram Moolenaar    let s:parsing_disasm_msg = 0
72782be4849SBram Moolenaar  elseif a:msg =~ '\&\"disassemble \$pc'
72882be4849SBram Moolenaar    if a:msg =~ '+100'
72982be4849SBram Moolenaar      " This is our second disasm attempt
73082be4849SBram Moolenaar      let s:parsing_disasm_msg = 2
73182be4849SBram Moolenaar    endif
73282be4849SBram Moolenaar  else
73382be4849SBram Moolenaar    let value = substitute(a:msg, '^\~\"[ ]*', '', '')
73482be4849SBram Moolenaar    let value = substitute(value, '^=>[ ]*', '', '')
73582be4849SBram Moolenaar    let value = substitute(value, '\\n\"
73682be4849SBram Moolenaar$', '', '')
73782be4849SBram Moolenaar    let value = substitute(value, '\\n\"$', '', '')
73882be4849SBram Moolenaar    let value = substitute(value, '
73982be4849SBram Moolenaar', '', '')
74082be4849SBram Moolenaar    let value = substitute(value, '\\t', ' ', 'g')
74182be4849SBram Moolenaar
74282be4849SBram Moolenaar    if value != '' || !empty(s:asm_lines)
74382be4849SBram Moolenaar      call add(s:asm_lines, value)
74482be4849SBram Moolenaar    endif
74582be4849SBram Moolenaar  endif
746fe386641SBram Moolenaarendfunc
747fe386641SBram Moolenaar
748fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
749fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
750fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
751fe386641SBram Moolenaar
752fe386641SBram Moolenaar  for msg in msgs
753fe386641SBram Moolenaar    " remove prefixed NL
754fe386641SBram Moolenaar    if msg[0] == "\n"
75582be4849SBram Moolenaar      let msg = msg[1:]
75682be4849SBram Moolenaar    endif
75782be4849SBram Moolenaar
75882be4849SBram Moolenaar    if s:parsing_disasm_msg
7591b9645deSBram Moolenaar      call s:HandleDisasmMsg(msg)
760e09ba7baSBram Moolenaar    elseif msg != ''
76145d5f26dSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
762e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
763e09ba7baSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
764e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
7654551c0a9SBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
7664551c0a9SBram Moolenaar	call s:HandleBreakpointDelete(msg)
76745d5f26dSBram Moolenaar      elseif msg =~ '^=thread-group-started'
76845d5f26dSBram Moolenaar	call s:HandleProgramRun(msg)
76945d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
77045d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
77182be4849SBram Moolenaar      elseif msg =~ '^\^error,msg='
77282be4849SBram Moolenaar	call s:HandleError(msg)
77382be4849SBram Moolenaar      elseif msg =~ '^disassemble'
774e09ba7baSBram Moolenaar        let s:parsing_disasm_msg = 1
775e09ba7baSBram Moolenaar        let s:asm_lines = []
776e09ba7baSBram Moolenaar      endif
777e09ba7baSBram Moolenaar    endif
778e09ba7baSBram Moolenaar  endfor
779589edb34SBram Moolenaarendfunc
780589edb34SBram Moolenaar
781589edb34SBram Moolenaarfunc s:GotoProgram()
782589edb34SBram Moolenaar  if has('win32')
783589edb34SBram Moolenaar    if executable('powershell')
784589edb34SBram Moolenaar      call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid))
785469bdbdeSBram Moolenaar    endif
786589edb34SBram Moolenaar  else
787589edb34SBram Moolenaar    call win_gotoid(s:ptywin)
788589edb34SBram Moolenaar  endif
789e09ba7baSBram Moolenaarendfunc
790e09ba7baSBram Moolenaar
791963c1ad5SBram Moolenaar" Install commands in the current window to control the debugger.
792963c1ad5SBram Moolenaarfunc s:InstallCommands()
793963c1ad5SBram Moolenaar  let save_cpo = &cpo
794589edb34SBram Moolenaar  set cpo&vim
79571137fedSBram Moolenaar
796e09ba7baSBram Moolenaar  command -nargs=? Break call s:SetBreakpoint(<q-args>)
79745d5f26dSBram Moolenaar  command Clear call s:ClearBreakpoint()
798e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
79960e73f2aSBram Moolenaar  command Over call s:SendCommand('-exec-next')
80060e73f2aSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
80160e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
802b3307b5eSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
803b3307b5eSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
804b3307b5eSBram Moolenaar
805b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
806b3307b5eSBram Moolenaar  if s:way == 'prompt'
807b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
808b3307b5eSBram Moolenaar  else
809b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
81045d5f26dSBram Moolenaar  endif
81145d5f26dSBram Moolenaar
812589edb34SBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
813b3307b5eSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
81482be4849SBram Moolenaar  command Program call s:GotoProgram()
81571137fedSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
81645d5f26dSBram Moolenaar  command Asm call s:GotoAsmwinOrCreateIt()
817388a5d4fSBram Moolenaar  command Winbar call s:InstallWinbar()
818388a5d4fSBram Moolenaar
81945d5f26dSBram Moolenaar  if !exists('g:termdebug_map_K') || g:termdebug_map_K
820388a5d4fSBram Moolenaar    let s:k_map_saved = maparg('K', 'n', 0, 1)
8211b9645deSBram Moolenaar    nnoremap K :Evaluate<CR>
822f0b03c4eSBram Moolenaar  endif
82371137fedSBram Moolenaar
82471137fedSBram Moolenaar  if has('menu') && &mouse != ''
82571137fedSBram Moolenaar    call s:InstallWinbar()
82671137fedSBram Moolenaar
82771137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
82871137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
82971137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
83071137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
83171137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
83271137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
83371137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
834963c1ad5SBram Moolenaar    endif
835963c1ad5SBram Moolenaar  endif
83671137fedSBram Moolenaar
83771137fedSBram Moolenaar  let &cpo = save_cpo
83871137fedSBram Moolenaarendfunc
83971137fedSBram Moolenaar
84071137fedSBram Moolenaarlet s:winbar_winids = []
84171137fedSBram Moolenaar
842c4b533e1SBram Moolenaar" Install the window toolbar in the current window.
84324a98a0eSBram Moolenaarfunc s:InstallWinbar()
84424a98a0eSBram Moolenaar  if has('menu') && &mouse != ''
84524a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
84624a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
84760e73f2aSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
84824a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
84971137fedSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
850c4b533e1SBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
851e09ba7baSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
852e09ba7baSBram Moolenaar  endif
853e09ba7baSBram Moolenaarendfunc
854e09ba7baSBram Moolenaar
855e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
85671137fedSBram Moolenaarfunc s:DeleteCommands()
857e09ba7baSBram Moolenaar  delcommand Break
85845d5f26dSBram Moolenaar  delcommand Clear
859e09ba7baSBram Moolenaar  delcommand Step
86060e73f2aSBram Moolenaar  delcommand Over
86160e73f2aSBram Moolenaar  delcommand Finish
86260e73f2aSBram Moolenaar  delcommand Run
863e09ba7baSBram Moolenaar  delcommand Arguments
86445d5f26dSBram Moolenaar  delcommand Stop
86545d5f26dSBram Moolenaar  delcommand Continue
86645d5f26dSBram Moolenaar  delcommand Evaluate
867b3623a38SBram Moolenaar  delcommand Gdb
86882be4849SBram Moolenaar  delcommand Program
86971137fedSBram Moolenaar  delcommand Source
87045d5f26dSBram Moolenaar  delcommand Asm
8711b884a00SBram Moolenaar  delcommand Winbar
8721b884a00SBram Moolenaar
8731b884a00SBram Moolenaar  if exists('s:k_map_saved')
8741b884a00SBram Moolenaar    if empty(s:k_map_saved)
875388a5d4fSBram Moolenaar      nunmap K
8761b884a00SBram Moolenaar    else
877388a5d4fSBram Moolenaar      call mapset('n', 0, s:k_map_saved)
878388a5d4fSBram Moolenaar    endif
8791b9645deSBram Moolenaar    unlet s:k_map_saved
8801b9645deSBram Moolenaar  endif
88171137fedSBram Moolenaar
88271137fedSBram Moolenaar  if has('menu')
88371137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
88471137fedSBram Moolenaar    let curwinid = win_getid(winnr())
8851b9645deSBram Moolenaar    for winid in s:winbar_winids
8861b9645deSBram Moolenaar      if win_gotoid(winid)
8871b9645deSBram Moolenaar	aunmenu WinBar.Step
8881b9645deSBram Moolenaar	aunmenu WinBar.Next
88960e73f2aSBram Moolenaar	aunmenu WinBar.Finish
8901b9645deSBram Moolenaar	aunmenu WinBar.Cont
8911b9645deSBram Moolenaar	aunmenu WinBar.Stop
89271137fedSBram Moolenaar	aunmenu WinBar.Eval
89371137fedSBram Moolenaar      endif
89471137fedSBram Moolenaar    endfor
89571137fedSBram Moolenaar    call win_gotoid(curwinid)
89671137fedSBram Moolenaar    let s:winbar_winids = []
89771137fedSBram Moolenaar
89871137fedSBram Moolenaar    if exists('s:saved_mousemodel')
89971137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
90071137fedSBram Moolenaar      unlet s:saved_mousemodel
90171137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
90271137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
90371137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
90471137fedSBram Moolenaar      aunmenu PopUp.Evaluate
9051b9645deSBram Moolenaar    endif
90645d5f26dSBram Moolenaar  endif
90737402ed5SBram Moolenaar
90837402ed5SBram Moolenaar  exe 'sign unplace ' . s:pc_id
90937402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
91037402ed5SBram Moolenaar    for subid in keys(entries)
91145d5f26dSBram Moolenaar      exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
91245d5f26dSBram Moolenaar    endfor
91337402ed5SBram Moolenaar  endfor
914a15b0a93SBram Moolenaar  unlet s:breakpoints
915a15b0a93SBram Moolenaar  unlet s:breakpoint_locations
916a15b0a93SBram Moolenaar
917a15b0a93SBram Moolenaar  sign undefine debugPC
918a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
9194551c0a9SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
920e09ba7baSBram Moolenaar  endfor
921e09ba7baSBram Moolenaar  let s:BreakpointSigns = []
922e09ba7baSBram Moolenaarendfunc
923589edb34SBram Moolenaar
92460e73f2aSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
92560e73f2aSBram Moolenaarfunc s:SetBreakpoint(at)
92660e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
92760e73f2aSBram Moolenaar  " Interrupt to make it work.
92860e73f2aSBram Moolenaar  let do_continue = 0
929b3307b5eSBram Moolenaar  if !s:stopped
9304551c0a9SBram Moolenaar    let do_continue = 1
931b3307b5eSBram Moolenaar    if s:way == 'prompt'
93260e73f2aSBram Moolenaar      call s:PromptInterrupt()
933b3307b5eSBram Moolenaar    else
93460e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
93560e73f2aSBram Moolenaar    endif
936589edb34SBram Moolenaar    sleep 10m
937a15b0a93SBram Moolenaar  endif
938589edb34SBram Moolenaar
939589edb34SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
940589edb34SBram Moolenaar  let at = empty(a:at) ?
94160e73f2aSBram Moolenaar        \ fnameescape(expand('%:p')) . ':' . line('.') : a:at
94260e73f2aSBram Moolenaar  call s:SendCommand('-break-insert ' . at)
94360e73f2aSBram Moolenaar  if do_continue
944e09ba7baSBram Moolenaar    call s:SendCommand('-exec-continue')
945e09ba7baSBram Moolenaar  endif
94671137fedSBram Moolenaarendfunc
94771137fedSBram Moolenaar
948e09ba7baSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
949e09ba7baSBram Moolenaarfunc s:ClearBreakpoint()
95037402ed5SBram Moolenaar  let fname = fnameescape(expand('%:p'))
95137402ed5SBram Moolenaar  let lnum = line('.')
95237402ed5SBram Moolenaar  let bploc = printf('%s:%d', fname, lnum)
95337402ed5SBram Moolenaar  if has_key(s:breakpoint_locations, bploc)
95437402ed5SBram Moolenaar    let idx = 0
95537402ed5SBram Moolenaar    for id in s:breakpoint_locations[bploc]
95637402ed5SBram Moolenaar      if has_key(s:breakpoints, id)
95737402ed5SBram Moolenaar	" Assume this always works, the reply is simply "^done".
95837402ed5SBram Moolenaar	call s:SendCommand('-break-delete ' . id)
95937402ed5SBram Moolenaar	for subid in keys(s:breakpoints[id])
96037402ed5SBram Moolenaar	  exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
96137402ed5SBram Moolenaar	endfor
962e09ba7baSBram Moolenaar	unlet s:breakpoints[id]
96337402ed5SBram Moolenaar	unlet s:breakpoint_locations[bploc][idx]
96437402ed5SBram Moolenaar	break
965e09ba7baSBram Moolenaar      else
966e09ba7baSBram Moolenaar	let idx += 1
96737402ed5SBram Moolenaar      endif
96837402ed5SBram Moolenaar    endfor
96937402ed5SBram Moolenaar    if empty(s:breakpoint_locations[bploc])
97037402ed5SBram Moolenaar      unlet s:breakpoint_locations[bploc]
971e09ba7baSBram Moolenaar    endif
972e09ba7baSBram Moolenaar  endif
97360e73f2aSBram Moolenaarendfunc
97460e73f2aSBram Moolenaar
97560e73f2aSBram Moolenaarfunc s:Run(args)
97660e73f2aSBram Moolenaar  if a:args != ''
97760e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
97860e73f2aSBram Moolenaar  endif
97960e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
98051b0f370SBram Moolenaarendfunc
98151b0f370SBram Moolenaar
98251b0f370SBram Moolenaarfunc s:SendEval(expr)
98351b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
98451b0f370SBram Moolenaar  let s:evalexpr = a:expr
98545d5f26dSBram Moolenaarendfunc
98645d5f26dSBram Moolenaar
98745d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
98845d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
98945d5f26dSBram Moolenaar  if a:arg != ''
99045d5f26dSBram Moolenaar    let expr = a:arg
99145d5f26dSBram Moolenaar  elseif a:range == 2
99245d5f26dSBram Moolenaar    let pos = getcurpos()
99345d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
99445d5f26dSBram Moolenaar    let regt = getregtype('v')
99545d5f26dSBram Moolenaar    normal! gv"vy
99645d5f26dSBram Moolenaar    let expr = @v
99745d5f26dSBram Moolenaar    call setpos('.', pos)
99845d5f26dSBram Moolenaar    call setreg('v', reg, regt)
99945d5f26dSBram Moolenaar  else
100022f1d0e3SBram Moolenaar    let expr = expand('<cexpr>')
100151b0f370SBram Moolenaar  endif
100245d5f26dSBram Moolenaar  let s:ignoreEvalError = 0
100345d5f26dSBram Moolenaar  call s:SendEval(expr)
100422f1d0e3SBram Moolenaarendfunc
100551b0f370SBram Moolenaar
100651b0f370SBram Moolenaarlet s:ignoreEvalError = 0
100745d5f26dSBram Moolenaarlet s:evalFromBalloonExpr = 0
100845d5f26dSBram Moolenaar
10091b9645deSBram Moolenaar" Handle the result of data-evaluate-expression
10101b9645deSBram Moolenaarfunc s:HandleEvaluate(msg)
101151b0f370SBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
101251b0f370SBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
101351b0f370SBram Moolenaar  if s:evalFromBalloonExpr
101451b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
101551b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
101651b0f370SBram Moolenaar    else
101751b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
101851b0f370SBram Moolenaar    endif
10191b9645deSBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
102051b0f370SBram Moolenaar  else
10211b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
10227f2e9d7cSBram Moolenaar  endif
10231b9645deSBram Moolenaar
102422f1d0e3SBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
102551b0f370SBram Moolenaar    " Looks like a pointer, also display what it points to.
102651b0f370SBram Moolenaar    let s:ignoreEvalError = 1
102751b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
10281b9645deSBram Moolenaar  else
102945d5f26dSBram Moolenaar    let s:evalFromBalloonExpr = 0
103045d5f26dSBram Moolenaar  endif
103151b0f370SBram Moolenaarendfunc
103251b0f370SBram Moolenaar
103351b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
1034b3307b5eSBram Moolenaar" if there is any.
1035396e829fSBram Moolenaarfunc TermDebugBalloonExpr()
1036b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
1037b3307b5eSBram Moolenaar    return ''
1038b3307b5eSBram Moolenaar  endif
1039b3307b5eSBram Moolenaar  if !s:stopped
1040396e829fSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
104151b0f370SBram Moolenaar    " mouse triggers a balloon.
104251b0f370SBram Moolenaar    return ''
104351b0f370SBram Moolenaar  endif
104422f1d0e3SBram Moolenaar  let s:evalFromBalloonExpr = 1
104522f1d0e3SBram Moolenaar  let s:evalFromBalloonExprResult = ''
104651b0f370SBram Moolenaar  let s:ignoreEvalError = 1
104751b0f370SBram Moolenaar  call s:SendEval(v:beval_text)
104851b0f370SBram Moolenaar  return ''
104945d5f26dSBram Moolenaarendfunc
105045d5f26dSBram Moolenaar
105122f1d0e3SBram Moolenaar" Handle an error.
105251b0f370SBram Moolenaarfunc s:HandleError(msg)
105322f1d0e3SBram Moolenaar  if s:ignoreEvalError
105422f1d0e3SBram Moolenaar    " Result of s:SendEval() failed, ignore.
105551b0f370SBram Moolenaar    let s:ignoreEvalError = 0
105651b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
105745d5f26dSBram Moolenaar    return
105845d5f26dSBram Moolenaar  endif
105945d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
1060b3307b5eSBram Moolenaarendfunc
1061b3307b5eSBram Moolenaar
1062c4b533e1SBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
1063b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
1064c4b533e1SBram Moolenaar    new
1065c4b533e1SBram Moolenaar    let s:sourcewin = win_getid(winnr())
1066c4b533e1SBram Moolenaar    call s:InstallWinbar()
1067c4b533e1SBram Moolenaar  endif
106882be4849SBram Moolenaarendfunc
106982be4849SBram Moolenaar
107082be4849SBram Moolenaarfunc s:GotoAsmwinOrCreateIt()
107182be4849SBram Moolenaar  if !win_gotoid(s:asmwin)
107282be4849SBram Moolenaar    if win_gotoid(s:sourcewin)
107382be4849SBram Moolenaar      exe 'rightbelow new'
107482be4849SBram Moolenaar    else
107582be4849SBram Moolenaar      exe 'new'
107682be4849SBram Moolenaar    endif
107782be4849SBram Moolenaar
107882be4849SBram Moolenaar    let s:asmwin = win_getid(winnr())
107982be4849SBram Moolenaar
108082be4849SBram Moolenaar    setlocal nowrap
108182be4849SBram Moolenaar    setlocal number
108282be4849SBram Moolenaar    setlocal noswapfile
108382be4849SBram Moolenaar    setlocal buftype=nofile
108482be4849SBram Moolenaar
108582be4849SBram Moolenaar    let asmbuf = bufnr('Termdebug-asm-listing')
108682be4849SBram Moolenaar    if asmbuf > 0
108782be4849SBram Moolenaar      exe 'buffer' . asmbuf
108882be4849SBram Moolenaar    else
108982be4849SBram Moolenaar      exe 'file Termdebug-asm-listing'
109082be4849SBram Moolenaar    endif
109182be4849SBram Moolenaar
109282be4849SBram Moolenaar    if exists('g:termdebug_disasm_window')
109382be4849SBram Moolenaar      if g:termdebug_disasm_window > 1
109482be4849SBram Moolenaar        exe 'resize ' . g:termdebug_disasm_window
109582be4849SBram Moolenaar      endif
109682be4849SBram Moolenaar    endif
109782be4849SBram Moolenaar  endif
109882be4849SBram Moolenaar
109982be4849SBram Moolenaar  if s:asm_addr != ''
110082be4849SBram Moolenaar    let lnum = search('^' . s:asm_addr)
110182be4849SBram Moolenaar    if lnum == 0
110282be4849SBram Moolenaar      if s:stopped
110382be4849SBram Moolenaar        call s:SendCommand('disassemble $pc')
110482be4849SBram Moolenaar      endif
110582be4849SBram Moolenaar    else
110682be4849SBram Moolenaar      exe 'sign unplace ' . s:asm_id
110782be4849SBram Moolenaar      exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
110882be4849SBram Moolenaar    endif
110982be4849SBram Moolenaar  endif
1110e09ba7baSBram Moolenaarendfunc
1111e09ba7baSBram Moolenaar
1112e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
1113fe386641SBram Moolenaar" Will update the sign that shows the current position.
1114fe386641SBram Moolenaarfunc s:HandleCursor(msg)
111560e73f2aSBram Moolenaar  let wid = win_getid(winnr())
11164551c0a9SBram Moolenaar
111760e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
111860e73f2aSBram Moolenaar    call ch_log('program stopped')
11194551c0a9SBram Moolenaar    let s:stopped = 1
112060e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
112160e73f2aSBram Moolenaar    call ch_log('program running')
112260e73f2aSBram Moolenaar    let s:stopped = 0
1123a15b0a93SBram Moolenaar  endif
1124a15b0a93SBram Moolenaar
1125a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
1126a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
1127a15b0a93SBram Moolenaar  else
112882be4849SBram Moolenaar    let fname = ''
112982be4849SBram Moolenaar  endif
113082be4849SBram Moolenaar
113182be4849SBram Moolenaar  if a:msg =~ 'addr='
113282be4849SBram Moolenaar    let asm_addr = s:GetAsmAddr(a:msg)
113382be4849SBram Moolenaar    if asm_addr != ''
113482be4849SBram Moolenaar      let s:asm_addr = asm_addr
113582be4849SBram Moolenaar
113682be4849SBram Moolenaar      let curwinid = win_getid(winnr())
113782be4849SBram Moolenaar      if win_gotoid(s:asmwin)
113882be4849SBram Moolenaar        let lnum = search('^' . s:asm_addr)
113982be4849SBram Moolenaar        if lnum == 0
114082be4849SBram Moolenaar          call s:SendCommand('disassemble $pc')
114182be4849SBram Moolenaar        else
114282be4849SBram Moolenaar          exe 'sign unplace ' . s:asm_id
114382be4849SBram Moolenaar          exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
114482be4849SBram Moolenaar        endif
114582be4849SBram Moolenaar
114682be4849SBram Moolenaar        call win_gotoid(curwinid)
114782be4849SBram Moolenaar      endif
114882be4849SBram Moolenaar    endif
11491b9645deSBram Moolenaar  endif
1150e09ba7baSBram Moolenaar
1151fe386641SBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
11524551c0a9SBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
11531b9645deSBram Moolenaar    if lnum =~ '^[0-9]*$'
1154fe386641SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
1155fe386641SBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
1156fe386641SBram Moolenaar	if &modified
1157b3307b5eSBram Moolenaar	  " TODO: find existing window
1158c4b533e1SBram Moolenaar	  exe 'split ' . fnameescape(fname)
1159fe386641SBram Moolenaar	  let s:sourcewin = win_getid(winnr())
1160fe386641SBram Moolenaar	  call s:InstallWinbar()
1161fe386641SBram Moolenaar	else
1162fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
1163fe386641SBram Moolenaar	endif
116401164a65SBram Moolenaar      endif
116539f7aa3cSBram Moolenaar      exe lnum
1166cb80aa2dSBram Moolenaar      exe 'sign unplace ' . s:pc_id
1167cb80aa2dSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname
1168cb80aa2dSBram Moolenaar      if !exists('b:save_signcolumn')
1169cb80aa2dSBram Moolenaar	let b:save_signcolumn = &signcolumn
1170fe386641SBram Moolenaar	call add(s:signcolumn_buflist, bufnr())
1171fe386641SBram Moolenaar      endif
11724551c0a9SBram Moolenaar      setlocal signcolumn=yes
1173fe386641SBram Moolenaar    endif
1174fe386641SBram Moolenaar  elseif !s:stopped || fname != ''
1175fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
1176fe386641SBram Moolenaar  endif
1177e09ba7baSBram Moolenaar
1178e09ba7baSBram Moolenaar  call win_gotoid(wid)
1179de1a8314SBram Moolenaarendfunc
1180a15b0a93SBram Moolenaar
118137402ed5SBram Moolenaarlet s:BreakpointSigns = []
118237402ed5SBram Moolenaar
118337402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid)
118437402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
118537402ed5SBram Moolenaar  if index(s:BreakpointSigns, nr) == -1
1186de1a8314SBram Moolenaar    call add(s:BreakpointSigns, nr)
1187de1a8314SBram Moolenaar    exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint"
1188de1a8314SBram Moolenaar  endif
118937402ed5SBram Moolenaarendfunc
119037402ed5SBram Moolenaar
11915378e1cfSBram Moolenaarfunc! s:SplitMsg(s)
11925378e1cfSBram Moolenaar  return split(a:s, '{.\{-}}\zs')
1193e09ba7baSBram Moolenaarendfunction
1194e09ba7baSBram Moolenaar
1195e09ba7baSBram Moolenaar" Handle setting a breakpoint
11966dccc962SBram Moolenaar" Will update the sign that shows the breakpoint
11976dccc962SBram Moolenaarfunc s:HandleNewBreakpoint(msg)
11986dccc962SBram Moolenaar  if a:msg !~ 'fullname='
11996dccc962SBram Moolenaar    " a watch does not have a file name
12005378e1cfSBram Moolenaar    return
12015378e1cfSBram Moolenaar  endif
12025378e1cfSBram Moolenaar  for msg in s:SplitMsg(a:msg)
12035378e1cfSBram Moolenaar    let fname = s:GetFullname(msg)
12045378e1cfSBram Moolenaar    if empty(fname)
12055378e1cfSBram Moolenaar      continue
12065378e1cfSBram Moolenaar    endif
1207e09ba7baSBram Moolenaar    let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
1208fe386641SBram Moolenaar    if empty(nr)
1209e09ba7baSBram Moolenaar      return
121037402ed5SBram Moolenaar    endif
121137402ed5SBram Moolenaar
121237402ed5SBram Moolenaar    " If "nr" is 123 it becomes "123.0" and subid is "0".
121337402ed5SBram Moolenaar    " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded.
121437402ed5SBram Moolenaar    let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0')
121537402ed5SBram Moolenaar    call s:CreateBreakpoint(id, subid)
121637402ed5SBram Moolenaar
121737402ed5SBram Moolenaar    if has_key(s:breakpoints, id)
121837402ed5SBram Moolenaar      let entries = s:breakpoints[id]
121937402ed5SBram Moolenaar    else
122037402ed5SBram Moolenaar      let entries = {}
122137402ed5SBram Moolenaar      let s:breakpoints[id] = entries
122237402ed5SBram Moolenaar    endif
1223e09ba7baSBram Moolenaar    if has_key(entries, subid)
1224e09ba7baSBram Moolenaar      let entry = entries[subid]
122537402ed5SBram Moolenaar    else
1226fe386641SBram Moolenaar      let entry = {}
1227e09ba7baSBram Moolenaar      let entries[subid] = entry
12285378e1cfSBram Moolenaar    endif
1229e09ba7baSBram Moolenaar
1230e09ba7baSBram Moolenaar    let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
12311b9645deSBram Moolenaar    let entry['fname'] = fname
123237402ed5SBram Moolenaar    let entry['lnum'] = lnum
123337402ed5SBram Moolenaar
123437402ed5SBram Moolenaar    let bploc = printf('%s:%d', fname, lnum)
123537402ed5SBram Moolenaar    if !has_key(s:breakpoint_locations, bploc)
123637402ed5SBram Moolenaar      let s:breakpoint_locations[bploc] = []
123737402ed5SBram Moolenaar    endif
12381b9645deSBram Moolenaar    let s:breakpoint_locations[bploc] += [id]
123937402ed5SBram Moolenaar
12401b9645deSBram Moolenaar    if bufloaded(fname)
12415378e1cfSBram Moolenaar      call s:PlaceSign(id, subid, entry)
12421b9645deSBram Moolenaar    endif
12431b9645deSBram Moolenaar  endfor
124437402ed5SBram Moolenaarendfunc
124537402ed5SBram Moolenaar
12463132cdddSBram Moolenaarfunc s:PlaceSign(id, subid, entry)
12471b9645deSBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
1248e09ba7baSBram Moolenaar  exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' priority=110 file=' . a:entry['fname']
1249e09ba7baSBram Moolenaar  let a:entry['placed'] = 1
1250e09ba7baSBram Moolenaarendfunc
1251e09ba7baSBram Moolenaar
1252e09ba7baSBram Moolenaar" Handle deleting a breakpoint
125337402ed5SBram Moolenaar" Will remove the sign that shows the breakpoint
125437402ed5SBram Moolenaarfunc s:HandleBreakpointDelete(msg)
1255e09ba7baSBram Moolenaar  let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
1256e09ba7baSBram Moolenaar  if empty(id)
125737402ed5SBram Moolenaar    return
125837402ed5SBram Moolenaar  endif
12591b9645deSBram Moolenaar  if has_key(s:breakpoints, id)
126037402ed5SBram Moolenaar    for [subid, entry] in items(s:breakpoints[id])
12611b9645deSBram Moolenaar      if has_key(entry, 'placed')
12621b9645deSBram Moolenaar	exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
12635378e1cfSBram Moolenaar	unlet entry['placed']
126437402ed5SBram Moolenaar      endif
126537402ed5SBram Moolenaar    endfor
1266c572da5fSBram Moolenaar    unlet s:breakpoints[id]
12671b9645deSBram Moolenaar  endif
12684551c0a9SBram Moolenaarendfunc
12694551c0a9SBram Moolenaar
12704551c0a9SBram Moolenaar" Handle the debugged program starting to run.
12714551c0a9SBram Moolenaar" Will store the process ID in s:pid
12724551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
12734551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
12744551c0a9SBram Moolenaar  if nr == 0
12754551c0a9SBram Moolenaar    return
12764551c0a9SBram Moolenaar  endif
12774551c0a9SBram Moolenaar  let s:pid = nr
12784551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
12791b9645deSBram Moolenaarendfunc
12801b9645deSBram Moolenaar
12811b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
128237402ed5SBram Moolenaarfunc s:BufRead()
128337402ed5SBram Moolenaar  let fname = expand('<afile>:p')
12841b9645deSBram Moolenaar  for [id, entries] in items(s:breakpoints)
128537402ed5SBram Moolenaar    for [subid, entry] in items(entries)
12861b9645deSBram Moolenaar      if entry['fname'] == fname
12871b9645deSBram Moolenaar	call s:PlaceSign(id, subid, entry)
128837402ed5SBram Moolenaar      endif
12891b9645deSBram Moolenaar    endfor
12901b9645deSBram Moolenaar  endfor
12911b9645deSBram Moolenaarendfunc
12921b9645deSBram Moolenaar
12931b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
129437402ed5SBram Moolenaarfunc s:BufUnloaded()
129537402ed5SBram Moolenaar  let fname = expand('<afile>:p')
12961b9645deSBram Moolenaar  for [id, entries] in items(s:breakpoints)
12971b9645deSBram Moolenaar    for [subid, entry] in items(entries)
12981b9645deSBram Moolenaar      if entry['fname'] == fname
12991b9645deSBram Moolenaar	let entry['placed'] = 0
130037402ed5SBram Moolenaar      endif
13011b9645deSBram Moolenaar    endfor
1302ca4cc018SBram Moolenaar  endfor
1303ca4cc018SBram Moolenaarendfunc
1304ca4cc018SBram Moolenaar
1305let &cpo = s:keepcpo
1306unlet s:keepcpo
1307