1fe386641SBram Moolenaar" Debugger plugin using gdb.
2c572da5fSBram Moolenaar"
3b3307b5eSBram Moolenaar" Author: Bram Moolenaar
4b3307b5eSBram Moolenaar" Copyright: Vim license applies, see ":help license"
5*82be4849SBram Moolenaar" Last Change: 2021 Jan 03
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
73*82be4849SBram Moolenaarlet s:asm_id = 13
74*82be4849SBram Moolenaarlet s:break_id = 14  " breakpoint number is added to this
7560e73f2aSBram Moolenaarlet s:stopped = 1
76e09ba7baSBram Moolenaar
77*82be4849SBram Moolenaarlet s:parsing_disasm_msg = 0
78*82be4849SBram Moolenaarlet s:asm_lines = []
79*82be4849SBram Moolenaarlet s:asm_addr = ''
80*82be4849SBram 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
122*82be4849SBram Moolenaar  let s:asmwin = 0
123b3623a38SBram Moolenaar
124b3307b5eSBram Moolenaar  " Uncomment this line to write logging in "debuglog".
125b3307b5eSBram Moolenaar  " call ch_logfile('debuglog', 'w')
126b3307b5eSBram Moolenaar
127b3307b5eSBram Moolenaar  let s:sourcewin = win_getid(winnr())
128cb80aa2dSBram Moolenaar
129cb80aa2dSBram Moolenaar  " Remember the old value of 'signcolumn' for each buffer that it's set in, so
130cb80aa2dSBram Moolenaar  " that we can restore the value for all buffers.
131cb80aa2dSBram Moolenaar  let b:save_signcolumn = &signcolumn
132cb80aa2dSBram Moolenaar  let s:signcolumn_buflist = [bufnr()]
133fe386641SBram Moolenaar
13424a98a0eSBram Moolenaar  let s:save_columns = 0
13568e6560bSBram Moolenaar  let s:allleft = 0
13624a98a0eSBram Moolenaar  if exists('g:termdebug_wide')
13724a98a0eSBram Moolenaar    if &columns < g:termdebug_wide
13838baa3e6SBram Moolenaar      let s:save_columns = &columns
13938baa3e6SBram Moolenaar      let &columns = g:termdebug_wide
14068e6560bSBram Moolenaar      " If we make the Vim window wider, use the whole left halve for the debug
14168e6560bSBram Moolenaar      " windows.
14268e6560bSBram Moolenaar      let s:allleft = 1
14324a98a0eSBram Moolenaar    endif
144b3307b5eSBram Moolenaar    let s:vertical = 1
14538baa3e6SBram Moolenaar  else
146b3307b5eSBram Moolenaar    let s:vertical = 0
14738baa3e6SBram Moolenaar  endif
14838baa3e6SBram Moolenaar
149b3307b5eSBram Moolenaar  " Override using a terminal window by setting g:termdebug_use_prompt to 1.
150b3307b5eSBram Moolenaar  let use_prompt = exists('g:termdebug_use_prompt') && g:termdebug_use_prompt
151b3307b5eSBram Moolenaar  if has('terminal') && !has('win32') && !use_prompt
152b3307b5eSBram Moolenaar    let s:way = 'terminal'
153b3307b5eSBram Moolenaar  else
154b3307b5eSBram Moolenaar    let s:way = 'prompt'
155b3307b5eSBram Moolenaar  endif
156b3307b5eSBram Moolenaar
157b3307b5eSBram Moolenaar  if s:way == 'prompt'
158b3307b5eSBram Moolenaar    call s:StartDebug_prompt(a:dict)
159b3307b5eSBram Moolenaar  else
160b3307b5eSBram Moolenaar    call s:StartDebug_term(a:dict)
161b3307b5eSBram Moolenaar  endif
162*82be4849SBram Moolenaar
163*82be4849SBram Moolenaar  if exists('g:termdebug_disasm_window')
164*82be4849SBram Moolenaar    if g:termdebug_disasm_window
165*82be4849SBram Moolenaar      let curwinid = win_getid(winnr())
166*82be4849SBram Moolenaar      call s:GotoAsmwinOrCreateIt()
167*82be4849SBram Moolenaar      call win_gotoid(curwinid)
168*82be4849SBram Moolenaar    endif
169*82be4849SBram Moolenaar  endif
170b3307b5eSBram Moolenaarendfunc
171b3307b5eSBram Moolenaar
172ef3c6a5bSBram Moolenaar" Use when debugger didn't start or ended.
173ef3c6a5bSBram Moolenaarfunc s:CloseBuffers()
174ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:ptybuf
175ef3c6a5bSBram Moolenaar  exe 'bwipe! ' . s:commbuf
176ef3c6a5bSBram Moolenaar  unlet! s:gdbwin
177ef3c6a5bSBram Moolenaarendfunc
178ef3c6a5bSBram Moolenaar
179b3307b5eSBram Moolenaarfunc s:StartDebug_term(dict)
180b3307b5eSBram Moolenaar  " Open a terminal window without a job, to run the debugged program in.
181fe386641SBram Moolenaar  let s:ptybuf = term_start('NONE', {
182b3307b5eSBram Moolenaar	\ 'term_name': 'debugged program',
183b3307b5eSBram Moolenaar	\ 'vertical': s:vertical,
184fe386641SBram Moolenaar	\ })
185fe386641SBram Moolenaar  if s:ptybuf == 0
186fe386641SBram Moolenaar    echoerr 'Failed to open the program terminal window'
187fe386641SBram Moolenaar    return
188fe386641SBram Moolenaar  endif
189fe386641SBram Moolenaar  let pty = job_info(term_getjob(s:ptybuf))['tty_out']
19045d5f26dSBram Moolenaar  let s:ptywin = win_getid(winnr())
191b3307b5eSBram Moolenaar  if s:vertical
19251b0f370SBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
19351b0f370SBram Moolenaar    " columns for that, thus one less for the terminal window.
19451b0f370SBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
19568e6560bSBram Moolenaar    if s:allleft
19668e6560bSBram Moolenaar      " use the whole left column
19768e6560bSBram Moolenaar      wincmd H
19868e6560bSBram Moolenaar    endif
19951b0f370SBram Moolenaar  endif
200fe386641SBram Moolenaar
201fe386641SBram Moolenaar  " Create a hidden terminal window to communicate with gdb
202fe386641SBram Moolenaar  let s:commbuf = term_start('NONE', {
203fe386641SBram Moolenaar	\ 'term_name': 'gdb communication',
204fe386641SBram Moolenaar	\ 'out_cb': function('s:CommOutput'),
205fe386641SBram Moolenaar	\ 'hidden': 1,
206fe386641SBram Moolenaar	\ })
207fe386641SBram Moolenaar  if s:commbuf == 0
208fe386641SBram Moolenaar    echoerr 'Failed to open the communication terminal window'
209fe386641SBram Moolenaar    exe 'bwipe! ' . s:ptybuf
210fe386641SBram Moolenaar    return
211fe386641SBram Moolenaar  endif
212fe386641SBram Moolenaar  let commpty = job_info(term_getjob(s:commbuf))['tty_out']
213c572da5fSBram Moolenaar
214c572da5fSBram Moolenaar  " Open a terminal window to run the debugger.
215c3632516SBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
21632c67ba7SBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
21732c67ba7SBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
21832c67ba7SBram Moolenaar
21932c67ba7SBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
220b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
22160e73f2aSBram Moolenaar  let s:gdbbuf = term_start(cmd, {
222fe386641SBram Moolenaar	\ 'term_finish': 'close',
223c572da5fSBram Moolenaar	\ })
22460e73f2aSBram Moolenaar  if s:gdbbuf == 0
225fe386641SBram Moolenaar    echoerr 'Failed to open the gdb terminal window'
226ef3c6a5bSBram Moolenaar    call s:CloseBuffers()
227fe386641SBram Moolenaar    return
228fe386641SBram Moolenaar  endif
22945d5f26dSBram Moolenaar  let s:gdbwin = win_getid(winnr())
230fe386641SBram Moolenaar
23132c67ba7SBram Moolenaar  " Set arguments to be run
23232c67ba7SBram Moolenaar  if len(proc_args)
23332c67ba7SBram Moolenaar    call term_sendkeys(s:gdbbuf, 'set args ' . join(proc_args) . "\r")
23432c67ba7SBram Moolenaar  endif
23532c67ba7SBram Moolenaar
236fe386641SBram Moolenaar  " Connect gdb to the communication pty, using the GDB/MI interface
23760e73f2aSBram Moolenaar  call term_sendkeys(s:gdbbuf, 'new-ui mi ' . commpty . "\r")
23860e73f2aSBram Moolenaar
2393e4b84d0SBram Moolenaar  " Wait for the response to show up, users may not notice the error and wonder
2403e4b84d0SBram Moolenaar  " why the debugger doesn't work.
2413e4b84d0SBram Moolenaar  let try_count = 0
2423e4b84d0SBram Moolenaar  while 1
243ef3c6a5bSBram Moolenaar    let gdbproc = term_getjob(s:gdbbuf)
244ef3c6a5bSBram Moolenaar    if gdbproc == v:null || job_status(gdbproc) !=# 'run'
245ef3c6a5bSBram Moolenaar      echoerr string(g:termdebugger) . ' exited unexpectedly'
246ef3c6a5bSBram Moolenaar      call s:CloseBuffers()
247ef3c6a5bSBram Moolenaar      return
248ef3c6a5bSBram Moolenaar    endif
249ef3c6a5bSBram Moolenaar
2503e4b84d0SBram Moolenaar    let response = ''
251b3623a38SBram Moolenaar    for lnum in range(1, 200)
25219c8fe19SBram Moolenaar      let line1 = term_getline(s:gdbbuf, lnum)
25319c8fe19SBram Moolenaar      let line2 = term_getline(s:gdbbuf, lnum + 1)
25419c8fe19SBram Moolenaar      if line1 =~ 'new-ui mi '
255f63db65bSBram Moolenaar	" response can be in the same line or the next line
25619c8fe19SBram Moolenaar	let response = line1 . line2
2573e4b84d0SBram Moolenaar	if response =~ 'Undefined command'
258f3ba14ffSBram Moolenaar	  echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
259ef3c6a5bSBram Moolenaar	  call s:CloseBuffers()
2603e4b84d0SBram Moolenaar	  return
2613e4b84d0SBram Moolenaar	endif
2623e4b84d0SBram Moolenaar	if response =~ 'New UI allocated'
2633e4b84d0SBram Moolenaar	  " Success!
2643e4b84d0SBram Moolenaar	  break
2653e4b84d0SBram Moolenaar	endif
26619c8fe19SBram Moolenaar      elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi '
26719c8fe19SBram Moolenaar	" Reading symbols might take a while, try more times
26806fe74aeSBram Moolenaar	let try_count -= 1
26906fe74aeSBram Moolenaar      endif
2703e4b84d0SBram Moolenaar    endfor
2713e4b84d0SBram Moolenaar    if response =~ 'New UI allocated'
2723e4b84d0SBram Moolenaar      break
2733e4b84d0SBram Moolenaar    endif
2743e4b84d0SBram Moolenaar    let try_count += 1
2753e4b84d0SBram Moolenaar    if try_count > 100
2763e4b84d0SBram Moolenaar      echoerr 'Cannot check if your gdb works, continuing anyway'
2773e4b84d0SBram Moolenaar      break
2783e4b84d0SBram Moolenaar    endif
2793e4b84d0SBram Moolenaar    sleep 10m
2803e4b84d0SBram Moolenaar  endwhile
2813e4b84d0SBram Moolenaar
28291359014SBram Moolenaar  " Interpret commands while the target is running.  This should usually only be
28360e73f2aSBram Moolenaar  " exec-interrupt, since many commands don't work properly while the target is
28460e73f2aSBram Moolenaar  " running.
28560e73f2aSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
286b3307b5eSBram Moolenaar  " Older gdb uses a different command.
287b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
288e09ba7baSBram Moolenaar
289f3ba14ffSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
290f3ba14ffSBram Moolenaar  " "Type <return> to continue" prompt.
291b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
292f3ba14ffSBram Moolenaar
293ef3c6a5bSBram Moolenaar  call job_setoptions(gdbproc, {'exit_cb': function('s:EndTermDebug')})
294b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
295b3307b5eSBram Moolenaarendfunc
296b3307b5eSBram Moolenaar
297b3307b5eSBram Moolenaarfunc s:StartDebug_prompt(dict)
298b3307b5eSBram Moolenaar  " Open a window with a prompt buffer to run gdb in.
299b3307b5eSBram Moolenaar  if s:vertical
300b3307b5eSBram Moolenaar    vertical new
301b3307b5eSBram Moolenaar  else
302b3307b5eSBram Moolenaar    new
303b3307b5eSBram Moolenaar  endif
304b3307b5eSBram Moolenaar  let s:gdbwin = win_getid(winnr())
305b3307b5eSBram Moolenaar  let s:promptbuf = bufnr('')
306b3307b5eSBram Moolenaar  call prompt_setprompt(s:promptbuf, 'gdb> ')
307b3307b5eSBram Moolenaar  set buftype=prompt
308b3307b5eSBram Moolenaar  file gdb
309b3307b5eSBram Moolenaar  call prompt_setcallback(s:promptbuf, function('s:PromptCallback'))
310b3307b5eSBram Moolenaar  call prompt_setinterrupt(s:promptbuf, function('s:PromptInterrupt'))
311b3307b5eSBram Moolenaar
312b3307b5eSBram Moolenaar  if s:vertical
313b3307b5eSBram Moolenaar    " Assuming the source code window will get a signcolumn, use two more
314b3307b5eSBram Moolenaar    " columns for that, thus one less for the terminal window.
315b3307b5eSBram Moolenaar    exe (&columns / 2 - 1) . "wincmd |"
316b3307b5eSBram Moolenaar  endif
317b3307b5eSBram Moolenaar
318b3307b5eSBram Moolenaar  " Add -quiet to avoid the intro message causing a hit-enter prompt.
319b3307b5eSBram Moolenaar  let gdb_args = get(a:dict, 'gdb_args', [])
320b3307b5eSBram Moolenaar  let proc_args = get(a:dict, 'proc_args', [])
321b3307b5eSBram Moolenaar
322b3307b5eSBram Moolenaar  let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
323b3307b5eSBram Moolenaar  call ch_log('executing "' . join(cmd) . '"')
324b3307b5eSBram Moolenaar
325b3307b5eSBram Moolenaar  let s:gdbjob = job_start(cmd, {
326b3307b5eSBram Moolenaar	\ 'exit_cb': function('s:EndPromptDebug'),
327b3307b5eSBram Moolenaar	\ 'out_cb': function('s:GdbOutCallback'),
328b3307b5eSBram Moolenaar	\ })
329b3307b5eSBram Moolenaar  if job_status(s:gdbjob) != "run"
330b3307b5eSBram Moolenaar    echoerr 'Failed to start gdb'
331b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:promptbuf
332b3307b5eSBram Moolenaar    return
333b3307b5eSBram Moolenaar  endif
3344551c0a9SBram Moolenaar  " Mark the buffer modified so that it's not easy to close.
3354551c0a9SBram Moolenaar  set modified
336b3307b5eSBram Moolenaar  let s:gdb_channel = job_getchannel(s:gdbjob)
337b3307b5eSBram Moolenaar
33891359014SBram Moolenaar  " Interpret commands while the target is running.  This should usually only
339b3307b5eSBram Moolenaar  " be exec-interrupt, since many commands don't work properly while the
340b3307b5eSBram Moolenaar  " target is running.
341b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set mi-async on')
342b3307b5eSBram Moolenaar  " Older gdb uses a different command.
343b3307b5eSBram Moolenaar  call s:SendCommand('-gdb-set target-async on')
344b3307b5eSBram Moolenaar
345b3307b5eSBram Moolenaar  let s:ptybuf = 0
346b3307b5eSBram Moolenaar  if has('win32')
347b3307b5eSBram Moolenaar    " MS-Windows: run in a new console window for maximum compatibility
348b3307b5eSBram Moolenaar    call s:SendCommand('set new-console on')
349b3307b5eSBram Moolenaar  elseif has('terminal')
350b3307b5eSBram Moolenaar    " Unix: Run the debugged program in a terminal window.  Open it below the
351b3307b5eSBram Moolenaar    " gdb window.
352b3307b5eSBram Moolenaar    belowright let s:ptybuf = term_start('NONE', {
353b3307b5eSBram Moolenaar	  \ 'term_name': 'debugged program',
354b3307b5eSBram Moolenaar	  \ })
355b3307b5eSBram Moolenaar    if s:ptybuf == 0
356b3307b5eSBram Moolenaar      echoerr 'Failed to open the program terminal window'
357b3307b5eSBram Moolenaar      call job_stop(s:gdbjob)
358b3307b5eSBram Moolenaar      return
359b3307b5eSBram Moolenaar    endif
360b3307b5eSBram Moolenaar    let s:ptywin = win_getid(winnr())
361b3307b5eSBram Moolenaar    let pty = job_info(term_getjob(s:ptybuf))['tty_out']
362b3307b5eSBram Moolenaar    call s:SendCommand('tty ' . pty)
363b3307b5eSBram Moolenaar
364b3307b5eSBram Moolenaar    " Since GDB runs in a prompt window, the environment has not been set to
365b3307b5eSBram Moolenaar    " match a terminal window, need to do that now.
366b3307b5eSBram Moolenaar    call s:SendCommand('set env TERM = xterm-color')
367b3307b5eSBram Moolenaar    call s:SendCommand('set env ROWS = ' . winheight(s:ptywin))
368b3307b5eSBram Moolenaar    call s:SendCommand('set env LINES = ' . winheight(s:ptywin))
369b3307b5eSBram Moolenaar    call s:SendCommand('set env COLUMNS = ' . winwidth(s:ptywin))
370b3307b5eSBram Moolenaar    call s:SendCommand('set env COLORS = ' . &t_Co)
371b3307b5eSBram Moolenaar    call s:SendCommand('set env VIM_TERMINAL = ' . v:version)
372b3307b5eSBram Moolenaar  else
373b3307b5eSBram Moolenaar    " TODO: open a new terminal get get the tty name, pass on to gdb
374b3307b5eSBram Moolenaar    call s:SendCommand('show inferior-tty')
375b3307b5eSBram Moolenaar  endif
376b3307b5eSBram Moolenaar  call s:SendCommand('set print pretty on')
377b3307b5eSBram Moolenaar  call s:SendCommand('set breakpoint pending on')
378b3307b5eSBram Moolenaar  " Disable pagination, it causes everything to stop at the gdb
379b3307b5eSBram Moolenaar  call s:SendCommand('set pagination off')
380b3307b5eSBram Moolenaar
381b3307b5eSBram Moolenaar  " Set arguments to be run
382b3307b5eSBram Moolenaar  if len(proc_args)
383b3307b5eSBram Moolenaar    call s:SendCommand('set args ' . join(proc_args))
384b3307b5eSBram Moolenaar  endif
385b3307b5eSBram Moolenaar
386b3307b5eSBram Moolenaar  call s:StartDebugCommon(a:dict)
387b3307b5eSBram Moolenaar  startinsert
388b3307b5eSBram Moolenaarendfunc
389b3307b5eSBram Moolenaar
390b3307b5eSBram Moolenaarfunc s:StartDebugCommon(dict)
39138baa3e6SBram Moolenaar  " Sign used to highlight the line where the program has stopped.
39238baa3e6SBram Moolenaar  " There can be only one.
39338baa3e6SBram Moolenaar  sign define debugPC linehl=debugPC
39438baa3e6SBram Moolenaar
39545d5f26dSBram Moolenaar  " Install debugger commands in the text window.
396b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
397e09ba7baSBram Moolenaar  call s:InstallCommands()
39845d5f26dSBram Moolenaar  call win_gotoid(s:gdbwin)
399e09ba7baSBram Moolenaar
40051b0f370SBram Moolenaar  " Enable showing a balloon with eval info
401246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
402246fe03dSBram Moolenaar    set balloonexpr=TermDebugBalloonExpr()
40351b0f370SBram Moolenaar    if has("balloon_eval")
40451b0f370SBram Moolenaar      set ballooneval
405246fe03dSBram Moolenaar    endif
40651b0f370SBram Moolenaar    if has("balloon_eval_term")
40751b0f370SBram Moolenaar      set balloonevalterm
40851b0f370SBram Moolenaar    endif
40951b0f370SBram Moolenaar  endif
41051b0f370SBram Moolenaar
4115378e1cfSBram Moolenaar  " Contains breakpoints that have been placed, key is a string with the GDB
4125378e1cfSBram Moolenaar  " breakpoint number.
41337402ed5SBram Moolenaar  " Each entry is a dict, containing the sub-breakpoints.  Key is the subid.
41437402ed5SBram Moolenaar  " For a breakpoint that is just a number the subid is zero.
41537402ed5SBram Moolenaar  " For a breakpoint "123.4" the id is "123" and subid is "4".
41637402ed5SBram Moolenaar  " Example, when breakpoint "44", "123", "123.1" and "123.2" exist:
41737402ed5SBram Moolenaar  " {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}}
418e09ba7baSBram Moolenaar  let s:breakpoints = {}
4191b9645deSBram Moolenaar
42037402ed5SBram Moolenaar  " Contains breakpoints by file/lnum.  The key is "fname:lnum".
42137402ed5SBram Moolenaar  " Each entry is a list of breakpoint IDs at that position.
42237402ed5SBram Moolenaar  let s:breakpoint_locations = {}
42337402ed5SBram Moolenaar
4241b9645deSBram Moolenaar  augroup TermDebug
4251b9645deSBram Moolenaar    au BufRead * call s:BufRead()
4261b9645deSBram Moolenaar    au BufUnload * call s:BufUnloaded()
427f07f9e73SBram Moolenaar    au OptionSet background call s:Highlight(0, v:option_old, v:option_new)
4281b9645deSBram Moolenaar  augroup END
42932c67ba7SBram Moolenaar
430b3307b5eSBram Moolenaar  " Run the command if the bang attribute was given and got to the debug
431b3307b5eSBram Moolenaar  " window.
43232c67ba7SBram Moolenaar  if get(a:dict, 'bang', 0)
43332c67ba7SBram Moolenaar    call s:SendCommand('-exec-run')
43432c67ba7SBram Moolenaar    call win_gotoid(s:ptywin)
43532c67ba7SBram Moolenaar  endif
436c572da5fSBram Moolenaarendfunc
437c572da5fSBram Moolenaar
438b3307b5eSBram Moolenaar" Send a command to gdb.  "cmd" is the string without line terminator.
439b3307b5eSBram Moolenaarfunc s:SendCommand(cmd)
440b3307b5eSBram Moolenaar  call ch_log('sending to gdb: ' . a:cmd)
441b3307b5eSBram Moolenaar  if s:way == 'prompt'
442b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
443b3307b5eSBram Moolenaar  else
444b3307b5eSBram Moolenaar    call term_sendkeys(s:commbuf, a:cmd . "\r")
445b3307b5eSBram Moolenaar  endif
446b3307b5eSBram Moolenaarendfunc
447b3307b5eSBram Moolenaar
448b3307b5eSBram Moolenaar" This is global so that a user can create their mappings with this.
449b3307b5eSBram Moolenaarfunc TermDebugSendCommand(cmd)
450b3307b5eSBram Moolenaar  if s:way == 'prompt'
451b3307b5eSBram Moolenaar    call ch_sendraw(s:gdb_channel, a:cmd . "\n")
452b3307b5eSBram Moolenaar  else
453b3307b5eSBram Moolenaar    let do_continue = 0
454b3307b5eSBram Moolenaar    if !s:stopped
455b3307b5eSBram Moolenaar      let do_continue = 1
456b3307b5eSBram Moolenaar      call s:SendCommand('-exec-interrupt')
457b3307b5eSBram Moolenaar      sleep 10m
458b3307b5eSBram Moolenaar    endif
459b3307b5eSBram Moolenaar    call term_sendkeys(s:gdbbuf, a:cmd . "\r")
460b3307b5eSBram Moolenaar    if do_continue
461b3307b5eSBram Moolenaar      Continue
462b3307b5eSBram Moolenaar    endif
463b3307b5eSBram Moolenaar  endif
464b3307b5eSBram Moolenaarendfunc
465b3307b5eSBram Moolenaar
466b3307b5eSBram Moolenaar" Function called when entering a line in the prompt buffer.
467b3307b5eSBram Moolenaarfunc s:PromptCallback(text)
468b3307b5eSBram Moolenaar  call s:SendCommand(a:text)
469b3307b5eSBram Moolenaarendfunc
470b3307b5eSBram Moolenaar
4714551c0a9SBram Moolenaar" Function called when pressing CTRL-C in the prompt buffer and when placing a
4724551c0a9SBram Moolenaar" breakpoint.
473b3307b5eSBram Moolenaarfunc s:PromptInterrupt()
4742ed890f1SBram Moolenaar  call ch_log('Interrupting gdb')
4752ed890f1SBram Moolenaar  if has('win32')
4762ed890f1SBram Moolenaar    " Using job_stop() does not work on MS-Windows, need to send SIGTRAP to
4772ed890f1SBram Moolenaar    " the debugger program so that gdb responds again.
4784551c0a9SBram Moolenaar    if s:pid == 0
4794551c0a9SBram Moolenaar      echoerr 'Cannot interrupt gdb, did not find a process ID'
4804551c0a9SBram Moolenaar    else
4814551c0a9SBram Moolenaar      call debugbreak(s:pid)
4824551c0a9SBram Moolenaar    endif
4832ed890f1SBram Moolenaar  else
4842ed890f1SBram Moolenaar    call job_stop(s:gdbjob, 'int')
4852ed890f1SBram Moolenaar  endif
486b3307b5eSBram Moolenaarendfunc
487b3307b5eSBram Moolenaar
488b3307b5eSBram Moolenaar" Function called when gdb outputs text.
489b3307b5eSBram Moolenaarfunc s:GdbOutCallback(channel, text)
490b3307b5eSBram Moolenaar  call ch_log('received from gdb: ' . a:text)
491b3307b5eSBram Moolenaar
492b3307b5eSBram Moolenaar  " Drop the gdb prompt, we have our own.
493b3307b5eSBram Moolenaar  " Drop status and echo'd commands.
494a15b0a93SBram Moolenaar  if a:text == '(gdb) ' || a:text == '^done' || a:text[0] == '&'
495b3307b5eSBram Moolenaar    return
496b3307b5eSBram Moolenaar  endif
497b3307b5eSBram Moolenaar  if a:text =~ '^^error,msg='
498b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[11:])
499b3307b5eSBram Moolenaar    if exists('s:evalexpr') && text =~ 'A syntax error in expression, near\|No symbol .* in current context'
500b3307b5eSBram Moolenaar      " Silently drop evaluation errors.
501b3307b5eSBram Moolenaar      unlet s:evalexpr
502b3307b5eSBram Moolenaar      return
503b3307b5eSBram Moolenaar    endif
504b3307b5eSBram Moolenaar  elseif a:text[0] == '~'
505b3307b5eSBram Moolenaar    let text = s:DecodeMessage(a:text[1:])
506b3307b5eSBram Moolenaar  else
507b3307b5eSBram Moolenaar    call s:CommOutput(a:channel, a:text)
508b3307b5eSBram Moolenaar    return
509b3307b5eSBram Moolenaar  endif
510b3307b5eSBram Moolenaar
511b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
512b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
513b3307b5eSBram Moolenaar
514b3307b5eSBram Moolenaar  " Add the output above the current prompt.
515b3307b5eSBram Moolenaar  call append(line('$') - 1, text)
5164551c0a9SBram Moolenaar  set modified
517b3307b5eSBram Moolenaar
518b3307b5eSBram Moolenaar  call win_gotoid(curwinid)
519b3307b5eSBram Moolenaarendfunc
520b3307b5eSBram Moolenaar
521b3307b5eSBram Moolenaar" Decode a message from gdb.  quotedText starts with a ", return the text up
522b3307b5eSBram Moolenaar" to the next ", unescaping characters.
523b3307b5eSBram Moolenaarfunc s:DecodeMessage(quotedText)
524b3307b5eSBram Moolenaar  if a:quotedText[0] != '"'
525a15b0a93SBram Moolenaar    echoerr 'DecodeMessage(): missing quote in ' . a:quotedText
526b3307b5eSBram Moolenaar    return
527b3307b5eSBram Moolenaar  endif
528b3307b5eSBram Moolenaar  let result = ''
529b3307b5eSBram Moolenaar  let i = 1
530b3307b5eSBram Moolenaar  while a:quotedText[i] != '"' && i < len(a:quotedText)
531b3307b5eSBram Moolenaar    if a:quotedText[i] == '\'
532b3307b5eSBram Moolenaar      let i += 1
533b3307b5eSBram Moolenaar      if a:quotedText[i] == 'n'
534b3307b5eSBram Moolenaar	" drop \n
535b3307b5eSBram Moolenaar	let i += 1
536b3307b5eSBram Moolenaar	continue
537589edb34SBram Moolenaar      elseif a:quotedText[i] == 't'
538589edb34SBram Moolenaar	" append \t
539589edb34SBram Moolenaar	let i += 1
540589edb34SBram Moolenaar	let result .= "\t"
541589edb34SBram Moolenaar	continue
542b3307b5eSBram Moolenaar      endif
543b3307b5eSBram Moolenaar    endif
544b3307b5eSBram Moolenaar    let result .= a:quotedText[i]
545b3307b5eSBram Moolenaar    let i += 1
546b3307b5eSBram Moolenaar  endwhile
547b3307b5eSBram Moolenaar  return result
548b3307b5eSBram Moolenaarendfunc
549b3307b5eSBram Moolenaar
550a15b0a93SBram Moolenaar" Extract the "name" value from a gdb message with fullname="name".
551a15b0a93SBram Moolenaarfunc s:GetFullname(msg)
5525378e1cfSBram Moolenaar  if a:msg !~ 'fullname'
5535378e1cfSBram Moolenaar    return ''
5545378e1cfSBram Moolenaar  endif
555a15b0a93SBram Moolenaar  let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
556a15b0a93SBram Moolenaar  if has('win32') && name =~ ':\\\\'
557a15b0a93SBram Moolenaar    " sometimes the name arrives double-escaped
558a15b0a93SBram Moolenaar    let name = substitute(name, '\\\\', '\\', 'g')
559a15b0a93SBram Moolenaar  endif
560a15b0a93SBram Moolenaar  return name
561a15b0a93SBram Moolenaarendfunc
562a15b0a93SBram Moolenaar
563*82be4849SBram Moolenaar" Extract the "addr" value from a gdb message with addr="0x0001234".
564*82be4849SBram Moolenaarfunc s:GetAsmAddr(msg)
565*82be4849SBram Moolenaar  if a:msg !~ 'addr='
566*82be4849SBram Moolenaar    return ''
567*82be4849SBram Moolenaar  endif
568*82be4849SBram Moolenaar  let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', ''))
569*82be4849SBram Moolenaar  return addr
570*82be4849SBram Moolenaarendfunc
571b3307b5eSBram Moolenaarfunc s:EndTermDebug(job, status)
572fe386641SBram Moolenaar  exe 'bwipe! ' . s:commbuf
573b3623a38SBram Moolenaar  unlet s:gdbwin
574e09ba7baSBram Moolenaar
575b3307b5eSBram Moolenaar  call s:EndDebugCommon()
576b3307b5eSBram Moolenaarendfunc
577b3307b5eSBram Moolenaar
578b3307b5eSBram Moolenaarfunc s:EndDebugCommon()
579e09ba7baSBram Moolenaar  let curwinid = win_getid(winnr())
580e09ba7baSBram Moolenaar
581b3307b5eSBram Moolenaar  if exists('s:ptybuf') && s:ptybuf
582b3307b5eSBram Moolenaar    exe 'bwipe! ' . s:ptybuf
583b3307b5eSBram Moolenaar  endif
584b3307b5eSBram Moolenaar
585cb80aa2dSBram Moolenaar  " Restore 'signcolumn' in all buffers for which it was set.
586b3307b5eSBram Moolenaar  call win_gotoid(s:sourcewin)
587cb80aa2dSBram Moolenaar  let was_buf = bufnr()
588cb80aa2dSBram Moolenaar  for bufnr in s:signcolumn_buflist
589cb80aa2dSBram Moolenaar    if bufexists(bufnr)
590cb80aa2dSBram Moolenaar      exe bufnr .. "buf"
591cb80aa2dSBram Moolenaar      if exists('b:save_signcolumn')
592cb80aa2dSBram Moolenaar	let &signcolumn = b:save_signcolumn
593cb80aa2dSBram Moolenaar	unlet b:save_signcolumn
594cb80aa2dSBram Moolenaar      endif
595cb80aa2dSBram Moolenaar    endif
596cb80aa2dSBram Moolenaar  endfor
597cb80aa2dSBram Moolenaar  exe was_buf .. "buf"
598cb80aa2dSBram Moolenaar
599e09ba7baSBram Moolenaar  call s:DeleteCommands()
600e09ba7baSBram Moolenaar
601e09ba7baSBram Moolenaar  call win_gotoid(curwinid)
602b3307b5eSBram Moolenaar
60338baa3e6SBram Moolenaar  if s:save_columns > 0
60438baa3e6SBram Moolenaar    let &columns = s:save_columns
60538baa3e6SBram Moolenaar  endif
6061b9645deSBram Moolenaar
607246fe03dSBram Moolenaar  if has("balloon_eval") || has("balloon_eval_term")
608246fe03dSBram Moolenaar    set balloonexpr=
60951b0f370SBram Moolenaar    if has("balloon_eval")
61051b0f370SBram Moolenaar      set noballooneval
611246fe03dSBram Moolenaar    endif
61251b0f370SBram Moolenaar    if has("balloon_eval_term")
61351b0f370SBram Moolenaar      set noballoonevalterm
61451b0f370SBram Moolenaar    endif
61551b0f370SBram Moolenaar  endif
61651b0f370SBram Moolenaar
6171b9645deSBram Moolenaar  au! TermDebug
618fe386641SBram Moolenaarendfunc
619fe386641SBram Moolenaar
620b3307b5eSBram Moolenaarfunc s:EndPromptDebug(job, status)
621b3307b5eSBram Moolenaar  let curwinid = win_getid(winnr())
622b3307b5eSBram Moolenaar  call win_gotoid(s:gdbwin)
6234551c0a9SBram Moolenaar  set nomodified
624b3307b5eSBram Moolenaar  close
625b3307b5eSBram Moolenaar  if curwinid != s:gdbwin
626b3307b5eSBram Moolenaar    call win_gotoid(curwinid)
627b3307b5eSBram Moolenaar  endif
628b3307b5eSBram Moolenaar
629b3307b5eSBram Moolenaar  call s:EndDebugCommon()
630b3307b5eSBram Moolenaar  unlet s:gdbwin
631b3307b5eSBram Moolenaar  call ch_log("Returning from EndPromptDebug()")
632b3307b5eSBram Moolenaarendfunc
633b3307b5eSBram Moolenaar
634*82be4849SBram Moolenaar" Disassembly window - added by Michael Sartain
635*82be4849SBram Moolenaar"
636*82be4849SBram Moolenaar" - CommOutput: disassemble $pc
637*82be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n"
638*82be4849SBram Moolenaar" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n"
639*82be4849SBram Moolenaar" - CommOutput: ~"   0x0000555556466f69 <+0>:\tpush   rbp\n"
640*82be4849SBram Moolenaar" ...
641*82be4849SBram Moolenaar" - CommOutput: ~"   0x0000555556467cd0:\tpop    rbp\n"
642*82be4849SBram Moolenaar" - CommOutput: ~"   0x0000555556467cd1:\tret    \n"
643*82be4849SBram Moolenaar" - CommOutput: ~"End of assembler dump.\n"
644*82be4849SBram Moolenaar" - CommOutput: ^done
645*82be4849SBram Moolenaar
646*82be4849SBram Moolenaar" - CommOutput: disassemble $pc
647*82be4849SBram Moolenaar" - CommOutput: &"disassemble $pc\n"
648*82be4849SBram Moolenaar" - CommOutput: &"No function contains specified address.\n"
649*82be4849SBram Moolenaar" - CommOutput: ^error,msg="No function contains specified address."
650*82be4849SBram Moolenaarfunc s:HandleDisasmMsg(msg)
651*82be4849SBram Moolenaar  if a:msg =~ '^\^done'
652*82be4849SBram Moolenaar    let curwinid = win_getid(winnr())
653*82be4849SBram Moolenaar    if win_gotoid(s:asmwin)
654*82be4849SBram Moolenaar      silent normal! gg0"_dG
655*82be4849SBram Moolenaar      call setline(1, s:asm_lines)
656*82be4849SBram Moolenaar      set nomodified
657*82be4849SBram Moolenaar      set filetype=asm
658*82be4849SBram Moolenaar
659*82be4849SBram Moolenaar      let lnum = search('^' . s:asm_addr)
660*82be4849SBram Moolenaar      if lnum != 0
661*82be4849SBram Moolenaar        exe 'sign unplace ' . s:asm_id
662*82be4849SBram Moolenaar        exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
663*82be4849SBram Moolenaar      endif
664*82be4849SBram Moolenaar
665*82be4849SBram Moolenaar      call win_gotoid(curwinid)
666*82be4849SBram Moolenaar    endif
667*82be4849SBram Moolenaar
668*82be4849SBram Moolenaar    let s:parsing_disasm_msg = 0
669*82be4849SBram Moolenaar    let s:asm_lines = []
670*82be4849SBram Moolenaar  elseif a:msg =~ '^\^error,msg='
671*82be4849SBram Moolenaar    if s:parsing_disasm_msg == 1
672*82be4849SBram Moolenaar      " Disassemble call ran into an error. This can happen when gdb can't
673*82be4849SBram Moolenaar      " find the function frame address, so let's try to disassemble starting
674*82be4849SBram Moolenaar      " at current PC
675*82be4849SBram Moolenaar      call s:SendCommand('disassemble $pc,+100')
676*82be4849SBram Moolenaar    endif
677*82be4849SBram Moolenaar    let s:parsing_disasm_msg = 0
678*82be4849SBram Moolenaar  elseif a:msg =~ '\&\"disassemble \$pc'
679*82be4849SBram Moolenaar    if a:msg =~ '+100'
680*82be4849SBram Moolenaar      " This is our second disasm attempt
681*82be4849SBram Moolenaar      let s:parsing_disasm_msg = 2
682*82be4849SBram Moolenaar    endif
683*82be4849SBram Moolenaar  else
684*82be4849SBram Moolenaar    let value = substitute(a:msg, '^\~\"[ ]*', '', '')
685*82be4849SBram Moolenaar    let value = substitute(value, '^=>[ ]*', '', '')
686*82be4849SBram Moolenaar    let value = substitute(value, '\\n\"
687*82be4849SBram Moolenaar$', '', '')
688*82be4849SBram Moolenaar    let value = substitute(value, '\\n\"$', '', '')
689*82be4849SBram Moolenaar    let value = substitute(value, '
690*82be4849SBram Moolenaar', '', '')
691*82be4849SBram Moolenaar    let value = substitute(value, '\\t', ' ', 'g')
692*82be4849SBram Moolenaar
693*82be4849SBram Moolenaar    if value != '' || !empty(s:asm_lines)
694*82be4849SBram Moolenaar      call add(s:asm_lines, value)
695*82be4849SBram Moolenaar    endif
696*82be4849SBram Moolenaar  endif
697fe386641SBram Moolenaarendfunc
698fe386641SBram Moolenaar
699fe386641SBram Moolenaar" Handle a message received from gdb on the GDB/MI interface.
700fe386641SBram Moolenaarfunc s:CommOutput(chan, msg)
701fe386641SBram Moolenaar  let msgs = split(a:msg, "\r")
702fe386641SBram Moolenaar
703fe386641SBram Moolenaar  for msg in msgs
704fe386641SBram Moolenaar    " remove prefixed NL
705fe386641SBram Moolenaar    if msg[0] == "\n"
706*82be4849SBram Moolenaar      let msg = msg[1:]
707*82be4849SBram Moolenaar    endif
708*82be4849SBram Moolenaar
709*82be4849SBram Moolenaar    if s:parsing_disasm_msg
7101b9645deSBram Moolenaar      call s:HandleDisasmMsg(msg)
711e09ba7baSBram Moolenaar    elseif msg != ''
71245d5f26dSBram Moolenaar      if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
713e09ba7baSBram Moolenaar	call s:HandleCursor(msg)
714e09ba7baSBram Moolenaar      elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
715e09ba7baSBram Moolenaar	call s:HandleNewBreakpoint(msg)
7164551c0a9SBram Moolenaar      elseif msg =~ '^=breakpoint-deleted,'
7174551c0a9SBram Moolenaar	call s:HandleBreakpointDelete(msg)
71845d5f26dSBram Moolenaar      elseif msg =~ '^=thread-group-started'
71945d5f26dSBram Moolenaar	call s:HandleProgramRun(msg)
72045d5f26dSBram Moolenaar      elseif msg =~ '^\^done,value='
72145d5f26dSBram Moolenaar	call s:HandleEvaluate(msg)
722*82be4849SBram Moolenaar      elseif msg =~ '^\^error,msg='
723*82be4849SBram Moolenaar	call s:HandleError(msg)
724*82be4849SBram Moolenaar      elseif msg =~ '^disassemble'
725e09ba7baSBram Moolenaar        let s:parsing_disasm_msg = 1
726e09ba7baSBram Moolenaar        let s:asm_lines = []
727e09ba7baSBram Moolenaar      endif
728e09ba7baSBram Moolenaar    endif
729e09ba7baSBram Moolenaar  endfor
730589edb34SBram Moolenaarendfunc
731589edb34SBram Moolenaar
732589edb34SBram Moolenaarfunc s:GotoProgram()
733589edb34SBram Moolenaar  if has('win32')
734589edb34SBram Moolenaar    if executable('powershell')
735589edb34SBram Moolenaar      call system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', s:pid))
736469bdbdeSBram Moolenaar    endif
737589edb34SBram Moolenaar  else
738589edb34SBram Moolenaar    call win_gotoid(s:ptywin)
739589edb34SBram Moolenaar  endif
740e09ba7baSBram Moolenaarendfunc
741e09ba7baSBram Moolenaar
742963c1ad5SBram Moolenaar" Install commands in the current window to control the debugger.
743963c1ad5SBram Moolenaarfunc s:InstallCommands()
744963c1ad5SBram Moolenaar  let save_cpo = &cpo
745589edb34SBram Moolenaar  set cpo&vim
74671137fedSBram Moolenaar
747e09ba7baSBram Moolenaar  command -nargs=? Break call s:SetBreakpoint(<q-args>)
74845d5f26dSBram Moolenaar  command Clear call s:ClearBreakpoint()
749e09ba7baSBram Moolenaar  command Step call s:SendCommand('-exec-step')
75060e73f2aSBram Moolenaar  command Over call s:SendCommand('-exec-next')
75160e73f2aSBram Moolenaar  command Finish call s:SendCommand('-exec-finish')
75260e73f2aSBram Moolenaar  command -nargs=* Run call s:Run(<q-args>)
753b3307b5eSBram Moolenaar  command -nargs=* Arguments call s:SendCommand('-exec-arguments ' . <q-args>)
754b3307b5eSBram Moolenaar  command Stop call s:SendCommand('-exec-interrupt')
755b3307b5eSBram Moolenaar
756b3307b5eSBram Moolenaar  " using -exec-continue results in CTRL-C in gdb window not working
757b3307b5eSBram Moolenaar  if s:way == 'prompt'
758b3307b5eSBram Moolenaar    command Continue call s:SendCommand('continue')
759b3307b5eSBram Moolenaar  else
760b3307b5eSBram Moolenaar    command Continue call term_sendkeys(s:gdbbuf, "continue\r")
76145d5f26dSBram Moolenaar  endif
76245d5f26dSBram Moolenaar
763589edb34SBram Moolenaar  command -range -nargs=* Evaluate call s:Evaluate(<range>, <q-args>)
764b3307b5eSBram Moolenaar  command Gdb call win_gotoid(s:gdbwin)
765*82be4849SBram Moolenaar  command Program call s:GotoProgram()
76671137fedSBram Moolenaar  command Source call s:GotoSourcewinOrCreateIt()
76745d5f26dSBram Moolenaar  command Asm call s:GotoAsmwinOrCreateIt()
768388a5d4fSBram Moolenaar  command Winbar call s:InstallWinbar()
769388a5d4fSBram Moolenaar
77045d5f26dSBram Moolenaar  if !exists('g:termdebug_map_K') || g:termdebug_map_K
771388a5d4fSBram Moolenaar    let s:k_map_saved = maparg('K', 'n', 0, 1)
7721b9645deSBram Moolenaar    nnoremap K :Evaluate<CR>
773f0b03c4eSBram Moolenaar  endif
77471137fedSBram Moolenaar
77571137fedSBram Moolenaar  if has('menu') && &mouse != ''
77671137fedSBram Moolenaar    call s:InstallWinbar()
77771137fedSBram Moolenaar
77871137fedSBram Moolenaar    if !exists('g:termdebug_popup') || g:termdebug_popup != 0
77971137fedSBram Moolenaar      let s:saved_mousemodel = &mousemodel
78071137fedSBram Moolenaar      let &mousemodel = 'popup_setpos'
78171137fedSBram Moolenaar      an 1.200 PopUp.-SEP3-	<Nop>
78271137fedSBram Moolenaar      an 1.210 PopUp.Set\ breakpoint	:Break<CR>
78371137fedSBram Moolenaar      an 1.220 PopUp.Clear\ breakpoint	:Clear<CR>
78471137fedSBram Moolenaar      an 1.230 PopUp.Evaluate		:Evaluate<CR>
785963c1ad5SBram Moolenaar    endif
786963c1ad5SBram Moolenaar  endif
78771137fedSBram Moolenaar
78871137fedSBram Moolenaar  let &cpo = save_cpo
78971137fedSBram Moolenaarendfunc
79071137fedSBram Moolenaar
79171137fedSBram Moolenaarlet s:winbar_winids = []
79271137fedSBram Moolenaar
793c4b533e1SBram Moolenaar" Install the window toolbar in the current window.
79424a98a0eSBram Moolenaarfunc s:InstallWinbar()
79524a98a0eSBram Moolenaar  if has('menu') && &mouse != ''
79624a98a0eSBram Moolenaar    nnoremenu WinBar.Step   :Step<CR>
79724a98a0eSBram Moolenaar    nnoremenu WinBar.Next   :Over<CR>
79860e73f2aSBram Moolenaar    nnoremenu WinBar.Finish :Finish<CR>
79924a98a0eSBram Moolenaar    nnoremenu WinBar.Cont   :Continue<CR>
80071137fedSBram Moolenaar    nnoremenu WinBar.Stop   :Stop<CR>
801c4b533e1SBram Moolenaar    nnoremenu WinBar.Eval   :Evaluate<CR>
802e09ba7baSBram Moolenaar    call add(s:winbar_winids, win_getid(winnr()))
803e09ba7baSBram Moolenaar  endif
804e09ba7baSBram Moolenaarendfunc
805e09ba7baSBram Moolenaar
806e09ba7baSBram Moolenaar" Delete installed debugger commands in the current window.
80771137fedSBram Moolenaarfunc s:DeleteCommands()
808e09ba7baSBram Moolenaar  delcommand Break
80945d5f26dSBram Moolenaar  delcommand Clear
810e09ba7baSBram Moolenaar  delcommand Step
81160e73f2aSBram Moolenaar  delcommand Over
81260e73f2aSBram Moolenaar  delcommand Finish
81360e73f2aSBram Moolenaar  delcommand Run
814e09ba7baSBram Moolenaar  delcommand Arguments
81545d5f26dSBram Moolenaar  delcommand Stop
81645d5f26dSBram Moolenaar  delcommand Continue
81745d5f26dSBram Moolenaar  delcommand Evaluate
818b3623a38SBram Moolenaar  delcommand Gdb
819*82be4849SBram Moolenaar  delcommand Program
82071137fedSBram Moolenaar  delcommand Source
82145d5f26dSBram Moolenaar  delcommand Asm
8221b884a00SBram Moolenaar  delcommand Winbar
8231b884a00SBram Moolenaar
8241b884a00SBram Moolenaar  if exists('s:k_map_saved')
8251b884a00SBram Moolenaar    if empty(s:k_map_saved)
826388a5d4fSBram Moolenaar      nunmap K
8271b884a00SBram Moolenaar    else
828388a5d4fSBram Moolenaar      call mapset('n', 0, s:k_map_saved)
829388a5d4fSBram Moolenaar    endif
8301b9645deSBram Moolenaar    unlet s:k_map_saved
8311b9645deSBram Moolenaar  endif
83271137fedSBram Moolenaar
83371137fedSBram Moolenaar  if has('menu')
83471137fedSBram Moolenaar    " Remove the WinBar entries from all windows where it was added.
83571137fedSBram Moolenaar    let curwinid = win_getid(winnr())
8361b9645deSBram Moolenaar    for winid in s:winbar_winids
8371b9645deSBram Moolenaar      if win_gotoid(winid)
8381b9645deSBram Moolenaar	aunmenu WinBar.Step
8391b9645deSBram Moolenaar	aunmenu WinBar.Next
84060e73f2aSBram Moolenaar	aunmenu WinBar.Finish
8411b9645deSBram Moolenaar	aunmenu WinBar.Cont
8421b9645deSBram Moolenaar	aunmenu WinBar.Stop
84371137fedSBram Moolenaar	aunmenu WinBar.Eval
84471137fedSBram Moolenaar      endif
84571137fedSBram Moolenaar    endfor
84671137fedSBram Moolenaar    call win_gotoid(curwinid)
84771137fedSBram Moolenaar    let s:winbar_winids = []
84871137fedSBram Moolenaar
84971137fedSBram Moolenaar    if exists('s:saved_mousemodel')
85071137fedSBram Moolenaar      let &mousemodel = s:saved_mousemodel
85171137fedSBram Moolenaar      unlet s:saved_mousemodel
85271137fedSBram Moolenaar      aunmenu PopUp.-SEP3-
85371137fedSBram Moolenaar      aunmenu PopUp.Set\ breakpoint
85471137fedSBram Moolenaar      aunmenu PopUp.Clear\ breakpoint
85571137fedSBram Moolenaar      aunmenu PopUp.Evaluate
8561b9645deSBram Moolenaar    endif
85745d5f26dSBram Moolenaar  endif
85837402ed5SBram Moolenaar
85937402ed5SBram Moolenaar  exe 'sign unplace ' . s:pc_id
86037402ed5SBram Moolenaar  for [id, entries] in items(s:breakpoints)
86137402ed5SBram Moolenaar    for subid in keys(entries)
86245d5f26dSBram Moolenaar      exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
86345d5f26dSBram Moolenaar    endfor
86437402ed5SBram Moolenaar  endfor
865a15b0a93SBram Moolenaar  unlet s:breakpoints
866a15b0a93SBram Moolenaar  unlet s:breakpoint_locations
867a15b0a93SBram Moolenaar
868a15b0a93SBram Moolenaar  sign undefine debugPC
869a15b0a93SBram Moolenaar  for val in s:BreakpointSigns
8704551c0a9SBram Moolenaar    exe "sign undefine debugBreakpoint" . val
871e09ba7baSBram Moolenaar  endfor
872e09ba7baSBram Moolenaar  let s:BreakpointSigns = []
873e09ba7baSBram Moolenaarendfunc
874589edb34SBram Moolenaar
87560e73f2aSBram Moolenaar" :Break - Set a breakpoint at the cursor position.
87660e73f2aSBram Moolenaarfunc s:SetBreakpoint(at)
87760e73f2aSBram Moolenaar  " Setting a breakpoint may not work while the program is running.
87860e73f2aSBram Moolenaar  " Interrupt to make it work.
87960e73f2aSBram Moolenaar  let do_continue = 0
880b3307b5eSBram Moolenaar  if !s:stopped
8814551c0a9SBram Moolenaar    let do_continue = 1
882b3307b5eSBram Moolenaar    if s:way == 'prompt'
88360e73f2aSBram Moolenaar      call s:PromptInterrupt()
884b3307b5eSBram Moolenaar    else
88560e73f2aSBram Moolenaar      call s:SendCommand('-exec-interrupt')
88660e73f2aSBram Moolenaar    endif
887589edb34SBram Moolenaar    sleep 10m
888a15b0a93SBram Moolenaar  endif
889589edb34SBram Moolenaar
890589edb34SBram Moolenaar  " Use the fname:lnum format, older gdb can't handle --source.
891589edb34SBram Moolenaar  let at = empty(a:at) ?
89260e73f2aSBram Moolenaar        \ fnameescape(expand('%:p')) . ':' . line('.') : a:at
89360e73f2aSBram Moolenaar  call s:SendCommand('-break-insert ' . at)
89460e73f2aSBram Moolenaar  if do_continue
895e09ba7baSBram Moolenaar    call s:SendCommand('-exec-continue')
896e09ba7baSBram Moolenaar  endif
89771137fedSBram Moolenaarendfunc
89871137fedSBram Moolenaar
899e09ba7baSBram Moolenaar" :Clear - Delete a breakpoint at the cursor position.
900e09ba7baSBram Moolenaarfunc s:ClearBreakpoint()
90137402ed5SBram Moolenaar  let fname = fnameescape(expand('%:p'))
90237402ed5SBram Moolenaar  let lnum = line('.')
90337402ed5SBram Moolenaar  let bploc = printf('%s:%d', fname, lnum)
90437402ed5SBram Moolenaar  if has_key(s:breakpoint_locations, bploc)
90537402ed5SBram Moolenaar    let idx = 0
90637402ed5SBram Moolenaar    for id in s:breakpoint_locations[bploc]
90737402ed5SBram Moolenaar      if has_key(s:breakpoints, id)
90837402ed5SBram Moolenaar	" Assume this always works, the reply is simply "^done".
90937402ed5SBram Moolenaar	call s:SendCommand('-break-delete ' . id)
91037402ed5SBram Moolenaar	for subid in keys(s:breakpoints[id])
91137402ed5SBram Moolenaar	  exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
91237402ed5SBram Moolenaar	endfor
913e09ba7baSBram Moolenaar	unlet s:breakpoints[id]
91437402ed5SBram Moolenaar	unlet s:breakpoint_locations[bploc][idx]
91537402ed5SBram Moolenaar	break
916e09ba7baSBram Moolenaar      else
917e09ba7baSBram Moolenaar	let idx += 1
91837402ed5SBram Moolenaar      endif
91937402ed5SBram Moolenaar    endfor
92037402ed5SBram Moolenaar    if empty(s:breakpoint_locations[bploc])
92137402ed5SBram Moolenaar      unlet s:breakpoint_locations[bploc]
922e09ba7baSBram Moolenaar    endif
923e09ba7baSBram Moolenaar  endif
92460e73f2aSBram Moolenaarendfunc
92560e73f2aSBram Moolenaar
92660e73f2aSBram Moolenaarfunc s:Run(args)
92760e73f2aSBram Moolenaar  if a:args != ''
92860e73f2aSBram Moolenaar    call s:SendCommand('-exec-arguments ' . a:args)
92960e73f2aSBram Moolenaar  endif
93060e73f2aSBram Moolenaar  call s:SendCommand('-exec-run')
93151b0f370SBram Moolenaarendfunc
93251b0f370SBram Moolenaar
93351b0f370SBram Moolenaarfunc s:SendEval(expr)
93451b0f370SBram Moolenaar  call s:SendCommand('-data-evaluate-expression "' . a:expr . '"')
93551b0f370SBram Moolenaar  let s:evalexpr = a:expr
93645d5f26dSBram Moolenaarendfunc
93745d5f26dSBram Moolenaar
93845d5f26dSBram Moolenaar" :Evaluate - evaluate what is under the cursor
93945d5f26dSBram Moolenaarfunc s:Evaluate(range, arg)
94045d5f26dSBram Moolenaar  if a:arg != ''
94145d5f26dSBram Moolenaar    let expr = a:arg
94245d5f26dSBram Moolenaar  elseif a:range == 2
94345d5f26dSBram Moolenaar    let pos = getcurpos()
94445d5f26dSBram Moolenaar    let reg = getreg('v', 1, 1)
94545d5f26dSBram Moolenaar    let regt = getregtype('v')
94645d5f26dSBram Moolenaar    normal! gv"vy
94745d5f26dSBram Moolenaar    let expr = @v
94845d5f26dSBram Moolenaar    call setpos('.', pos)
94945d5f26dSBram Moolenaar    call setreg('v', reg, regt)
95045d5f26dSBram Moolenaar  else
95122f1d0e3SBram Moolenaar    let expr = expand('<cexpr>')
95251b0f370SBram Moolenaar  endif
95345d5f26dSBram Moolenaar  let s:ignoreEvalError = 0
95445d5f26dSBram Moolenaar  call s:SendEval(expr)
95522f1d0e3SBram Moolenaarendfunc
95651b0f370SBram Moolenaar
95751b0f370SBram Moolenaarlet s:ignoreEvalError = 0
95845d5f26dSBram Moolenaarlet s:evalFromBalloonExpr = 0
95945d5f26dSBram Moolenaar
9601b9645deSBram Moolenaar" Handle the result of data-evaluate-expression
9611b9645deSBram Moolenaarfunc s:HandleEvaluate(msg)
96251b0f370SBram Moolenaar  let value = substitute(a:msg, '.*value="\(.*\)"', '\1', '')
96351b0f370SBram Moolenaar  let value = substitute(value, '\\"', '"', 'g')
96451b0f370SBram Moolenaar  if s:evalFromBalloonExpr
96551b0f370SBram Moolenaar    if s:evalFromBalloonExprResult == ''
96651b0f370SBram Moolenaar      let s:evalFromBalloonExprResult = s:evalexpr . ': ' . value
96751b0f370SBram Moolenaar    else
96851b0f370SBram Moolenaar      let s:evalFromBalloonExprResult .= ' = ' . value
96951b0f370SBram Moolenaar    endif
9701b9645deSBram Moolenaar    call balloon_show(s:evalFromBalloonExprResult)
97151b0f370SBram Moolenaar  else
9721b9645deSBram Moolenaar    echomsg '"' . s:evalexpr . '": ' . value
9737f2e9d7cSBram Moolenaar  endif
9741b9645deSBram Moolenaar
97522f1d0e3SBram Moolenaar  if s:evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$'
97651b0f370SBram Moolenaar    " Looks like a pointer, also display what it points to.
97751b0f370SBram Moolenaar    let s:ignoreEvalError = 1
97851b0f370SBram Moolenaar    call s:SendEval('*' . s:evalexpr)
9791b9645deSBram Moolenaar  else
98045d5f26dSBram Moolenaar    let s:evalFromBalloonExpr = 0
98145d5f26dSBram Moolenaar  endif
98251b0f370SBram Moolenaarendfunc
98351b0f370SBram Moolenaar
98451b0f370SBram Moolenaar" Show a balloon with information of the variable under the mouse pointer,
985b3307b5eSBram Moolenaar" if there is any.
986396e829fSBram Moolenaarfunc TermDebugBalloonExpr()
987b3307b5eSBram Moolenaar  if v:beval_winid != s:sourcewin
988b3307b5eSBram Moolenaar    return ''
989b3307b5eSBram Moolenaar  endif
990b3307b5eSBram Moolenaar  if !s:stopped
991396e829fSBram Moolenaar    " Only evaluate when stopped, otherwise setting a breakpoint using the
99251b0f370SBram Moolenaar    " mouse triggers a balloon.
99351b0f370SBram Moolenaar    return ''
99451b0f370SBram Moolenaar  endif
99522f1d0e3SBram Moolenaar  let s:evalFromBalloonExpr = 1
99622f1d0e3SBram Moolenaar  let s:evalFromBalloonExprResult = ''
99751b0f370SBram Moolenaar  let s:ignoreEvalError = 1
99851b0f370SBram Moolenaar  call s:SendEval(v:beval_text)
99951b0f370SBram Moolenaar  return ''
100045d5f26dSBram Moolenaarendfunc
100145d5f26dSBram Moolenaar
100222f1d0e3SBram Moolenaar" Handle an error.
100351b0f370SBram Moolenaarfunc s:HandleError(msg)
100422f1d0e3SBram Moolenaar  if s:ignoreEvalError
100522f1d0e3SBram Moolenaar    " Result of s:SendEval() failed, ignore.
100651b0f370SBram Moolenaar    let s:ignoreEvalError = 0
100751b0f370SBram Moolenaar    let s:evalFromBalloonExpr = 0
100845d5f26dSBram Moolenaar    return
100945d5f26dSBram Moolenaar  endif
101045d5f26dSBram Moolenaar  echoerr substitute(a:msg, '.*msg="\(.*\)"', '\1', '')
1011b3307b5eSBram Moolenaarendfunc
1012b3307b5eSBram Moolenaar
1013c4b533e1SBram Moolenaarfunc s:GotoSourcewinOrCreateIt()
1014b3307b5eSBram Moolenaar  if !win_gotoid(s:sourcewin)
1015c4b533e1SBram Moolenaar    new
1016c4b533e1SBram Moolenaar    let s:sourcewin = win_getid(winnr())
1017c4b533e1SBram Moolenaar    call s:InstallWinbar()
1018c4b533e1SBram Moolenaar  endif
1019*82be4849SBram Moolenaarendfunc
1020*82be4849SBram Moolenaar
1021*82be4849SBram Moolenaarfunc s:GotoAsmwinOrCreateIt()
1022*82be4849SBram Moolenaar  if !win_gotoid(s:asmwin)
1023*82be4849SBram Moolenaar    if win_gotoid(s:sourcewin)
1024*82be4849SBram Moolenaar      exe 'rightbelow new'
1025*82be4849SBram Moolenaar    else
1026*82be4849SBram Moolenaar      exe 'new'
1027*82be4849SBram Moolenaar    endif
1028*82be4849SBram Moolenaar
1029*82be4849SBram Moolenaar    let s:asmwin = win_getid(winnr())
1030*82be4849SBram Moolenaar
1031*82be4849SBram Moolenaar    setlocal nowrap
1032*82be4849SBram Moolenaar    setlocal number
1033*82be4849SBram Moolenaar    setlocal noswapfile
1034*82be4849SBram Moolenaar    setlocal buftype=nofile
1035*82be4849SBram Moolenaar
1036*82be4849SBram Moolenaar    let asmbuf = bufnr('Termdebug-asm-listing')
1037*82be4849SBram Moolenaar    if asmbuf > 0
1038*82be4849SBram Moolenaar      exe 'buffer' . asmbuf
1039*82be4849SBram Moolenaar    else
1040*82be4849SBram Moolenaar      exe 'file Termdebug-asm-listing'
1041*82be4849SBram Moolenaar    endif
1042*82be4849SBram Moolenaar
1043*82be4849SBram Moolenaar    if exists('g:termdebug_disasm_window')
1044*82be4849SBram Moolenaar      if g:termdebug_disasm_window > 1
1045*82be4849SBram Moolenaar        exe 'resize ' . g:termdebug_disasm_window
1046*82be4849SBram Moolenaar      endif
1047*82be4849SBram Moolenaar    endif
1048*82be4849SBram Moolenaar  endif
1049*82be4849SBram Moolenaar
1050*82be4849SBram Moolenaar  if s:asm_addr != ''
1051*82be4849SBram Moolenaar    let lnum = search('^' . s:asm_addr)
1052*82be4849SBram Moolenaar    if lnum == 0
1053*82be4849SBram Moolenaar      if s:stopped
1054*82be4849SBram Moolenaar        call s:SendCommand('disassemble $pc')
1055*82be4849SBram Moolenaar      endif
1056*82be4849SBram Moolenaar    else
1057*82be4849SBram Moolenaar      exe 'sign unplace ' . s:asm_id
1058*82be4849SBram Moolenaar      exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
1059*82be4849SBram Moolenaar    endif
1060*82be4849SBram Moolenaar  endif
1061e09ba7baSBram Moolenaarendfunc
1062e09ba7baSBram Moolenaar
1063e09ba7baSBram Moolenaar" Handle stopping and running message from gdb.
1064fe386641SBram Moolenaar" Will update the sign that shows the current position.
1065fe386641SBram Moolenaarfunc s:HandleCursor(msg)
106660e73f2aSBram Moolenaar  let wid = win_getid(winnr())
10674551c0a9SBram Moolenaar
106860e73f2aSBram Moolenaar  if a:msg =~ '^\*stopped'
106960e73f2aSBram Moolenaar    call ch_log('program stopped')
10704551c0a9SBram Moolenaar    let s:stopped = 1
107160e73f2aSBram Moolenaar  elseif a:msg =~ '^\*running'
107260e73f2aSBram Moolenaar    call ch_log('program running')
107360e73f2aSBram Moolenaar    let s:stopped = 0
1074a15b0a93SBram Moolenaar  endif
1075a15b0a93SBram Moolenaar
1076a15b0a93SBram Moolenaar  if a:msg =~ 'fullname='
1077a15b0a93SBram Moolenaar    let fname = s:GetFullname(a:msg)
1078a15b0a93SBram Moolenaar  else
1079*82be4849SBram Moolenaar    let fname = ''
1080*82be4849SBram Moolenaar  endif
1081*82be4849SBram Moolenaar
1082*82be4849SBram Moolenaar  if a:msg =~ 'addr='
1083*82be4849SBram Moolenaar    let asm_addr = s:GetAsmAddr(a:msg)
1084*82be4849SBram Moolenaar    if asm_addr != ''
1085*82be4849SBram Moolenaar      let s:asm_addr = asm_addr
1086*82be4849SBram Moolenaar
1087*82be4849SBram Moolenaar      let curwinid = win_getid(winnr())
1088*82be4849SBram Moolenaar      if win_gotoid(s:asmwin)
1089*82be4849SBram Moolenaar        let lnum = search('^' . s:asm_addr)
1090*82be4849SBram Moolenaar        if lnum == 0
1091*82be4849SBram Moolenaar          call s:SendCommand('disassemble $pc')
1092*82be4849SBram Moolenaar        else
1093*82be4849SBram Moolenaar          exe 'sign unplace ' . s:asm_id
1094*82be4849SBram Moolenaar          exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC'
1095*82be4849SBram Moolenaar        endif
1096*82be4849SBram Moolenaar
1097*82be4849SBram Moolenaar        call win_gotoid(curwinid)
1098*82be4849SBram Moolenaar      endif
1099*82be4849SBram Moolenaar    endif
11001b9645deSBram Moolenaar  endif
1101e09ba7baSBram Moolenaar
1102fe386641SBram Moolenaar  if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
11034551c0a9SBram Moolenaar    let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
11041b9645deSBram Moolenaar    if lnum =~ '^[0-9]*$'
1105fe386641SBram Moolenaar    call s:GotoSourcewinOrCreateIt()
1106fe386641SBram Moolenaar      if expand('%:p') != fnamemodify(fname, ':p')
1107fe386641SBram Moolenaar	if &modified
1108b3307b5eSBram Moolenaar	  " TODO: find existing window
1109c4b533e1SBram Moolenaar	  exe 'split ' . fnameescape(fname)
1110fe386641SBram Moolenaar	  let s:sourcewin = win_getid(winnr())
1111fe386641SBram Moolenaar	  call s:InstallWinbar()
1112fe386641SBram Moolenaar	else
1113fe386641SBram Moolenaar	  exe 'edit ' . fnameescape(fname)
1114fe386641SBram Moolenaar	endif
111501164a65SBram Moolenaar      endif
111639f7aa3cSBram Moolenaar      exe lnum
1117cb80aa2dSBram Moolenaar      exe 'sign unplace ' . s:pc_id
1118cb80aa2dSBram Moolenaar      exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC priority=110 file=' . fname
1119cb80aa2dSBram Moolenaar      if !exists('b:save_signcolumn')
1120cb80aa2dSBram Moolenaar	let b:save_signcolumn = &signcolumn
1121fe386641SBram Moolenaar	call add(s:signcolumn_buflist, bufnr())
1122fe386641SBram Moolenaar      endif
11234551c0a9SBram Moolenaar      setlocal signcolumn=yes
1124fe386641SBram Moolenaar    endif
1125fe386641SBram Moolenaar  elseif !s:stopped || fname != ''
1126fe386641SBram Moolenaar    exe 'sign unplace ' . s:pc_id
1127fe386641SBram Moolenaar  endif
1128e09ba7baSBram Moolenaar
1129e09ba7baSBram Moolenaar  call win_gotoid(wid)
1130de1a8314SBram Moolenaarendfunc
1131a15b0a93SBram Moolenaar
113237402ed5SBram Moolenaarlet s:BreakpointSigns = []
113337402ed5SBram Moolenaar
113437402ed5SBram Moolenaarfunc s:CreateBreakpoint(id, subid)
113537402ed5SBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
113637402ed5SBram Moolenaar  if index(s:BreakpointSigns, nr) == -1
1137de1a8314SBram Moolenaar    call add(s:BreakpointSigns, nr)
1138de1a8314SBram Moolenaar    exe "sign define debugBreakpoint" . nr . " text=" . substitute(nr, '\..*', '', '') . " texthl=debugBreakpoint"
1139de1a8314SBram Moolenaar  endif
114037402ed5SBram Moolenaarendfunc
114137402ed5SBram Moolenaar
11425378e1cfSBram Moolenaarfunc! s:SplitMsg(s)
11435378e1cfSBram Moolenaar  return split(a:s, '{.\{-}}\zs')
1144e09ba7baSBram Moolenaarendfunction
1145e09ba7baSBram Moolenaar
1146e09ba7baSBram Moolenaar" Handle setting a breakpoint
11476dccc962SBram Moolenaar" Will update the sign that shows the breakpoint
11486dccc962SBram Moolenaarfunc s:HandleNewBreakpoint(msg)
11496dccc962SBram Moolenaar  if a:msg !~ 'fullname='
11506dccc962SBram Moolenaar    " a watch does not have a file name
11515378e1cfSBram Moolenaar    return
11525378e1cfSBram Moolenaar  endif
11535378e1cfSBram Moolenaar  for msg in s:SplitMsg(a:msg)
11545378e1cfSBram Moolenaar    let fname = s:GetFullname(msg)
11555378e1cfSBram Moolenaar    if empty(fname)
11565378e1cfSBram Moolenaar      continue
11575378e1cfSBram Moolenaar    endif
1158e09ba7baSBram Moolenaar    let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
1159fe386641SBram Moolenaar    if empty(nr)
1160e09ba7baSBram Moolenaar      return
116137402ed5SBram Moolenaar    endif
116237402ed5SBram Moolenaar
116337402ed5SBram Moolenaar    " If "nr" is 123 it becomes "123.0" and subid is "0".
116437402ed5SBram Moolenaar    " If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded.
116537402ed5SBram Moolenaar    let [id, subid; _] = map(split(nr . '.0', '\.'), 'v:val + 0')
116637402ed5SBram Moolenaar    call s:CreateBreakpoint(id, subid)
116737402ed5SBram Moolenaar
116837402ed5SBram Moolenaar    if has_key(s:breakpoints, id)
116937402ed5SBram Moolenaar      let entries = s:breakpoints[id]
117037402ed5SBram Moolenaar    else
117137402ed5SBram Moolenaar      let entries = {}
117237402ed5SBram Moolenaar      let s:breakpoints[id] = entries
117337402ed5SBram Moolenaar    endif
1174e09ba7baSBram Moolenaar    if has_key(entries, subid)
1175e09ba7baSBram Moolenaar      let entry = entries[subid]
117637402ed5SBram Moolenaar    else
1177fe386641SBram Moolenaar      let entry = {}
1178e09ba7baSBram Moolenaar      let entries[subid] = entry
11795378e1cfSBram Moolenaar    endif
1180e09ba7baSBram Moolenaar
1181e09ba7baSBram Moolenaar    let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
11821b9645deSBram Moolenaar    let entry['fname'] = fname
118337402ed5SBram Moolenaar    let entry['lnum'] = lnum
118437402ed5SBram Moolenaar
118537402ed5SBram Moolenaar    let bploc = printf('%s:%d', fname, lnum)
118637402ed5SBram Moolenaar    if !has_key(s:breakpoint_locations, bploc)
118737402ed5SBram Moolenaar      let s:breakpoint_locations[bploc] = []
118837402ed5SBram Moolenaar    endif
11891b9645deSBram Moolenaar    let s:breakpoint_locations[bploc] += [id]
119037402ed5SBram Moolenaar
11911b9645deSBram Moolenaar    if bufloaded(fname)
11925378e1cfSBram Moolenaar      call s:PlaceSign(id, subid, entry)
11931b9645deSBram Moolenaar    endif
11941b9645deSBram Moolenaar  endfor
119537402ed5SBram Moolenaarendfunc
119637402ed5SBram Moolenaar
11973132cdddSBram Moolenaarfunc s:PlaceSign(id, subid, entry)
11981b9645deSBram Moolenaar  let nr = printf('%d.%d', a:id, a:subid)
1199e09ba7baSBram Moolenaar  exe 'sign place ' . s:Breakpoint2SignNumber(a:id, a:subid) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . nr . ' priority=110 file=' . a:entry['fname']
1200e09ba7baSBram Moolenaar  let a:entry['placed'] = 1
1201e09ba7baSBram Moolenaarendfunc
1202e09ba7baSBram Moolenaar
1203e09ba7baSBram Moolenaar" Handle deleting a breakpoint
120437402ed5SBram Moolenaar" Will remove the sign that shows the breakpoint
120537402ed5SBram Moolenaarfunc s:HandleBreakpointDelete(msg)
1206e09ba7baSBram Moolenaar  let id = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0
1207e09ba7baSBram Moolenaar  if empty(id)
120837402ed5SBram Moolenaar    return
120937402ed5SBram Moolenaar  endif
12101b9645deSBram Moolenaar  if has_key(s:breakpoints, id)
121137402ed5SBram Moolenaar    for [subid, entry] in items(s:breakpoints[id])
12121b9645deSBram Moolenaar      if has_key(entry, 'placed')
12131b9645deSBram Moolenaar	exe 'sign unplace ' . s:Breakpoint2SignNumber(id, subid)
12145378e1cfSBram Moolenaar	unlet entry['placed']
121537402ed5SBram Moolenaar      endif
121637402ed5SBram Moolenaar    endfor
1217c572da5fSBram Moolenaar    unlet s:breakpoints[id]
12181b9645deSBram Moolenaar  endif
12194551c0a9SBram Moolenaarendfunc
12204551c0a9SBram Moolenaar
12214551c0a9SBram Moolenaar" Handle the debugged program starting to run.
12224551c0a9SBram Moolenaar" Will store the process ID in s:pid
12234551c0a9SBram Moolenaarfunc s:HandleProgramRun(msg)
12244551c0a9SBram Moolenaar  let nr = substitute(a:msg, '.*pid="\([0-9]*\)\".*', '\1', '') + 0
12254551c0a9SBram Moolenaar  if nr == 0
12264551c0a9SBram Moolenaar    return
12274551c0a9SBram Moolenaar  endif
12284551c0a9SBram Moolenaar  let s:pid = nr
12294551c0a9SBram Moolenaar  call ch_log('Detected process ID: ' . s:pid)
12301b9645deSBram Moolenaarendfunc
12311b9645deSBram Moolenaar
12321b9645deSBram Moolenaar" Handle a BufRead autocommand event: place any signs.
123337402ed5SBram Moolenaarfunc s:BufRead()
123437402ed5SBram Moolenaar  let fname = expand('<afile>:p')
12351b9645deSBram Moolenaar  for [id, entries] in items(s:breakpoints)
123637402ed5SBram Moolenaar    for [subid, entry] in items(entries)
12371b9645deSBram Moolenaar      if entry['fname'] == fname
12381b9645deSBram Moolenaar	call s:PlaceSign(id, subid, entry)
123937402ed5SBram Moolenaar      endif
12401b9645deSBram Moolenaar    endfor
12411b9645deSBram Moolenaar  endfor
12421b9645deSBram Moolenaarendfunc
12431b9645deSBram Moolenaar
12441b9645deSBram Moolenaar" Handle a BufUnloaded autocommand event: unplace any signs.
124537402ed5SBram Moolenaarfunc s:BufUnloaded()
124637402ed5SBram Moolenaar  let fname = expand('<afile>:p')
12471b9645deSBram Moolenaar  for [id, entries] in items(s:breakpoints)
12481b9645deSBram Moolenaar    for [subid, entry] in items(entries)
12491b9645deSBram Moolenaar      if entry['fname'] == fname
12501b9645deSBram Moolenaar	let entry['placed'] = 0
125137402ed5SBram Moolenaar      endif
12521b9645deSBram Moolenaar    endfor
1253ca4cc018SBram Moolenaar  endfor
1254ca4cc018SBram Moolenaarendfunc
1255ca4cc018SBram Moolenaar
1256let &cpo = s:keepcpo
1257unlet s:keepcpo
1258