0
0
mirror of https://github.com/vim/vim.git synced 2025-09-27 04:14:06 -04:00

patch 7.4.1102

Problem:    Debugger has no stack backtrace support.
Solution:   Add "backtrace", "frame", "up" and "down" commands. (Alberto
            Fanjul, closes #433)
This commit is contained in:
Bram Moolenaar
2016-01-16 15:40:53 +01:00
parent e39b3d9fb4
commit f1f60f859c
8 changed files with 382 additions and 8 deletions

View File

@@ -1,4 +1,4 @@
*repeat.txt* For Vim version 7.4. Last change: 2015 Apr 13
*repeat.txt* For Vim version 7.4. Last change: 2016 Jan 16
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -483,16 +483,44 @@ Additionally, these commands can be used:
finish Finish the current script or user function and come
back to debug mode for the command after the one that
sourced or called it.
*>bt*
*>backtrace*
*>where*
backtrace Show the call stacktrace for current debugging session.
bt
where
*>frame*
frame N Goes to N bactrace level. + and - signs make movement
relative. E.g., ":frame +3" goes three frames up.
*>up*
up Goes one level up from call stacktrace.
*>down*
down Goes one level down from call stacktrace.
About the additional commands in debug mode:
- There is no command-line completion for them, you get the completion for the
normal Ex commands only.
- You can shorten them, up to a single character: "c", "n", "s" and "f".
- You can shorten them, up to a single character, unless more then one command
starts with the same letter. "f" stands for "finish", use "fr" for "frame".
- Hitting <CR> will repeat the previous one. When doing another command, this
is reset (because it's not clear what you want to repeat).
- When you want to use the Ex command with the same name, prepend a colon:
":cont", ":next", ":finish" (or shorter).
The backtrace shows the hierarchy of function calls, e.g.:
>bt ~
3 function One[3] ~
2 Two[3] ~
->1 Three[3] ~
0 Four ~
line 1: let four = 4 ~
The "->" points to the current frame. Use "up", "down" and "frame N" to
select another frame.
In the current frame you can evaluate the local function variables. There is
no way to see the command at the current line yet.
DEFINING BREAKPOINTS
*:breaka* *:breakadd*

View File

@@ -812,6 +812,7 @@ static char_u *get_tv_string_buf_chk __ARGS((typval_T *varp, char_u *buf));
static dictitem_T *find_var __ARGS((char_u *name, hashtab_T **htp, int no_autoload));
static dictitem_T *find_var_in_ht __ARGS((hashtab_T *ht, int htname, char_u *varname, int no_autoload));
static hashtab_T *find_var_ht __ARGS((char_u *name, char_u **varname));
static funccall_T *get_funccal __ARGS((void));
static void vars_clear_ext __ARGS((hashtab_T *ht, int free_val));
static void delete_var __ARGS((hashtab_T *ht, hashitem_T *hi));
static void list_one_var __ARGS((dictitem_T *v, char_u *prefix, int *first));
@@ -21735,7 +21736,7 @@ find_var_ht(name, varname)
if (current_funccal == NULL)
return &globvarht; /* global variable */
return &current_funccal->l_vars.dv_hashtab; /* l: variable */
return &get_funccal()->l_vars.dv_hashtab; /* l: variable */
}
*varname = name + 2;
if (*name == 'g') /* global variable */
@@ -21756,15 +21757,41 @@ find_var_ht(name, varname)
if (*name == 'v') /* v: variable */
return &vimvarht;
if (*name == 'a' && current_funccal != NULL) /* function argument */
return &current_funccal->l_avars.dv_hashtab;
return &get_funccal()->l_avars.dv_hashtab;
if (*name == 'l' && current_funccal != NULL) /* local function variable */
return &current_funccal->l_vars.dv_hashtab;
return &get_funccal()->l_vars.dv_hashtab;
if (*name == 's' /* script variable */
&& current_SID > 0 && current_SID <= ga_scripts.ga_len)
return &SCRIPT_VARS(current_SID);
return NULL;
}
/*
* Get function call environment based on bactrace debug level
*/
static funccall_T *
get_funccal()
{
int i;
funccall_T *funccal;
funccall_T *temp_funccal;
funccal = current_funccal;
if (debug_backtrace_level > 0)
{
for (i = 0; i < debug_backtrace_level; i++)
{
temp_funccal = funccal->caller;
if (temp_funccal)
funccal = temp_funccal;
else
/* backtrace level overflow. reset to max */
debug_backtrace_level = i;
}
}
return funccal;
}
/*
* Get the string value of a (global/local) variable.
* Note: see get_tv_string() for how long the pointer remains valid.

View File

@@ -68,6 +68,10 @@ typedef struct sn_prl_S
#if defined(FEAT_EVAL) || defined(PROTO)
static int debug_greedy = FALSE; /* batch mode debugging: don't save
and restore typeahead. */
static int get_maxbacktrace_level(void);
static void do_setdebugtracelevel(char_u *arg);
static void do_checkbacktracelevel(void);
static void do_showbacktrace(char_u *cmd);
/*
* do_debug(): Debug mode.
@@ -101,6 +105,10 @@ do_debug(cmd)
#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. */
@@ -178,6 +186,7 @@ do_debug(cmd)
# endif
cmdline_row = msg_row;
msg_starthere();
if (cmdline != NULL)
{
/* If this is a debug command, set "last_cmd".
@@ -197,8 +206,18 @@ do_debug(cmd)
case 's': last_cmd = CMD_STEP;
tail = "tep";
break;
case 'f': last_cmd = CMD_FINISH;
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";
@@ -206,6 +225,21 @@ do_debug(cmd)
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)
@@ -217,7 +251,7 @@ do_debug(cmd)
++p;
++tail;
}
if (ASCII_ISALPHA(*p))
if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME)
last_cmd = 0;
}
}
@@ -250,7 +284,31 @@ do_debug(cmd)
/* 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;
}
@@ -285,6 +343,92 @@ do_debug(cmd)
debug_did_msg = TRUE;
}
static int
get_maxbacktrace_level(void)
{
char *p, *q;
int maxbacktrace = 1;
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((char_u *)_("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((char_u *)"->%d %s", max - i, cur);
else
smsg((char_u *)" %d %s", max - i, cur);
++i;
if (next == NULL)
break;
*next = '.';
cur = next + 2;
}
}
if (sourcing_lnum != 0)
smsg((char_u *)_("line %ld: %s"), (long)sourcing_lnum, cmd);
else
smsg((char_u *)_("cmd: %s"), cmd);
}
/*
* ":debug".
*/

View File

@@ -231,6 +231,7 @@ EXTERN int ex_nesting_level INIT(= 0); /* nesting level */
EXTERN int debug_break_level INIT(= -1); /* break below this level */
EXTERN int debug_did_msg INIT(= FALSE); /* did "debug mode" message */
EXTERN int debug_tick INIT(= 0); /* breakpoint change count */
EXTERN int debug_backtrace_level INIT(= 0); /* breakpoint backtrace level */
# ifdef FEAT_PROFILE
EXTERN int do_profiling INIT(= PROF_NONE); /* PROF_ values */
# endif

View File

@@ -89,6 +89,7 @@ SCRIPTS_ALL = \
test105.out \
test106.out \
test107.out \
test108.out \
test_argument_0count.out \
test_argument_count.out \
test_autocmd_option.out \

87
src/testdir/test108.in Normal file
View File

@@ -0,0 +1,87 @@
Tests for backtrace debug commands. vim: set ft=vim :
STARTTEST
:so small.vim
:function! Foo()
: let var1 = 1
: let var2 = Bar(var1) + 9
: return var2
:endfunction
:
:function! Bar(var)
: let var1 = 2 + a:var
: let var2 = Bazz(var1) + 4
: return var2
:endfunction
:
:function! Bazz(var)
: let var1 = 3 + a:var
: let var3 = "another var"
: return var1
:endfunction
:new
:debuggreedy
:redir => out
:debug echo Foo()
step
step
step
step
step
step
echo "- show backtrace:\n"
backtrace
echo "\nshow variables on different levels:\n"
echo var1
up
back
echo var1
u
bt
echo var1
echo "\n- undefined vars:\n"
step
frame 2
echo "undefined var3 on former level:"
echo var3
fr 0
echo "here var3 is defined with \"another var\":"
echo var3
step
step
step
up
echo "\nundefined var2 on former level"
echo var2
down
echo "here var2 is defined with 10:"
echo var2
echo "\n- backtrace movements:\n"
b
echo "\nnext command cannot go down, we are on bottom\n"
down
up
echo "\nnext command cannot go up, we are on top\n"
up
b
echo "fil is not frame or finish, it is file"
fil
echo "\n- relative backtrace movement\n"
fr -1
frame
fra +1
fram
echo "\n- go beyond limits does not crash\n"
fr 100
fra
frame -40
fram
echo "\n- final result 19:"
cont
:0debuggreedy
:redir END
:$put =out
:w! test.out
:qa!
ENDTEST

84
src/testdir/test108.ok Normal file
View File

@@ -0,0 +1,84 @@
- show backtrace:
2 function Foo[2]
1 Bar[2]
->0 Bazz
line 2: let var3 = "another var"
show variables on different levels:
6
2 function Foo[2]
->1 Bar[2]
0 Bazz
line 2: let var3 = "another var"
3
->2 function Foo[2]
1 Bar[2]
0 Bazz
line 2: let var3 = "another var"
1
- undefined vars:
undefined var3 on former level:
Error detected while processing function Foo[2]..Bar[2]..Bazz:
line 3:
E121: Undefined variable: var3
E15: Invalid expression: var3
here var3 is defined with "another var":
another var
undefined var2 on former level
Error detected while processing function Foo[2]..Bar:
line 3:
E121: Undefined variable: var2
E15: Invalid expression: var2
here var2 is defined with 10:
10
- backtrace movements:
1 function Foo[2]
->0 Bar
line 3: End of function
next command cannot go down, we are on bottom
frame is zero
next command cannot go up, we are on top
frame at highest level: 1
->1 function Foo[2]
0 Bar
line 3: End of function
fil is not frame or finish, it is file
"[No Name]" --No lines in buffer--
- relative backtrace movement
1 function Foo[2]
->0 Bar
line 3: End of function
->1 function Foo[2]
0 Bar
line 3: End of function
- go beyond limits does not crash
frame at highest level: 1
->1 function Foo[2]
0 Bar
line 3: End of function
frame is zero
1 function Foo[2]
->0 Bar
line 3: End of function
- final result 19:
19

View File

@@ -741,6 +741,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1102,
/**/
1101,
/**/