comparison runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @ 15093:8690318105ee v8.1.0557

patch 8.1.0557: Termdebug: gdb may use X.Y for breakpoint number commit https://github.com/vim/vim/commit/5378e1cf0a05121bfa76df2279944ad3b0b5ce4f Author: Bram Moolenaar <Bram@vim.org> Date: Sun Dec 2 13:47:03 2018 +0100 patch 8.1.0557: Termdebug: gdb may use X.Y for breakpoint number Problem: Termdebug: gdb may use X.Y for breakpoint number. Solution: Handle X.Y breakpoint numbers. (Yasuhiro Matsumoto, close https://github.com/vim/vim/issues/3641)
author Bram Moolenaar <Bram@vim.org>
date Sun, 02 Dec 2018 14:00:06 +0100
parents 2f7e67dd088c
children 2090f267b311
comparison
equal deleted inserted replaced
15092:1aa58d876f4f 15093:8690318105ee
71 71
72 let s:pc_id = 12 72 let s:pc_id = 12
73 let s:break_id = 13 " breakpoint number is added to this 73 let s:break_id = 13 " breakpoint number is added to this
74 let s:stopped = 1 74 let s:stopped = 1
75 75
76 " Take a breakpoint number as used by GDB and turn it into an integer.
77 " The breakpoint may contain a dot: 123.4
78 func s:Breakpoint2SignNumber(nr)
79 let t = split(a:nr, '\.')
80 return t[0] * 1000 + (len(t) == 2 ? t[1] : 0)
81 endfunction
82
76 func s:Highlight(init, old, new) 83 func s:Highlight(init, old, new)
77 let default = a:init ? 'default ' : '' 84 let default = a:init ? 'default ' : ''
78 if a:new ==# 'light' && a:old !=# 'light' 85 if a:new ==# 'light' && a:old !=# 'light'
79 exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue" 86 exe "hi " . default . "debugPC term=reverse ctermbg=lightblue guibg=lightblue"
80 elseif a:new ==# 'dark' && a:old !=# 'dark' 87 elseif a:new ==# 'dark' && a:old !=# 'dark'
136 endfunc 143 endfunc
137 144
138 func s:StartDebug_term(dict) 145 func s:StartDebug_term(dict)
139 " Open a terminal window without a job, to run the debugged program in. 146 " Open a terminal window without a job, to run the debugged program in.
140 let s:ptybuf = term_start('NONE', { 147 let s:ptybuf = term_start('NONE', {
141 \ 'term_name': 'debugged program', 148 \ 'term_name': 'debugged program',
142 \ 'vertical': s:vertical, 149 \ 'vertical': s:vertical,
143 \ }) 150 \ })
144 if s:ptybuf == 0 151 if s:ptybuf == 0
145 echoerr 'Failed to open the program terminal window' 152 echoerr 'Failed to open the program terminal window'
146 return 153 return
147 endif 154 endif
148 let pty = job_info(term_getjob(s:ptybuf))['tty_out'] 155 let pty = job_info(term_getjob(s:ptybuf))['tty_out']
153 exe (&columns / 2 - 1) . "wincmd |" 160 exe (&columns / 2 - 1) . "wincmd |"
154 endif 161 endif
155 162
156 " Create a hidden terminal window to communicate with gdb 163 " Create a hidden terminal window to communicate with gdb
157 let s:commbuf = term_start('NONE', { 164 let s:commbuf = term_start('NONE', {
158 \ 'term_name': 'gdb communication', 165 \ 'term_name': 'gdb communication',
159 \ 'out_cb': function('s:CommOutput'), 166 \ 'out_cb': function('s:CommOutput'),
160 \ 'hidden': 1, 167 \ 'hidden': 1,
161 \ }) 168 \ })
162 if s:commbuf == 0 169 if s:commbuf == 0
163 echoerr 'Failed to open the communication terminal window' 170 echoerr 'Failed to open the communication terminal window'
164 exe 'bwipe! ' . s:ptybuf 171 exe 'bwipe! ' . s:ptybuf
165 return 172 return
166 endif 173 endif
172 let proc_args = get(a:dict, 'proc_args', []) 179 let proc_args = get(a:dict, 'proc_args', [])
173 180
174 let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args 181 let cmd = [g:termdebugger, '-quiet', '-tty', pty] + gdb_args
175 call ch_log('executing "' . join(cmd) . '"') 182 call ch_log('executing "' . join(cmd) . '"')
176 let s:gdbbuf = term_start(cmd, { 183 let s:gdbbuf = term_start(cmd, {
177 \ 'exit_cb': function('s:EndTermDebug'), 184 \ 'exit_cb': function('s:EndTermDebug'),
178 \ 'term_finish': 'close', 185 \ 'term_finish': 'close',
179 \ }) 186 \ })
180 if s:gdbbuf == 0 187 if s:gdbbuf == 0
181 echoerr 'Failed to open the gdb terminal window' 188 echoerr 'Failed to open the gdb terminal window'
182 exe 'bwipe! ' . s:ptybuf 189 exe 'bwipe! ' . s:ptybuf
183 exe 'bwipe! ' . s:commbuf 190 exe 'bwipe! ' . s:commbuf
184 return 191 return
198 let try_count = 0 205 let try_count = 0
199 while 1 206 while 1
200 let response = '' 207 let response = ''
201 for lnum in range(1,200) 208 for lnum in range(1,200)
202 if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi ' 209 if term_getline(s:gdbbuf, lnum) =~ 'new-ui mi '
203 " response can be in the same line or the next line 210 " response can be in the same line or the next line
204 let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1) 211 let response = term_getline(s:gdbbuf, lnum) . term_getline(s:gdbbuf, lnum + 1)
205 if response =~ 'Undefined command' 212 if response =~ 'Undefined command'
206 echoerr 'Sorry, your gdb is too old, gdb 7.12 is required' 213 echoerr 'Sorry, your gdb is too old, gdb 7.12 is required'
207 exe 'bwipe! ' . s:ptybuf 214 exe 'bwipe! ' . s:ptybuf
208 exe 'bwipe! ' . s:commbuf 215 exe 'bwipe! ' . s:commbuf
209 return 216 return
210 endif 217 endif
211 if response =~ 'New UI allocated' 218 if response =~ 'New UI allocated'
212 " Success! 219 " Success!
213 break 220 break
214 endif 221 endif
215 endif 222 endif
216 endfor 223 endfor
217 if response =~ 'New UI allocated' 224 if response =~ 'New UI allocated'
218 break 225 break
219 endif 226 endif
266 273
267 let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args 274 let cmd = [g:termdebugger, '-quiet', '--interpreter=mi2'] + gdb_args
268 call ch_log('executing "' . join(cmd) . '"') 275 call ch_log('executing "' . join(cmd) . '"')
269 276
270 let s:gdbjob = job_start(cmd, { 277 let s:gdbjob = job_start(cmd, {
271 \ 'exit_cb': function('s:EndPromptDebug'), 278 \ 'exit_cb': function('s:EndPromptDebug'),
272 \ 'out_cb': function('s:GdbOutCallback'), 279 \ 'out_cb': function('s:GdbOutCallback'),
273 \ }) 280 \ })
274 if job_status(s:gdbjob) != "run" 281 if job_status(s:gdbjob) != "run"
275 echoerr 'Failed to start gdb' 282 echoerr 'Failed to start gdb'
276 exe 'bwipe! ' . s:promptbuf 283 exe 'bwipe! ' . s:promptbuf
277 return 284 return
278 endif 285 endif
293 call s:SendCommand('set new-console on') 300 call s:SendCommand('set new-console on')
294 elseif has('terminal') 301 elseif has('terminal')
295 " Unix: Run the debugged program in a terminal window. Open it below the 302 " Unix: Run the debugged program in a terminal window. Open it below the
296 " gdb window. 303 " gdb window.
297 belowright let s:ptybuf = term_start('NONE', { 304 belowright let s:ptybuf = term_start('NONE', {
298 \ 'term_name': 'debugged program', 305 \ 'term_name': 'debugged program',
299 \ }) 306 \ })
300 if s:ptybuf == 0 307 if s:ptybuf == 0
301 echoerr 'Failed to open the program terminal window' 308 echoerr 'Failed to open the program terminal window'
302 call job_stop(s:gdbjob) 309 call job_stop(s:gdbjob)
303 return 310 return
304 endif 311 endif
351 if has("balloon_eval_term") 358 if has("balloon_eval_term")
352 set balloonevalterm 359 set balloonevalterm
353 endif 360 endif
354 endif 361 endif
355 362
356 " Contains breakpoints that have been placed, key is the number. 363 " Contains breakpoints that have been placed, key is a string with the GDB
364 " breakpoint number.
357 let s:breakpoints = {} 365 let s:breakpoints = {}
358 366
359 augroup TermDebug 367 augroup TermDebug
360 au BufRead * call s:BufRead() 368 au BufRead * call s:BufRead()
361 au BufUnload * call s:BufUnloaded() 369 au BufUnload * call s:BufUnloaded()
464 let i = 1 472 let i = 1
465 while a:quotedText[i] != '"' && i < len(a:quotedText) 473 while a:quotedText[i] != '"' && i < len(a:quotedText)
466 if a:quotedText[i] == '\' 474 if a:quotedText[i] == '\'
467 let i += 1 475 let i += 1
468 if a:quotedText[i] == 'n' 476 if a:quotedText[i] == 'n'
469 " drop \n 477 " drop \n
470 let i += 1 478 let i += 1
471 continue 479 continue
472 endif 480 endif
473 endif 481 endif
474 let result .= a:quotedText[i] 482 let result .= a:quotedText[i]
475 let i += 1 483 let i += 1
476 endwhile 484 endwhile
477 return result 485 return result
478 endfunc 486 endfunc
479 487
480 " Extract the "name" value from a gdb message with fullname="name". 488 " Extract the "name" value from a gdb message with fullname="name".
481 func s:GetFullname(msg) 489 func s:GetFullname(msg)
490 if a:msg !~ 'fullname'
491 return ''
492 endif
482 let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) 493 let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''))
483 if has('win32') && name =~ ':\\\\' 494 if has('win32') && name =~ ':\\\\'
484 " sometimes the name arrives double-escaped 495 " sometimes the name arrives double-escaped
485 let name = substitute(name, '\\\\', '\\', 'g') 496 let name = substitute(name, '\\\\', '\\', 'g')
486 endif 497 endif
547 if msg[0] == "\n" 558 if msg[0] == "\n"
548 let msg = msg[1:] 559 let msg = msg[1:]
549 endif 560 endif
550 if msg != '' 561 if msg != ''
551 if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' 562 if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)'
552 call s:HandleCursor(msg) 563 call s:HandleCursor(msg)
553 elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' 564 elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,'
554 call s:HandleNewBreakpoint(msg) 565 call s:HandleNewBreakpoint(msg)
555 elseif msg =~ '^=breakpoint-deleted,' 566 elseif msg =~ '^=breakpoint-deleted,'
556 call s:HandleBreakpointDelete(msg) 567 call s:HandleBreakpointDelete(msg)
557 elseif msg =~ '^=thread-group-started' 568 elseif msg =~ '^=thread-group-started'
558 call s:HandleProgramRun(msg) 569 call s:HandleProgramRun(msg)
559 elseif msg =~ '^\^done,value=' 570 elseif msg =~ '^\^done,value='
560 call s:HandleEvaluate(msg) 571 call s:HandleEvaluate(msg)
561 elseif msg =~ '^\^error,msg=' 572 elseif msg =~ '^\^error,msg='
562 call s:HandleError(msg) 573 call s:HandleError(msg)
563 endif 574 endif
564 endif 575 endif
565 endfor 576 endfor
566 endfunc 577 endfunc
567 578
648 if has('menu') 659 if has('menu')
649 " Remove the WinBar entries from all windows where it was added. 660 " Remove the WinBar entries from all windows where it was added.
650 let curwinid = win_getid(winnr()) 661 let curwinid = win_getid(winnr())
651 for winid in s:winbar_winids 662 for winid in s:winbar_winids
652 if win_gotoid(winid) 663 if win_gotoid(winid)
653 aunmenu WinBar.Step 664 aunmenu WinBar.Step
654 aunmenu WinBar.Next 665 aunmenu WinBar.Next
655 aunmenu WinBar.Finish 666 aunmenu WinBar.Finish
656 aunmenu WinBar.Cont 667 aunmenu WinBar.Cont
657 aunmenu WinBar.Stop 668 aunmenu WinBar.Stop
658 aunmenu WinBar.Eval 669 aunmenu WinBar.Eval
659 endif 670 endif
660 endfor 671 endfor
661 call win_gotoid(curwinid) 672 call win_gotoid(curwinid)
662 let s:winbar_winids = [] 673 let s:winbar_winids = []
663 674
671 endif 682 endif
672 endif 683 endif
673 684
674 exe 'sign unplace ' . s:pc_id 685 exe 'sign unplace ' . s:pc_id
675 for key in keys(s:breakpoints) 686 for key in keys(s:breakpoints)
676 exe 'sign unplace ' . (s:break_id + key) 687 exe 'sign unplace ' . (s:break_id + s:Breakpoint2SignNumber(key))
677 endfor 688 endfor
678 unlet s:breakpoints 689 unlet s:breakpoints
679 690
680 sign undefine debugPC 691 sign undefine debugPC
681 for val in s:BreakpointSigns 692 for val in s:BreakpointSigns
698 endif 709 endif
699 sleep 10m 710 sleep 10m
700 endif 711 endif
701 " Use the fname:lnum format, older gdb can't handle --source. 712 " Use the fname:lnum format, older gdb can't handle --source.
702 call s:SendCommand('-break-insert ' 713 call s:SendCommand('-break-insert '
703 \ . fnameescape(expand('%:p')) . ':' . line('.')) 714 \ . fnameescape(expand('%:p')) . ':' . line('.'))
704 if do_continue 715 if do_continue
705 call s:SendCommand('-exec-continue') 716 call s:SendCommand('-exec-continue')
706 endif 717 endif
707 endfunc 718 endfunc
708 719
712 let lnum = line('.') 723 let lnum = line('.')
713 for [key, val] in items(s:breakpoints) 724 for [key, val] in items(s:breakpoints)
714 if val['fname'] == fname && val['lnum'] == lnum 725 if val['fname'] == fname && val['lnum'] == lnum
715 call s:SendCommand('-break-delete ' . key) 726 call s:SendCommand('-break-delete ' . key)
716 " Assume this always wors, the reply is simply "^done". 727 " Assume this always wors, the reply is simply "^done".
717 exe 'sign unplace ' . (s:break_id + key) 728 exe 'sign unplace ' . (s:break_id + s:Breakpoint2SignNumber(key))
718 unlet s:breakpoints[key] 729 unlet s:breakpoints[key]
719 break 730 break
720 endif 731 endif
721 endfor 732 endfor
722 endfunc 733 endfunc
837 if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) 848 if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname)
838 let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 849 let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '')
839 if lnum =~ '^[0-9]*$' 850 if lnum =~ '^[0-9]*$'
840 call s:GotoSourcewinOrCreateIt() 851 call s:GotoSourcewinOrCreateIt()
841 if expand('%:p') != fnamemodify(fname, ':p') 852 if expand('%:p') != fnamemodify(fname, ':p')
842 if &modified 853 if &modified
843 " TODO: find existing window 854 " TODO: find existing window
844 exe 'split ' . fnameescape(fname) 855 exe 'split ' . fnameescape(fname)
845 let s:sourcewin = win_getid(winnr()) 856 let s:sourcewin = win_getid(winnr())
846 call s:InstallWinbar() 857 call s:InstallWinbar()
847 else 858 else
848 exe 'edit ' . fnameescape(fname) 859 exe 'edit ' . fnameescape(fname)
849 endif 860 endif
850 endif 861 endif
851 exe lnum 862 exe lnum
852 exe 'sign unplace ' . s:pc_id 863 exe 'sign unplace ' . s:pc_id
853 exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname 864 exe 'sign place ' . s:pc_id . ' line=' . lnum . ' name=debugPC file=' . fname
854 setlocal signcolumn=yes 865 setlocal signcolumn=yes
863 let s:BreakpointSigns = [] 874 let s:BreakpointSigns = []
864 875
865 func s:CreateBreakpoint(nr) 876 func s:CreateBreakpoint(nr)
866 if index(s:BreakpointSigns, a:nr) == -1 877 if index(s:BreakpointSigns, a:nr) == -1
867 call add(s:BreakpointSigns, a:nr) 878 call add(s:BreakpointSigns, a:nr)
868 exe "sign define debugBreakpoint" . a:nr . " text=" . a:nr . " texthl=debugBreakpoint" 879 exe "sign define debugBreakpoint" . a:nr . " text=" . substitute(a:nr, '\..*', '', '') . " texthl=debugBreakpoint"
869 endif 880 endif
870 endfunc 881 endfunc
882
883 func s:SplitMsg(s)
884 return split(a:s, '{\%([a-z-]\+=[^,]\+,*\)\+}\zs')
885 endfunction
871 886
872 " Handle setting a breakpoint 887 " Handle setting a breakpoint
873 " Will update the sign that shows the breakpoint 888 " Will update the sign that shows the breakpoint
874 func s:HandleNewBreakpoint(msg) 889 func s:HandleNewBreakpoint(msg)
875 if a:msg !~ 'fullname=' 890 if a:msg !~ 'fullname='
876 " a watch does not have a file name 891 " a watch does not have a file name
877 return 892 return
878 endif 893 endif
879 894 for msg in s:SplitMsg(a:msg)
880 let nr = substitute(a:msg, '.*number="\([0-9]*\)".*', '\1', '') + 0 895 let fname = s:GetFullname(msg)
881 if nr == 0 896 if empty(fname)
882 return 897 continue
883 endif 898 endif
884 call s:CreateBreakpoint(nr) 899 let nr = substitute(msg, '.*number="\([0-9.]*\)\".*', '\1', '')
885 900 if empty(nr)
886 if has_key(s:breakpoints, nr) 901 return
887 let entry = s:breakpoints[nr] 902 endif
888 else 903 call s:CreateBreakpoint(nr)
889 let entry = {} 904
890 let s:breakpoints[nr] = entry 905 if has_key(s:breakpoints, nr)
891 endif 906 let entry = s:breakpoints[nr]
892 907 else
893 let fname = s:GetFullname(a:msg) 908 let entry = {}
894 let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') 909 let s:breakpoints[nr] = entry
895 let entry['fname'] = fname 910 endif
896 let entry['lnum'] = lnum 911
897 912 let lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '')
898 if bufloaded(fname) 913 let entry['fname'] = fname
899 call s:PlaceSign(nr, entry) 914 let entry['lnum'] = lnum
900 endif 915
916 if bufloaded(fname)
917 call s:PlaceSign(nr, entry)
918 endif
919 endfor
901 endfunc 920 endfunc
902 921
903 func s:PlaceSign(nr, entry) 922 func s:PlaceSign(nr, entry)
904 exe 'sign place ' . (s:break_id + a:nr) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname'] 923 exe 'sign place ' . (s:break_id + s:Breakpoint2SignNumber(a:nr)) . ' line=' . a:entry['lnum'] . ' name=debugBreakpoint' . a:nr . ' file=' . a:entry['fname']
905 let a:entry['placed'] = 1 924 let a:entry['placed'] = 1
906 endfunc 925 endfunc
907 926
908 " Handle deleting a breakpoint 927 " Handle deleting a breakpoint
909 " Will remove the sign that shows the breakpoint 928 " Will remove the sign that shows the breakpoint
910 func s:HandleBreakpointDelete(msg) 929 func s:HandleBreakpointDelete(msg)
911 let nr = substitute(a:msg, '.*id="\([0-9]*\)\".*', '\1', '') + 0 930 let key = substitute(a:msg, '.*id="\([0-9.]*\)\".*', '\1', '')
912 if nr == 0 931 if empty(key)
913 return 932 return
914 endif 933 endif
915 if has_key(s:breakpoints, nr) 934 for [nr, entry] in items(s:breakpoints)
935 if stridx(nr, key) != 0
936 continue
937 endif
916 let entry = s:breakpoints[nr] 938 let entry = s:breakpoints[nr]
917 if has_key(entry, 'placed') 939 if has_key(entry, 'placed')
918 exe 'sign unplace ' . (s:break_id + nr) 940 exe 'sign unplace ' . (s:break_id + s:Breakpoint2SignNumber(nr))
919 unlet entry['placed'] 941 unlet entry['placed']
920 endif 942 endif
921 unlet s:breakpoints[nr] 943 unlet s:breakpoints[nr]
922 endif 944 endfor
923 endfunc 945 endfunc
924 946
925 " Handle the debugged program starting to run. 947 " Handle the debugged program starting to run.
926 " Will store the process ID in s:pid 948 " Will store the process ID in s:pid
927 func s:HandleProgramRun(msg) 949 func s:HandleProgramRun(msg)