diff options
author | Bram Moolenaar <Bram@vim.org> | 2019-04-21 11:35:00 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2019-04-21 11:35:00 +0200 |
commit | eead75c5e8e1f965548c55ee3a9388b2cb3afc36 (patch) | |
tree | b3c47222504286009afa1b615edf637954d91fb7 /src | |
parent | 911ead126903aeb9564bad89e46a147ed4959896 (diff) |
patch 8.1.1195: Vim script debugger functionality needs cleanupv8.1.1195
Problem: Vim script debugger functionality needs cleanup.
Solution: Move debugger code to a separate file. Add more tests. (Yegappan
Lakshmanan, closes #4285)
Diffstat (limited to 'src')
-rw-r--r-- | src/Make_bc5.mak | 1 | ||||
-rw-r--r-- | src/Make_cyg_ming.mak | 1 | ||||
-rw-r--r-- | src/Make_dice.mak | 4 | ||||
-rw-r--r-- | src/Make_ivc.mak | 5 | ||||
-rw-r--r-- | src/Make_manx.mak | 6 | ||||
-rw-r--r-- | src/Make_morph.mak | 1 | ||||
-rw-r--r-- | src/Make_mvc.mak | 4 | ||||
-rw-r--r-- | src/Make_sas.mak | 5 | ||||
-rw-r--r-- | src/Make_vms.mms | 32 | ||||
-rw-r--r-- | src/Makefile | 9 | ||||
-rw-r--r-- | src/debugger.c | 990 | ||||
-rw-r--r-- | src/ex_cmds2.c | 975 | ||||
-rw-r--r-- | src/proto.h | 1 | ||||
-rw-r--r-- | src/proto/debugger.pro | 14 | ||||
-rw-r--r-- | src/proto/ex_cmds2.pro | 12 | ||||
-rw-r--r-- | src/version.c | 2 |
16 files changed, 1061 insertions, 1001 deletions
diff --git a/src/Make_bc5.mak b/src/Make_bc5.mak index 1c13931d31..62d03972e8 100644 --- a/src/Make_bc5.mak +++ b/src/Make_bc5.mak @@ -516,6 +516,7 @@ vimobj = \ $(OBJDIR)\charset.obj \ $(OBJDIR)\crypt.obj \ $(OBJDIR)\crypt_zip.obj \ + $(OBJDIR)\debugger.obj \ $(OBJDIR)\dict.obj \ $(OBJDIR)\diff.obj \ $(OBJDIR)\digraph.obj \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index bfc5545536..3c01e63fe0 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -703,6 +703,7 @@ OBJ = \ $(OUTDIR)/charset.o \ $(OUTDIR)/crypt.o \ $(OUTDIR)/crypt_zip.o \ + $(OUTDIR)/debugger.o \ $(OUTDIR)/dict.o \ $(OUTDIR)/diff.o \ $(OUTDIR)/digraph.o \ diff --git a/src/Make_dice.mak b/src/Make_dice.mak index 5bad80b850..93e960a8db 100644 --- a/src/Make_dice.mak +++ b/src/Make_dice.mak @@ -33,6 +33,7 @@ SRC = \ charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ @@ -93,6 +94,7 @@ OBJ = o/arabic.o \ o/charset.o \ o/crypt.o \ o/crypt_zip.o \ + o/debugger.o \ o/dict.o \ o/diff.o \ o/digraph.o \ @@ -179,6 +181,8 @@ o/crypt.o: crypt.c $(SYMS) o/crypt_zip.o: crypt_zip.c $(SYMS) +o/debugger.o: debugger.c $(SYMS) + o/dict.o: dict.c $(SYMS) o/diff.o: diff.c $(SYMS) diff --git a/src/Make_ivc.mak b/src/Make_ivc.mak index 27b2c336d7..08bd87451f 100644 --- a/src/Make_ivc.mak +++ b/src/Make_ivc.mak @@ -217,6 +217,7 @@ LINK32_OBJS= \ "$(INTDIR)/charset.obj" \ "$(INTDIR)/crypt.obj" \ "$(INTDIR)/crypt_zip.obj" \ + "$(INTDIR)/debugger.obj" \ "$(INTDIR)/dict.obj" \ "$(INTDIR)/diff.obj" \ "$(INTDIR)/digraph.obj" \ @@ -369,6 +370,10 @@ SOURCE=.\crypt_zip.c # End Source File # Begin Source File +SOURCE=.\debugger.c +# End Source File +# Begin Source File + SOURCE=.\dict.c # End Source File # Begin Source File diff --git a/src/Make_manx.mak b/src/Make_manx.mak index d150e4b6fc..6eb7bfa684 100644 --- a/src/Make_manx.mak +++ b/src/Make_manx.mak @@ -43,6 +43,7 @@ SRC = arabic.c \ charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ @@ -105,6 +106,7 @@ OBJ = obj/arabic.o \ obj/charset.o \ obj/crypt.o \ obj/crypt_zip.o \ + obj/debugger.o \ obj/dict.o \ obj/diff.o \ obj/digraph.o \ @@ -165,6 +167,7 @@ PRO = proto/arabic.pro \ proto/charset.pro \ proto/crypt.pro \ proto/crypt_zip.pro \ + proto/debugger.pro \ proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ @@ -283,6 +286,9 @@ obj/crypt.o: crypt.c obj/crypt_zip.o: crypt_zip.c $(CCSYM) $@ crypt_zip.c +obj/debugger.o: debugger.c + $(CCSYM) $@ debugger.c + obj/dict.o: dict.c $(CCSYM) $@ dict.c diff --git a/src/Make_morph.mak b/src/Make_morph.mak index b224c67adc..65cf8447be 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -31,6 +31,7 @@ SRC = arabic.c \ charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index b6a1ac0d1c..7da56bc2d8 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -711,6 +711,7 @@ OBJ = \ $(OUTDIR)\charset.obj \ $(OUTDIR)\crypt.obj \ $(OUTDIR)\crypt_zip.obj \ + $(OUTDIR)\debugger.obj \ $(OUTDIR)\dict.obj \ $(OUTDIR)\diff.obj \ $(OUTDIR)\digraph.obj \ @@ -1367,6 +1368,8 @@ $(OUTDIR)/crypt.obj: $(OUTDIR) crypt.c $(INCL) $(OUTDIR)/crypt_zip.obj: $(OUTDIR) crypt_zip.c $(INCL) +$(OUTDIR)/debugger.obj: $(OUTDIR) debugger.c $(INCL) + $(OUTDIR)/dict.obj: $(OUTDIR) dict.c $(INCL) $(OUTDIR)/diff.obj: $(OUTDIR) diff.c $(INCL) @@ -1638,6 +1641,7 @@ proto.h: \ proto/charset.pro \ proto/crypt.pro \ proto/crypt_zip.pro \ + proto/debugger.pro \ proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ diff --git a/src/Make_sas.mak b/src/Make_sas.mak index 09650c31f6..78d6d2a93f 100644 --- a/src/Make_sas.mak +++ b/src/Make_sas.mak @@ -96,6 +96,7 @@ SRC = \ charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ @@ -157,6 +158,7 @@ OBJ = \ charset.o \ crypt.o \ crypt_zip.o \ + debugger.o \ dict.o \ diff.o \ digraph.o \ @@ -218,6 +220,7 @@ PRO = \ proto/charset.pro \ proto/crypt.pro \ proto/crypt_zip.pro \ + proto/debugger.pro \ proto/dict.pro \ proto/diff.pro \ proto/digraph.pro \ @@ -340,6 +343,8 @@ crypt.o: crypt.c proto/crypt.pro: crypt.c crypt_zip.o: crypt_zip.c proto/crypt_zip.pro: crypt_zip.c +debugger.o: debugger.c +proto/debugger.pro: debugger.c dict.o: dict.c proto/dict.pro: dict.c diff.o: diff.c diff --git a/src/Make_vms.mms b/src/Make_vms.mms index bc8aee69f6..edf77cb6bd 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -308,30 +308,30 @@ ALL_LIBS = $(LIBS) $(GUI_LIB_DIR) $(GUI_LIB) \ $(PERL_LIB) $(PYTHON_LIB) $(TCL_LIB) $(RUBY_LIB) SRC = arabic.c autocmd.c beval.c blob.c blowfish.c buffer.c charset.c \ - crypt.c crypt_zip.c dict.c diff.c digraph.c edit.c eval.c evalfunc.c \ - ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c if_cscope.c \ - if_xcmdsrv.c fileio.c findfile.c fold.c getchar.c hardcopy.c \ - hashtab.c indent.c insexpand.c json.c list.c main.c mark.c menu.c \ - mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c normal.c \ - ops.c option.c popupmnu.c quickfix.c regexp.c search.c sha256.c sign.c \ - spell.c spellfile.c syntax.c tag.c term.c termlib.c textprop.c ui.c \ - undo.c userfunc.c version.c screen.c window.c os_unix.c os_vms.c \ - pathdef.c + crypt.c crypt_zip.c debugger.c dict.c diff.c digraph.c edit.c eval.c \ + evalfunc.c ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c \ + if_cscope.c if_xcmdsrv.c fileio.c findfile.c fold.c getchar.c \ + hardcopy.c hashtab.c indent.c insexpand.c json.c list.c main.c mark.c \ + menu.c mbyte.c memfile.c memline.c message.c misc1.c misc2.c move.c \ + normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c \ + sha256.c sign.c spell.c spellfile.c syntax.c tag.c term.c termlib.c \ + textprop.c ui.c undo.c userfunc.c version.c screen.c window.c \ + os_unix.c os_vms.c pathdef.c \ $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \ $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC) OBJ = arabic.obj autocmd.obj beval.obj blob.obj blowfish.obj buffer.obj \ - charset.obj crypt.obj crypt_zip.obj dict.obj diff.obj digraph.obj \ - edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj ex_docmd.obj \ - ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj \ + charset.obj crypt.obj crypt_zip.obj debugger.obj dict.obj diff.obj \ + digraph.obj edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj \ + ex_docmd.obj ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj \ fileio.obj findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \ indent.obj insexpand.obj json.obj list.obj main.obj mark.obj \ menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \ move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \ quickfix.obj regexp.obj search.obj sha256.obj sign.obj spell.obj \ spellfile.obj syntax.obj tag.obj term.obj termlib.obj textprop.obj \ - ui.obj undo.obj userfunc.obj screen.obj version.obj window.obj \ - os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \ + ui.obj undo.obj userfunc.obj screen.obj version.obj \ + window.obj os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \ $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) @@ -522,6 +522,10 @@ crypt_zip.obj : crypt_zip.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h option.h structs.h \ regexp.h gui.h beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h \ proto.h globals.h +debugger.obj : debugger.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h dict.obj : dict.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ beval.h [.proto]gui_beval.pro alloc.h ex_cmds.h spell.h proto.h \ diff --git a/src/Makefile b/src/Makefile index bee20ae3e7..cf23ca816d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1579,6 +1579,7 @@ BASIC_SRC = \ charset.c \ crypt.c \ crypt_zip.c \ + debugger.c \ dict.c \ diff.c \ digraph.c \ @@ -1693,6 +1694,7 @@ OBJ_COMMON = \ objects/blowfish.o \ objects/crypt.o \ objects/crypt_zip.o \ + objects/debugger.o \ objects/dict.o \ objects/diff.o \ objects/digraph.o \ @@ -1820,6 +1822,7 @@ PRO_AUTO = \ charset.pro \ crypt.pro \ crypt_zip.pro \ + debugger.pro \ dict.pro \ diff.pro \ digraph.pro \ @@ -2963,6 +2966,9 @@ objects/crypt.o: crypt.c objects/crypt_zip.o: crypt_zip.c $(CCC) -o $@ crypt_zip.c +objects/debugger.o: debugger.c + $(CCC) -o $@ debugger.c + objects/dict.o: dict.c $(CCC) -o $@ dict.c @@ -3425,6 +3431,9 @@ objects/crypt_zip.o: crypt_zip.c vim.h protodef.h auto/config.h feature.h \ os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h +objects/debugger.o: debugger.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + os_mac.h ascii.h keymap.h term.h macros.h option.h beval.h structs.h \ + regexp.h gui.h alloc.h ex_cmds.h spell.h proto.h globals.h objects/dict.o: dict.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ diff --git a/src/debugger.c b/src/debugger.c new file mode 100644 index 0000000000..10dc86dcc6 --- /dev/null +++ b/src/debugger.c @@ -0,0 +1,990 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * debugger.c: Vim script debugger functions + */ + +#include "vim.h" + +#if defined(FEAT_EVAL) || defined(PROTO) +static int debug_greedy = FALSE; /* batch mode debugging: don't save + and restore typeahead. */ +static void do_setdebugtracelevel(char_u *arg); +static void do_checkbacktracelevel(void); +static void do_showbacktrace(char_u *cmd); + +static char_u *debug_oldval = NULL; /* old and newval for debug expressions */ +static char_u *debug_newval = NULL; +static int debug_expr = 0; /* use debug_expr */ + + int +has_watchexpr(void) +{ + return debug_expr; +} + +/* + * do_debug(): Debug mode. + * Repeatedly get Ex commands, until told to continue normal execution. + */ + void +do_debug(char_u *cmd) +{ + int save_msg_scroll = msg_scroll; + int save_State = State; + int save_did_emsg = did_emsg; + int save_cmd_silent = cmd_silent; + int save_msg_silent = msg_silent; + int save_emsg_silent = emsg_silent; + int save_redir_off = redir_off; + tasave_T typeaheadbuf; + int typeahead_saved = FALSE; + int save_ignore_script = 0; + int save_ex_normal_busy; + int n; + char_u *cmdline = NULL; + char_u *p; + char *tail = NULL; + static int last_cmd = 0; +#define CMD_CONT 1 +#define CMD_NEXT 2 +#define CMD_STEP 3 +#define CMD_FINISH 4 +#define CMD_QUIT 5 +#define CMD_INTERRUPT 6 +#define CMD_BACKTRACE 7 +#define CMD_FRAME 8 +#define CMD_UP 9 +#define CMD_DOWN 10 + +#ifdef ALWAYS_USE_GUI + /* Can't do this when there is no terminal for input/output. */ + if (!gui.in_use) + { + /* Break as soon as possible. */ + debug_break_level = 9999; + return; + } +#endif + + /* Make sure we are in raw mode and start termcap mode. Might have side + * effects... */ + settmode(TMODE_RAW); + starttermcap(); + + ++RedrawingDisabled; /* don't redisplay the window */ + ++no_wait_return; /* don't wait for return */ + did_emsg = FALSE; /* don't use error from debugged stuff */ + cmd_silent = FALSE; /* display commands */ + msg_silent = FALSE; /* display messages */ + emsg_silent = FALSE; /* display error messages */ + redir_off = TRUE; /* don't redirect debug commands */ + + State = NORMAL; + debug_mode = TRUE; + + if (!debug_did_msg) + msg(_("Entering Debug mode. Type \"cont\" to continue.")); + if (debug_oldval != NULL) + { + smsg(_("Oldval = \"%s\""), debug_oldval); + vim_free(debug_oldval); + debug_oldval = NULL; + } + if (debug_newval != NULL) + { + smsg(_("Newval = \"%s\""), debug_newval); + vim_free(debug_newval); + debug_newval = NULL; + } + if (sourcing_name != NULL) + msg((char *)sourcing_name); + if (sourcing_lnum != 0) + smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd); + else + smsg(_("cmd: %s"), cmd); + /* + * Repeat getting a command and executing it. + */ + for (;;) + { + msg_scroll = TRUE; + need_wait_return = FALSE; + + /* Save the current typeahead buffer and replace it with an empty one. + * This makes sure we get input from the user here and don't interfere + * with the commands being executed. Reset "ex_normal_busy" to avoid + * the side effects of using ":normal". Save the stuff buffer and make + * it empty. Set ignore_script to avoid reading from script input. */ + save_ex_normal_busy = ex_normal_busy; + ex_normal_busy = 0; + if (!debug_greedy) + { + save_typeahead(&typeaheadbuf); + typeahead_saved = TRUE; + save_ignore_script = ignore_script; + ignore_script = TRUE; + } + + vim_free(cmdline); + cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL); + + if (typeahead_saved) + { + restore_typeahead(&typeaheadbuf); + ignore_script = save_ignore_script; + } + ex_normal_busy = save_ex_normal_busy; + + cmdline_row = msg_row; + msg_starthere(); + if (cmdline != NULL) + { + /* If this is a debug command, set "last_cmd". + * If not, reset "last_cmd". + * For a blank line use previous command. */ + p = skipwhite(cmdline); + if (*p != NUL) + { + switch (*p) + { + case 'c': last_cmd = CMD_CONT; + tail = "ont"; + break; + case 'n': last_cmd = CMD_NEXT; + tail = "ext"; + break; + case 's': last_cmd = CMD_STEP; + tail = "tep"; + break; + case 'f': + last_cmd = 0; + if (p[1] == 'r') + { + last_cmd = CMD_FRAME; + tail = "rame"; + } + else + { + last_cmd = CMD_FINISH; + tail = "inish"; + } + break; + case 'q': last_cmd = CMD_QUIT; + tail = "uit"; + break; + case 'i': last_cmd = CMD_INTERRUPT; + tail = "nterrupt"; + break; + case 'b': last_cmd = CMD_BACKTRACE; + if (p[1] == 't') + tail = "t"; + else + tail = "acktrace"; + break; + case 'w': last_cmd = CMD_BACKTRACE; + tail = "here"; + break; + case 'u': last_cmd = CMD_UP; + tail = "p"; + break; + case 'd': last_cmd = CMD_DOWN; + tail = "own"; + break; + default: last_cmd = 0; + } + if (last_cmd != 0) + { + /* Check that the tail matches. */ + ++p; + while (*p != NUL && *p == *tail) + { + ++p; + ++tail; + } + if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) + last_cmd = 0; + } + } + + if (last_cmd != 0) + { + /* Execute debug command: decided where to break next and + * return. */ + switch (last_cmd) + { + case CMD_CONT: + debug_break_level = -1; + break; + case CMD_NEXT: + debug_break_level = ex_nesting_level; + break; + case CMD_STEP: + debug_break_level = 9999; + break; + case CMD_FINISH: + debug_break_level = ex_nesting_level - 1; + break; + case CMD_QUIT: + got_int = TRUE; + debug_break_level = -1; + break; + case CMD_INTERRUPT: + got_int = TRUE; + debug_break_level = 9999; + /* Do not repeat ">interrupt" cmd, continue stepping. */ + last_cmd = CMD_STEP; + break; + case CMD_BACKTRACE: + do_showbacktrace(cmd); + continue; + case CMD_FRAME: + if (*p == NUL) + { + do_showbacktrace(cmd); + } + else + { + p = skipwhite(p); + do_setdebugtracelevel(p); + } + continue; + case CMD_UP: + debug_backtrace_level++; + do_checkbacktracelevel(); + continue; + case CMD_DOWN: + debug_backtrace_level--; + do_checkbacktracelevel(); + continue; + } + /* Going out reset backtrace_level */ + debug_backtrace_level = 0; + break; + } + + /* don't debug this command */ + n = debug_break_level; + debug_break_level = -1; + (void)do_cmdline(cmdline, getexline, NULL, + DOCMD_VERBOSE|DOCMD_EXCRESET); + debug_break_level = n; + } + lines_left = Rows - 1; + } + vim_free(cmdline); + + --RedrawingDisabled; + --no_wait_return; + redraw_all_later(NOT_VALID); + need_wait_return = FALSE; + msg_scroll = save_msg_scroll; + lines_left = Rows - 1; + State = save_State; + debug_mode = FALSE; + did_emsg = save_did_emsg; + cmd_silent = save_cmd_silent; + msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + redir_off = save_redir_off; + + /* Only print the message again when typing a command before coming back + * here. */ + debug_did_msg = TRUE; +} + + static int +get_maxbacktrace_level(void) +{ + char *p, *q; + int maxbacktrace = 0; + + if (sourcing_name != NULL) + { + p = (char *)sourcing_name; + while ((q = strstr(p, "..")) != NULL) + { + p = q + 2; + maxbacktrace++; + } + } + return maxbacktrace; +} + + static void +do_setdebugtracelevel(char_u *arg) +{ + int level; + + level = atoi((char *)arg); + if (*arg == '+' || level < 0) + debug_backtrace_level += level; + else + debug_backtrace_level = level; + + do_checkbacktracelevel(); +} + + static void +do_checkbacktracelevel(void) +{ + if (debug_backtrace_level < 0) + { + debug_backtrace_level = 0; + msg(_("frame is zero")); + } + else + { + int max = get_maxbacktrace_level(); + + if (debug_backtrace_level > max) + { + debug_backtrace_level = max; + smsg(_("frame at highest level: %d"), max); + } + } +} + + static void +do_showbacktrace(char_u *cmd) +{ + char *cur; + char *next; + int i = 0; + int max = get_maxbacktrace_level(); + + if (sourcing_name != NULL) + { + cur = (char *)sourcing_name; + while (!got_int) + { + next = strstr(cur, ".."); + if (next != NULL) + *next = NUL; + if (i == max - debug_backtrace_level) + smsg("->%d %s", max - i, cur); + else + smsg(" %d %s", max - i, cur); + ++i; + if (next == NULL) + break; + *next = '.'; + cur = next + 2; + } + } + if (sourcing_lnum != 0) + smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd); + else + smsg(_("cmd: %s"), cmd); +} + +/* + * ":debug". + */ + void +ex_debug(exarg_T *eap) +{ + int debug_break_level_save = debug_break_level; + + debug_break_level = 9999; + do_cmdline_cmd(eap->arg); + debug_break_level = debug_break_level_save; +} + +static char_u *debug_breakpoint_name = NULL; +static linenr_T debug_breakpoint_lnum; + +/* + * When debugging or a breakpoint is set on a skipped command, no debug prompt + * is shown by do_one_cmd(). This situation is indicated by debug_skipped, and + * debug_skipped_name is then set to the source name in the breakpoint case. If + * a skipped command decides itself that a debug prompt should be displayed, it + * can do so by calling dbg_check_skipped(). + */ +static int debug_skipped; +static char_u *debug_skipped_name; + +/* + * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is + * at or below the break level. But only when the line is actually + * executed. Return TRUE and set breakpoint_name for skipped commands that + * decide to execute something themselves. + * Called from do_one_cmd() before executing a command. + */ + void +dbg_check_breakpoint(exarg_T *eap) +{ + char_u *p; + + debug_skipped = FALSE; + if (debug_breakpoint_name != NULL) + { + if (!eap->skip) + { + /* replace K_SNR with "<SNR>" */ + if (debug_breakpoint_name[0] == K_SPECIAL + && debug_breakpoint_name[1] == KS_EXTRA + && debug_breakpoint_name[2] == (int)KE_SNR) + p = (char_u *)"<SNR>"; + else + p = (char_u *)""; + smsg(_("Breakpoint in \"%s%s\" line %ld"), + p, + debug_breakpoint_name + (*p == NUL ? 0 : 3), + (long)debug_breakpoint_lnum); + debug_breakpoint_name = NULL; + do_debug(eap->cmd); + } + else + { + debug_skipped = TRUE; + debug_skipped_name = debug_breakpoint_name; + debug_breakpoint_name = NULL; + } + } + else if (ex_nesting_level <= debug_break_level) + { + if (!eap->skip) + do_debug(eap->cmd); + else + { + debug_skipped = TRUE; + debug_skipped_name = NULL; + } + } +} + +/* + * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was + * set. Return TRUE when the debug mode is entered this time. + */ + int +dbg_check_skipped(exarg_T *eap) +{ + int prev_got_int; + + if (debug_skipped) + { + /* + * Save the value of got_int and reset it. We don't want a previous + * interruption cause flushing the input buffer. + */ + prev_got_int = got_int; + got_int = FALSE; + debug_breakpoint_name = debug_skipped_name; + /* eap->skip is TRUE */ + eap->skip = FALSE; + (void)dbg_check_breakpoint(eap); + eap->skip = TRUE; + got_int |= prev_got_int; + return TRUE; + } + return FALSE; +} + +/* + * The list of breakpoints: dbg_breakp. + * This is a grow-array of structs. + */ +struct debuggy +{ + int dbg_nr; /* breakpoint number */ + int dbg_type; /* DBG_FUNC, DBG_FILE or DBG_EXPR */ + char_u *dbg_name; /* function, expression or file name */ + regprog_T *dbg_prog; /* regexp program */ + linenr_T dbg_lnum; /* line number in function or file */ + int dbg_forceit; /* ! used */ +#ifdef FEAT_EVAL + typval_T *dbg_val; /* last result of watchexpression */ +#endif + int dbg_level; /* stored nested level for expr */ +}; + +static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL}; +#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx]) +#define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx]) +static int last_breakp = 0; /* nr of last defined breakpoint */ + +#ifdef FEAT_PROFILE +/* Profiling uses file and func names similar to breakpoints. */ +static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL}; +#endif +#define DBG_FUNC 1 +#define DBG_FILE 2 +#define DBG_EXPR 3 + +static linenr_T debuggy_find(int file,char_u *fname, linenr_T after, garray_T *gap, int *fp); + +/* + * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them + * in the entry just after the last one in dbg_breakp. Note that "dbg_name" + * is allocated. + * Returns FAIL for failure. + */ + static int +dbg_parsearg( + char_u *arg, + garray_T *gap) /* either &dbg_breakp or &prof_ga */ +{ + char_u *p = arg; + char_u *q; + struct debuggy *bp; + int here = FALSE; + + if (ga_grow(gap, 1) == FAIL) + return FAIL; + bp = &DEBUGGY(gap, gap->ga_len); + + /* Find "func" or "file". */ + if (STRNCMP(p, "func", 4) == 0) + bp->dbg_type = DBG_FUNC; + else if (STRNCMP(p, "file", 4) == 0) + bp->dbg_type = DBG_FILE; + else if ( +#ifdef FEAT_PROFILE + gap != &prof_ga && +#endif + STRNCMP(p, "here", 4) == 0) + { + if (curbuf->b_ffname == NULL) + { + emsg(_(e_noname)); + return FAIL; + } + bp->dbg_type = DBG_FILE; + here = TRUE; + } + else if ( +#ifdef FEAT_PROFILE + gap != &prof_ga && +#endif + STRNCMP(p, "expr", 4) == 0) + bp->dbg_type = DBG_EXPR; + else + { + semsg(_(e_invarg2), p); + return FAIL; + } + p = skipwhite(p + 4); + + /* Find optional line number. */ + if (here) + bp->dbg_lnum = curwin->w_cursor.lnum; + else if ( +#ifdef FEAT_PROFILE + gap != &prof_ga && +#endif + VIM_ISDIGIT(*p)) + { + bp->dbg_lnum = getdigits(&p); + p = skipwhite(p); + } + else + bp->dbg_lnum = 0; + + /* Find the function or file name. Don't accept a function name with (). */ + if ((!here && *p == NUL) + || (here && *p != NUL) + || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL)) + { + semsg(_(e_invarg2), arg); + return FAIL; + } + + if (bp->dbg_type == DBG_FUNC) + bp->dbg_name = vim_strsave(p); + else if (here) + bp->dbg_name = vim_strsave(curbuf->b_ffname); + else if (bp->dbg_type == DBG_EXPR) + { + bp->dbg_name = vim_strsave(p); + if (bp->dbg_name != NULL) + bp->dbg_val = eval_expr(bp->dbg_name, NULL); + } + else + { + /* Expand the file name in the same way as do_source(). This means + * doing it twice, so that $DIR/file gets expanded when $DIR is + * "~/dir". */ + q = expand_env_save(p); + if (q == NULL) + return FAIL; + p = expand_env_save(q); + vim_free(q); + if (p == NULL) + return FAIL; + if (*p != '*') + { + bp->dbg_name = fix_fname(p); + vim_free(p); + } + else + bp->dbg_name = p; + } + + if (bp->dbg_name == NULL) + return FAIL; + return OK; +} + +/* + * ":breakadd". Also used for ":profile". + */ + void +ex_breakadd(exarg_T *eap) +{ + struct debuggy *bp; + char_u *pat; + garray_T *gap; + + gap = &dbg_breakp; +#ifdef FEAT_PROFILE + if (eap->cmdidx == CMD_profile) + gap = &prof_ga; +#endif + + if (dbg_parsearg(eap->arg, gap) == OK) + { + bp = &DEBUGGY(gap, gap->ga_len); + bp->dbg_forceit = eap->forceit; + + if (bp->dbg_type != DBG_EXPR) + { + pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE); + if (pat != NULL) + { + bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + vim_free(pat); + } + if (pat == NULL || bp->dbg_prog == NULL) + vim_free(bp->dbg_name); + else + { + if (bp->dbg_lnum == 0) /* default line number is 1 */ + bp->dbg_lnum = 1; +#ifdef FEAT_PROFILE + if (eap->cmdidx != CMD_profile) +#endif + { + DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp; + ++debug_tick; + } + ++gap->ga_len; + } + } + else + { + /* DBG_EXPR */ + DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp; + ++debug_tick; + } + } +} + +/* + * ":debuggreedy". + */ + void +ex_debuggreedy(exarg_T *eap) +{ + if (eap->addr_count == 0 || eap->line2 != 0) + debug_greedy = TRUE; + else + debug_greedy = FALSE; +} + +/* + * ":breakdel" and ":profdel". + */ + void +ex_breakdel(exarg_T *eap) +{ + struct debuggy *bp, *bpi; + int nr; + int todel = -1; + int del_all = FALSE; + int i; + linenr_T best_lnum = 0; + garray_T *gap; + + gap = &dbg_breakp; + if (eap->cmdidx == CMD_profdel) + { +#ifdef FEAT_PROFILE + gap = &prof_ga; +#else + ex_ni(eap); + return; +#endif + } + + if (vim_isdigit(*eap->arg)) + { + /* ":breakdel {nr}" */ + nr = atol((char *)eap->arg); + for (i = 0; i < gap->ga_len; ++i) + if (DEBUGGY(gap, i).dbg_nr == nr) + { + todel = i; + break; + } + } + else if (*eap->arg == '*') + { + todel = 0; + del_all = TRUE; + } + else + { + /* ":breakdel {func|file|expr} [lnum] {name}" */ + if (dbg_parsearg(eap->arg, gap) == FAIL) + return; + bp = &DEBUGGY(gap, gap->ga_len); + for (i = 0; i < gap->ga_len; ++i) + { + bpi = &DEBUGGY(gap, i); + if (bp->dbg_type == bpi->dbg_type + && STRCMP(bp->dbg_name, bpi->dbg_name) == 0 |