comparison src/ex_cmds2.c @ 16381:1dcbaa780b8e v8.1.1195

patch 8.1.1195: Vim script debugger functionality needs cleanup commit https://github.com/vim/vim/commit/eead75c5e8e1f965548c55ee3a9388b2cb3afc36 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Apr 21 11:35:00 2019 +0200 patch 8.1.1195: Vim script debugger functionality needs cleanup Problem: Vim script debugger functionality needs cleanup. Solution: Move debugger code to a separate file. Add more tests. (Yegappan Lakshmanan, closes #4285)
author Bram Moolenaar <Bram@vim.org>
date Sun, 21 Apr 2019 11:45:07 +0200
parents 6ee80f3b5ea9
children 6e87a69b8e0c
comparison
equal deleted inserted replaced
16380:5ad204d8a2f2 16381:1dcbaa780b8e
64 # define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) 64 # define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
65 # endif 65 # endif
66 #endif 66 #endif
67 67
68 #if defined(FEAT_EVAL) || defined(PROTO) 68 #if defined(FEAT_EVAL) || defined(PROTO)
69 static int debug_greedy = FALSE; /* batch mode debugging: don't save
70 and restore typeahead. */
71 static void do_setdebugtracelevel(char_u *arg);
72 static void do_checkbacktracelevel(void);
73 static void do_showbacktrace(char_u *cmd);
74
75 static char_u *debug_oldval = NULL; /* old and newval for debug expressions */
76 static char_u *debug_newval = NULL;
77 static int debug_expr = 0; /* use debug_expr */
78
79 int
80 has_watchexpr(void)
81 {
82 return debug_expr;
83 }
84
85 /*
86 * do_debug(): Debug mode.
87 * Repeatedly get Ex commands, until told to continue normal execution.
88 */
89 void
90 do_debug(char_u *cmd)
91 {
92 int save_msg_scroll = msg_scroll;
93 int save_State = State;
94 int save_did_emsg = did_emsg;
95 int save_cmd_silent = cmd_silent;
96 int save_msg_silent = msg_silent;
97 int save_emsg_silent = emsg_silent;
98 int save_redir_off = redir_off;
99 tasave_T typeaheadbuf;
100 int typeahead_saved = FALSE;
101 int save_ignore_script = 0;
102 int save_ex_normal_busy;
103 int n;
104 char_u *cmdline = NULL;
105 char_u *p;
106 char *tail = NULL;
107 static int last_cmd = 0;
108 #define CMD_CONT 1
109 #define CMD_NEXT 2
110 #define CMD_STEP 3
111 #define CMD_FINISH 4
112 #define CMD_QUIT 5
113 #define CMD_INTERRUPT 6
114 #define CMD_BACKTRACE 7
115 #define CMD_FRAME 8
116 #define CMD_UP 9
117 #define CMD_DOWN 10
118
119 #ifdef ALWAYS_USE_GUI
120 /* Can't do this when there is no terminal for input/output. */
121 if (!gui.in_use)
122 {
123 /* Break as soon as possible. */
124 debug_break_level = 9999;
125 return;
126 }
127 #endif
128
129 /* Make sure we are in raw mode and start termcap mode. Might have side
130 * effects... */
131 settmode(TMODE_RAW);
132 starttermcap();
133
134 ++RedrawingDisabled; /* don't redisplay the window */
135 ++no_wait_return; /* don't wait for return */
136 did_emsg = FALSE; /* don't use error from debugged stuff */
137 cmd_silent = FALSE; /* display commands */
138 msg_silent = FALSE; /* display messages */
139 emsg_silent = FALSE; /* display error messages */
140 redir_off = TRUE; /* don't redirect debug commands */
141
142 State = NORMAL;
143 debug_mode = TRUE;
144
145 if (!debug_did_msg)
146 msg(_("Entering Debug mode. Type \"cont\" to continue."));
147 if (debug_oldval != NULL)
148 {
149 smsg(_("Oldval = \"%s\""), debug_oldval);
150 vim_free(debug_oldval);
151 debug_oldval = NULL;
152 }
153 if (debug_newval != NULL)
154 {
155 smsg(_("Newval = \"%s\""), debug_newval);
156 vim_free(debug_newval);
157 debug_newval = NULL;
158 }
159 if (sourcing_name != NULL)
160 msg((char *)sourcing_name);
161 if (sourcing_lnum != 0)
162 smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd);
163 else
164 smsg(_("cmd: %s"), cmd);
165 /*
166 * Repeat getting a command and executing it.
167 */
168 for (;;)
169 {
170 msg_scroll = TRUE;
171 need_wait_return = FALSE;
172
173 /* Save the current typeahead buffer and replace it with an empty one.
174 * This makes sure we get input from the user here and don't interfere
175 * with the commands being executed. Reset "ex_normal_busy" to avoid
176 * the side effects of using ":normal". Save the stuff buffer and make
177 * it empty. Set ignore_script to avoid reading from script input. */
178 save_ex_normal_busy = ex_normal_busy;
179 ex_normal_busy = 0;
180 if (!debug_greedy)
181 {
182 save_typeahead(&typeaheadbuf);
183 typeahead_saved = TRUE;
184 save_ignore_script = ignore_script;
185 ignore_script = TRUE;
186 }
187
188 vim_free(cmdline);
189 cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL);
190
191 if (typeahead_saved)
192 {
193 restore_typeahead(&typeaheadbuf);
194 ignore_script = save_ignore_script;
195 }
196 ex_normal_busy = save_ex_normal_busy;
197
198 cmdline_row = msg_row;
199 msg_starthere();
200 if (cmdline != NULL)
201 {
202 /* If this is a debug command, set "last_cmd".
203 * If not, reset "last_cmd".
204 * For a blank line use previous command. */
205 p = skipwhite(cmdline);
206 if (*p != NUL)
207 {
208 switch (*p)
209 {
210 case 'c': last_cmd = CMD_CONT;
211 tail = "ont";
212 break;
213 case 'n': last_cmd = CMD_NEXT;
214 tail = "ext";
215 break;
216 case 's': last_cmd = CMD_STEP;
217 tail = "tep";
218 break;
219 case 'f':
220 last_cmd = 0;
221 if (p[1] == 'r')
222 {
223 last_cmd = CMD_FRAME;
224 tail = "rame";
225 }
226 else
227 {
228 last_cmd = CMD_FINISH;
229 tail = "inish";
230 }
231 break;
232 case 'q': last_cmd = CMD_QUIT;
233 tail = "uit";
234 break;
235 case 'i': last_cmd = CMD_INTERRUPT;
236 tail = "nterrupt";
237 break;
238 case 'b': last_cmd = CMD_BACKTRACE;
239 if (p[1] == 't')
240 tail = "t";
241 else
242 tail = "acktrace";
243 break;
244 case 'w': last_cmd = CMD_BACKTRACE;
245 tail = "here";
246 break;
247 case 'u': last_cmd = CMD_UP;
248 tail = "p";
249 break;
250 case 'd': last_cmd = CMD_DOWN;
251 tail = "own";
252 break;
253 default: last_cmd = 0;
254 }
255 if (last_cmd != 0)
256 {
257 /* Check that the tail matches. */
258 ++p;
259 while (*p != NUL && *p == *tail)
260 {
261 ++p;
262 ++tail;
263 }
264 if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME)
265 last_cmd = 0;
266 }
267 }
268
269 if (last_cmd != 0)
270 {
271 /* Execute debug command: decided where to break next and
272 * return. */
273 switch (last_cmd)
274 {
275 case CMD_CONT:
276 debug_break_level = -1;
277 break;
278 case CMD_NEXT:
279 debug_break_level = ex_nesting_level;
280 break;
281 case CMD_STEP:
282 debug_break_level = 9999;
283 break;
284 case CMD_FINISH:
285 debug_break_level = ex_nesting_level - 1;
286 break;
287 case CMD_QUIT:
288 got_int = TRUE;
289 debug_break_level = -1;
290 break;
291 case CMD_INTERRUPT:
292 got_int = TRUE;
293 debug_break_level = 9999;
294 /* Do not repeat ">interrupt" cmd, continue stepping. */
295 last_cmd = CMD_STEP;
296 break;
297 case CMD_BACKTRACE:
298 do_showbacktrace(cmd);
299 continue;
300 case CMD_FRAME:
301 if (*p == NUL)
302 {
303 do_showbacktrace(cmd);
304 }
305 else
306 {
307 p = skipwhite(p);
308 do_setdebugtracelevel(p);
309 }
310 continue;
311 case CMD_UP:
312 debug_backtrace_level++;
313 do_checkbacktracelevel();
314 continue;
315 case CMD_DOWN:
316 debug_backtrace_level--;
317 do_checkbacktracelevel();
318 continue;
319 }
320 /* Going out reset backtrace_level */
321 debug_backtrace_level = 0;
322 break;
323 }
324
325 /* don't debug this command */
326 n = debug_break_level;
327 debug_break_level = -1;
328 (void)do_cmdline(cmdline, getexline, NULL,
329 DOCMD_VERBOSE|DOCMD_EXCRESET);
330 debug_break_level = n;
331 }
332 lines_left = Rows - 1;
333 }
334 vim_free(cmdline);
335
336 --RedrawingDisabled;
337 --no_wait_return;
338 redraw_all_later(NOT_VALID);
339 need_wait_return = FALSE;
340 msg_scroll = save_msg_scroll;
341 lines_left = Rows - 1;
342 State = save_State;
343 debug_mode = FALSE;
344 did_emsg = save_did_emsg;
345 cmd_silent = save_cmd_silent;
346 msg_silent = save_msg_silent;
347 emsg_silent = save_emsg_silent;
348 redir_off = save_redir_off;
349
350 /* Only print the message again when typing a command before coming back
351 * here. */
352 debug_did_msg = TRUE;
353 }
354
355 static int
356 get_maxbacktrace_level(void)
357 {
358 char *p, *q;
359 int maxbacktrace = 0;
360
361 if (sourcing_name != NULL)
362 {
363 p = (char *)sourcing_name;
364 while ((q = strstr(p, "..")) != NULL)
365 {
366 p = q + 2;
367 maxbacktrace++;
368 }
369 }
370 return maxbacktrace;
371 }
372
373 static void
374 do_setdebugtracelevel(char_u *arg)
375 {
376 int level;
377
378 level = atoi((char *)arg);
379 if (*arg == '+' || level < 0)
380 debug_backtrace_level += level;
381 else
382 debug_backtrace_level = level;
383
384 do_checkbacktracelevel();
385 }
386
387 static void
388 do_checkbacktracelevel(void)
389 {
390 if (debug_backtrace_level < 0)
391 {
392 debug_backtrace_level = 0;
393 msg(_("frame is zero"));
394 }
395 else
396 {
397 int max = get_maxbacktrace_level();
398
399 if (debug_backtrace_level > max)
400 {
401 debug_backtrace_level = max;
402 smsg(_("frame at highest level: %d"), max);
403 }
404 }
405 }
406
407 static void
408 do_showbacktrace(char_u *cmd)
409 {
410 char *cur;
411 char *next;
412 int i = 0;
413 int max = get_maxbacktrace_level();
414
415 if (sourcing_name != NULL)
416 {
417 cur = (char *)sourcing_name;
418 while (!got_int)
419 {
420 next = strstr(cur, "..");
421 if (next != NULL)
422 *next = NUL;
423 if (i == max - debug_backtrace_level)
424 smsg("->%d %s", max - i, cur);
425 else
426 smsg(" %d %s", max - i, cur);
427 ++i;
428 if (next == NULL)
429 break;
430 *next = '.';
431 cur = next + 2;
432 }
433 }
434 if (sourcing_lnum != 0)
435 smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd);
436 else
437 smsg(_("cmd: %s"), cmd);
438 }
439
440 /*
441 * ":debug".
442 */
443 void
444 ex_debug(exarg_T *eap)
445 {
446 int debug_break_level_save = debug_break_level;
447
448 debug_break_level = 9999;
449 do_cmdline_cmd(eap->arg);
450 debug_break_level = debug_break_level_save;
451 }
452
453 static char_u *debug_breakpoint_name = NULL;
454 static linenr_T debug_breakpoint_lnum;
455
456 /*
457 * When debugging or a breakpoint is set on a skipped command, no debug prompt
458 * is shown by do_one_cmd(). This situation is indicated by debug_skipped, and
459 * debug_skipped_name is then set to the source name in the breakpoint case. If
460 * a skipped command decides itself that a debug prompt should be displayed, it
461 * can do so by calling dbg_check_skipped().
462 */
463 static int debug_skipped;
464 static char_u *debug_skipped_name;
465
466 /*
467 * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is
468 * at or below the break level. But only when the line is actually
469 * executed. Return TRUE and set breakpoint_name for skipped commands that
470 * decide to execute something themselves.
471 * Called from do_one_cmd() before executing a command.
472 */
473 void
474 dbg_check_breakpoint(exarg_T *eap)
475 {
476 char_u *p;
477
478 debug_skipped = FALSE;
479 if (debug_breakpoint_name != NULL)
480 {
481 if (!eap->skip)
482 {
483 /* replace K_SNR with "<SNR>" */
484 if (debug_breakpoint_name[0] == K_SPECIAL
485 && debug_breakpoint_name[1] == KS_EXTRA
486 && debug_breakpoint_name[2] == (int)KE_SNR)
487 p = (char_u *)"<SNR>";
488 else
489 p = (char_u *)"";
490 smsg(_("Breakpoint in \"%s%s\" line %ld"),
491 p,
492 debug_breakpoint_name + (*p == NUL ? 0 : 3),
493 (long)debug_breakpoint_lnum);
494 debug_breakpoint_name = NULL;
495 do_debug(eap->cmd);
496 }
497 else
498 {
499 debug_skipped = TRUE;
500 debug_skipped_name = debug_breakpoint_name;
501 debug_breakpoint_name = NULL;
502 }
503 }
504 else if (ex_nesting_level <= debug_break_level)
505 {
506 if (!eap->skip)
507 do_debug(eap->cmd);
508 else
509 {
510 debug_skipped = TRUE;
511 debug_skipped_name = NULL;
512 }
513 }
514 }
515
516 /*
517 * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was
518 * set. Return TRUE when the debug mode is entered this time.
519 */
520 int
521 dbg_check_skipped(exarg_T *eap)
522 {
523 int prev_got_int;
524
525 if (debug_skipped)
526 {
527 /*
528 * Save the value of got_int and reset it. We don't want a previous
529 * interruption cause flushing the input buffer.
530 */
531 prev_got_int = got_int;
532 got_int = FALSE;
533 debug_breakpoint_name = debug_skipped_name;
534 /* eap->skip is TRUE */
535 eap->skip = FALSE;
536 (void)dbg_check_breakpoint(eap);
537 eap->skip = TRUE;
538 got_int |= prev_got_int;
539 return TRUE;
540 }
541 return FALSE;
542 }
543
544 /*
545 * The list of breakpoints: dbg_breakp.
546 * This is a grow-array of structs.
547 */
548 struct debuggy
549 {
550 int dbg_nr; /* breakpoint number */
551 int dbg_type; /* DBG_FUNC, DBG_FILE or DBG_EXPR */
552 char_u *dbg_name; /* function, expression or file name */
553 regprog_T *dbg_prog; /* regexp program */
554 linenr_T dbg_lnum; /* line number in function or file */
555 int dbg_forceit; /* ! used */
556 #ifdef FEAT_EVAL
557 typval_T *dbg_val; /* last result of watchexpression */
558 #endif
559 int dbg_level; /* stored nested level for expr */
560 };
561
562 static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL};
563 #define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx])
564 #define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx])
565 static int last_breakp = 0; /* nr of last defined breakpoint */
566
567 #ifdef FEAT_PROFILE
568 /* Profiling uses file and func names similar to breakpoints. */
569 static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL};
570 #endif
571 #define DBG_FUNC 1
572 #define DBG_FILE 2
573 #define DBG_EXPR 3
574
575 static linenr_T debuggy_find(int file,char_u *fname, linenr_T after, garray_T *gap, int *fp);
576
577 /*
578 * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
579 * in the entry just after the last one in dbg_breakp. Note that "dbg_name"
580 * is allocated.
581 * Returns FAIL for failure.
582 */
583 static int
584 dbg_parsearg(
585 char_u *arg,
586 garray_T *gap) /* either &dbg_breakp or &prof_ga */
587 {
588 char_u *p = arg;
589 char_u *q;
590 struct debuggy *bp;
591 int here = FALSE;
592
593 if (ga_grow(gap, 1) == FAIL)
594 return FAIL;
595 bp = &DEBUGGY(gap, gap->ga_len);
596
597 /* Find "func" or "file". */
598 if (STRNCMP(p, "func", 4) == 0)
599 bp->dbg_type = DBG_FUNC;
600 else if (STRNCMP(p, "file", 4) == 0)
601 bp->dbg_type = DBG_FILE;
602 else if (
603 #ifdef FEAT_PROFILE
604 gap != &prof_ga &&
605 #endif
606 STRNCMP(p, "here", 4) == 0)
607 {
608 if (curbuf->b_ffname == NULL)
609 {
610 emsg(_(e_noname));
611 return FAIL;
612 }
613 bp->dbg_type = DBG_FILE;
614 here = TRUE;
615 }
616 else if (
617 #ifdef FEAT_PROFILE
618 gap != &prof_ga &&
619 #endif
620 STRNCMP(p, "expr", 4) == 0)
621 bp->dbg_type = DBG_EXPR;
622 else
623 {
624 semsg(_(e_invarg2), p);
625 return FAIL;
626 }
627 p = skipwhite(p + 4);
628
629 /* Find optional line number. */
630 if (here)
631 bp->dbg_lnum = curwin->w_cursor.lnum;
632 else if (
633 #ifdef FEAT_PROFILE
634 gap != &prof_ga &&
635 #endif
636 VIM_ISDIGIT(*p))
637 {
638 bp->dbg_lnum = getdigits(&p);
639 p = skipwhite(p);
640 }
641 else
642 bp->dbg_lnum = 0;
643
644 /* Find the function or file name. Don't accept a function name with (). */
645 if ((!here && *p == NUL)
646 || (here && *p != NUL)
647 || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL))
648 {
649 semsg(_(e_invarg2), arg);
650 return FAIL;
651 }
652
653 if (bp->dbg_type == DBG_FUNC)
654 bp->dbg_name = vim_strsave(p);
655 else if (here)
656 bp->dbg_name = vim_strsave(curbuf->b_ffname);
657 else if (bp->dbg_type == DBG_EXPR)
658 {
659 bp->dbg_name = vim_strsave(p);
660 if (bp->dbg_name != NULL)
661 bp->dbg_val = eval_expr(bp->dbg_name, NULL);
662 }
663 else
664 {
665 /* Expand the file name in the same way as do_source(). This means
666 * doing it twice, so that $DIR/file gets expanded when $DIR is
667 * "~/dir". */
668 q = expand_env_save(p);
669 if (q == NULL)
670 return FAIL;
671 p = expand_env_save(q);
672 vim_free(q);
673 if (p == NULL)
674 return FAIL;
675 if (*p != '*')
676 {
677 bp->dbg_name = fix_fname(p);
678 vim_free(p);
679 }
680 else
681 bp->dbg_name = p;
682 }
683
684 if (bp->dbg_name == NULL)
685 return FAIL;
686 return OK;
687 }
688
689 /*
690 * ":breakadd". Also used for ":profile".
691 */
692 void
693 ex_breakadd(exarg_T *eap)
694 {
695 struct debuggy *bp;
696 char_u *pat;
697 garray_T *gap;
698
699 gap = &dbg_breakp;
700 #ifdef FEAT_PROFILE
701 if (eap->cmdidx == CMD_profile)
702 gap = &prof_ga;
703 #endif
704
705 if (dbg_parsearg(eap->arg, gap) == OK)
706 {
707 bp = &DEBUGGY(gap, gap->ga_len);
708 bp->dbg_forceit = eap->forceit;
709
710 if (bp->dbg_type != DBG_EXPR)
711 {
712 pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE);
713 if (pat != NULL)
714 {
715 bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
716 vim_free(pat);
717 }
718 if (pat == NULL || bp->dbg_prog == NULL)
719 vim_free(bp->dbg_name);
720 else
721 {
722 if (bp->dbg_lnum == 0) /* default line number is 1 */
723 bp->dbg_lnum = 1;
724 #ifdef FEAT_PROFILE
725 if (eap->cmdidx != CMD_profile)
726 #endif
727 {
728 DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
729 ++debug_tick;
730 }
731 ++gap->ga_len;
732 }
733 }
734 else
735 {
736 /* DBG_EXPR */
737 DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
738 ++debug_tick;
739 }
740 }
741 }
742
743 /*
744 * ":debuggreedy".
745 */
746 void
747 ex_debuggreedy(exarg_T *eap)
748 {
749 if (eap->addr_count == 0 || eap->line2 != 0)
750 debug_greedy = TRUE;
751 else
752 debug_greedy = FALSE;
753 }
754
755 /*
756 * ":breakdel" and ":profdel".
757 */
758 void
759 ex_breakdel(exarg_T *eap)
760 {
761 struct debuggy *bp, *bpi;
762 int nr;
763 int todel = -1;
764 int del_all = FALSE;
765 int i;
766 linenr_T best_lnum = 0;
767 garray_T *gap;
768
769 gap = &dbg_breakp;
770 if (eap->cmdidx == CMD_profdel)
771 {
772 #ifdef FEAT_PROFILE
773 gap = &prof_ga;
774 #else
775 ex_ni(eap);
776 return;
777 #endif
778 }
779
780 if (vim_isdigit(*eap->arg))
781 {
782 /* ":breakdel {nr}" */
783 nr = atol((char *)eap->arg);
784 for (i = 0; i < gap->ga_len; ++i)
785 if (DEBUGGY(gap, i).dbg_nr == nr)
786 {
787 todel = i;
788 break;
789 }
790 }
791 else if (*eap->arg == '*')
792 {
793 todel = 0;
794 del_all = TRUE;
795 }
796 else
797 {
798 /* ":breakdel {func|file|expr} [lnum] {name}" */
799 if (dbg_parsearg(eap->arg, gap) == FAIL)
800 return;
801 bp = &DEBUGGY(gap, gap->ga_len);
802 for (i = 0; i < gap->ga_len; ++i)
803 {
804 bpi = &DEBUGGY(gap, i);
805 if (bp->dbg_type == bpi->dbg_type
806 && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
807 && (bp->dbg_lnum == bpi->dbg_lnum
808 || (bp->dbg_lnum == 0
809 && (best_lnum == 0
810 || bpi->dbg_lnum < best_lnum))))
811 {
812 todel = i;
813 best_lnum = bpi->dbg_lnum;
814 }
815 }
816 vim_free(bp->dbg_name);
817 }
818
819 if (todel < 0)
820 semsg(_("E161: Breakpoint not found: %s"), eap->arg);
821 else
822 {
823 while (gap->ga_len > 0)
824 {
825 vim_free(DEBUGGY(gap, todel).dbg_name);
826 #ifdef FEAT_EVAL
827 if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
828 && DEBUGGY(gap, todel).dbg_val != NULL)
829 free_tv(DEBUGGY(gap, todel).dbg_val);
830 #endif
831 vim_regfree(DEBUGGY(gap, todel).dbg_prog);
832 --gap->ga_len;
833 if (todel < gap->ga_len)
834 mch_memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
835 (gap->ga_len - todel) * sizeof(struct debuggy));
836 #ifdef FEAT_PROFILE
837 if (eap->cmdidx == CMD_breakdel)
838 #endif
839 ++debug_tick;
840 if (!del_all)
841 break;
842 }
843
844 /* If all breakpoints were removed clear the array. */
845 if (gap->ga_len == 0)
846 ga_clear(gap);
847 }
848 }
849
850 /*
851 * ":breaklist".
852 */
853 void
854 ex_breaklist(exarg_T *eap UNUSED)
855 {
856 struct debuggy *bp;
857 int i;
858
859 if (dbg_breakp.ga_len == 0)
860 msg(_("No breakpoints defined"));
861 else
862 for (i = 0; i < dbg_breakp.ga_len; ++i)
863 {
864 bp = &BREAKP(i);
865 if (bp->dbg_type == DBG_FILE)
866 home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, TRUE);
867 if (bp->dbg_type != DBG_EXPR)
868 smsg(_("%3d %s %s line %ld"),
869 bp->dbg_nr,
870 bp->dbg_type == DBG_FUNC ? "func" : "file",
871 bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
872 (long)bp->dbg_lnum);
873 else
874 smsg(_("%3d expr %s"),
875 bp->dbg_nr, bp->dbg_name);
876 }
877 }
878
879 /*
880 * Find a breakpoint for a function or sourced file.
881 * Returns line number at which to break; zero when no matching breakpoint.
882 */
883 linenr_T
884 dbg_find_breakpoint(
885 int file, /* TRUE for a file, FALSE for a function */
886 char_u *fname, /* file or function name */
887 linenr_T after) /* after this line number */
888 {
889 return debuggy_find(file, fname, after, &dbg_breakp, NULL);
890 }
891
892 #if defined(FEAT_PROFILE) || defined(PROTO)
893 /*
894 * Return TRUE if profiling is on for a function or sourced file.
895 */
896 int
897 has_profiling(
898 int file, /* TRUE for a file, FALSE for a function */
899 char_u *fname, /* file or function name */
900 int *fp) /* return: forceit */
901 {
902 return (debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
903 != (linenr_T)0);
904 }
905 #endif
906
907 /*
908 * Common code for dbg_find_breakpoint() and has_profiling().
909 */
910 static linenr_T
911 debuggy_find(
912 int file, /* TRUE for a file, FALSE for a function */
913 char_u *fname, /* file or function name */
914 linenr_T after, /* after this line number */
915 garray_T *gap, /* either &dbg_breakp or &prof_ga */
916 int *fp) /* if not NULL: return forceit */
917 {
918 struct debuggy *bp;
919 int i;
920 linenr_T lnum = 0;
921 char_u *name = fname;
922 int prev_got_int;
923
924 /* Return quickly when there are no breakpoints. */
925 if (gap->ga_len == 0)
926 return (linenr_T)0;
927
928 /* Replace K_SNR in function name with "<SNR>". */
929 if (!file && fname[0] == K_SPECIAL)
930 {
931 name = alloc((unsigned)STRLEN(fname) + 3);
932 if (name == NULL)
933 name = fname;
934 else
935 {
936 STRCPY(name, "<SNR>");
937 STRCPY(name + 5, fname + 3);
938 }
939 }
940
941 for (i = 0; i < gap->ga_len; ++i)
942 {
943 /* Skip entries that are not useful or are for a line that is beyond
944 * an already found breakpoint. */
945 bp = &DEBUGGY(gap, i);
946 if (((bp->dbg_type == DBG_FILE) == file &&
947 bp->dbg_type != DBG_EXPR && (
948 #ifdef FEAT_PROFILE
949 gap == &prof_ga ||
950 #endif
951 (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))))
952 {
953 /*
954 * Save the value of got_int and reset it. We don't want a
955 * previous interruption cancel matching, only hitting CTRL-C
956 * while matching should abort it.
957 */
958 prev_got_int = got_int;
959 got_int = FALSE;
960 if (vim_regexec_prog(&bp->dbg_prog, FALSE, name, (colnr_T)0))
961 {
962 lnum = bp->dbg_lnum;
963 if (fp != NULL)
964 *fp = bp->dbg_forceit;
965 }
966 got_int |= prev_got_int;
967 }
968 #ifdef FEAT_EVAL
969 else if (bp->dbg_type == DBG_EXPR)
970 {
971 typval_T *tv;
972 int line = FALSE;
973
974 prev_got_int = got_int;
975 got_int = FALSE;
976
977 tv = eval_expr(bp->dbg_name, NULL);
978 if (tv != NULL)
979 {
980 if (bp->dbg_val == NULL)
981 {
982 debug_oldval = typval_tostring(NULL);
983 bp->dbg_val = tv;
984 debug_newval = typval_tostring(bp->dbg_val);
985 line = TRUE;
986 }
987 else
988 {
989 if (typval_compare(tv, bp->dbg_val, TYPE_EQUAL,
990 TRUE, FALSE) == OK
991 && tv->vval.v_number == FALSE)
992 {
993 typval_T *v;
994
995 line = TRUE;
996 debug_oldval = typval_tostring(bp->dbg_val);
997 /* Need to evaluate again, typval_compare() overwrites
998 * "tv". */
999 v = eval_expr(bp->dbg_name, NULL);
1000 debug_newval = typval_tostring(v);
1001 free_tv(bp->dbg_val);
1002 bp->dbg_val = v;
1003 }
1004 free_tv(tv);
1005 }
1006 }
1007 else if (bp->dbg_val != NULL)
1008 {
1009 debug_oldval = typval_tostring(bp->dbg_val);
1010 debug_newval = typval_tostring(NULL);
1011 free_tv(bp->dbg_val);
1012 bp->dbg_val = NULL;
1013 line = TRUE;
1014 }
1015
1016 if (line)
1017 {
1018 lnum = after > 0 ? after : 1;
1019 break;
1020 }
1021
1022 got_int |= prev_got_int;
1023 }
1024 #endif
1025 }
1026 if (name != fname)
1027 vim_free(name);
1028
1029 return lnum;
1030 }
1031
1032 /*
1033 * Called when a breakpoint was encountered.
1034 */
1035 void
1036 dbg_breakpoint(char_u *name, linenr_T lnum)
1037 {
1038 /* We need to check if this line is actually executed in do_one_cmd() */
1039 debug_breakpoint_name = name;
1040 debug_breakpoint_lnum = lnum;
1041 }
1042
1043
1044 # if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO) 69 # if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO)
1045 /* 70 /*
1046 * Store the current time in "tm". 71 * Store the current time in "tm".
1047 */ 72 */
1048 void 73 void