Mercurial > vim
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) |