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