vim-patch:7.4.1102

Problem:    Debugger has no stack backtrace support.
Solution:   Add "backtrace", "frame", "up" and "down" commands. (Alberto
            Fanjul, closes vim/vim#433)

f1f60f859c
This commit is contained in:
Jurica Bradaric 2016-05-10 21:34:56 +02:00
parent 68717132b1
commit b4cbfd3c08
6 changed files with 372 additions and 13 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
@ -467,16 +467,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 backtrace 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

@ -18123,6 +18123,25 @@ static dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, in
return HI2DI(hi);
}
// Get function call environment based on backtrace debug level
static funccall_T *get_funccal(void)
{
funccall_T *funccal = current_funccal;
if (debug_backtrace_level > 0) {
for (int i = 0; i < debug_backtrace_level; i++) {
funccall_T *temp_funccal = funccal->caller;
if (temp_funccal) {
funccal = temp_funccal;
} else {
// backtrace level overflow. reset to max
debug_backtrace_level = i;
}
}
}
return funccal;
}
// Find the dict and hashtable used for a variable name. Set "varname" to the
// start of name without ':'.
static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
@ -18147,7 +18166,11 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
return &compat_hashtab;
}
*d = current_funccal ? &current_funccal->l_vars : &globvardict;
if (current_funccal == NULL) {
*d = &globvardict;
} else {
*d = &get_funccal()->l_vars; // l: variable
}
goto end;
}
@ -18169,9 +18192,9 @@ static hashtab_T *find_var_ht_dict(char_u *name, uint8_t **varname, dict_T **d)
} else if (*name == 'v') { // v: variable
*d = &vimvardict;
} else if (*name == 'a' && current_funccal != NULL) { // function argument
*d = &current_funccal->l_avars;
*d = &get_funccal()->l_avars;
} else if (*name == 'l' && current_funccal != NULL) { // local variable
*d = &current_funccal->l_vars;
*d = &get_funccal()->l_vars;
} else if (*name == 's' // script variable
&& current_SID > 0 && current_SID <= ga_scripts.ga_len) {
*d = &SCRIPT_SV(current_SID)->sv_dict;

View File

@ -116,6 +116,11 @@ struct source_cookie {
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.
* Repeatedly get Ex commands, until told to continue normal execution.
@ -144,6 +149,10 @@ void do_debug(char_u *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
++RedrawingDisabled; /* don't redisplay the window */
@ -194,6 +203,7 @@ void do_debug(char_u *cmd)
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".
@ -210,8 +220,15 @@ void do_debug(char_u *cmd)
case 's': last_cmd = CMD_STEP;
tail = "tep";
break;
case 'f': last_cmd = CMD_FINISH;
tail = "inish";
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";
@ -219,6 +236,26 @@ void do_debug(char_u *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) {
@ -228,8 +265,9 @@ void do_debug(char_u *cmd)
++p;
++tail;
}
if (ASCII_ISALPHA(*p))
if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) {
last_cmd = 0;
}
}
}
@ -259,7 +297,28 @@ void do_debug(char_u *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;
}
@ -294,6 +353,78 @@ void do_debug(char_u *cmd)
debug_did_msg = TRUE;
}
static int get_maxbacktrace_level(void)
{
int maxbacktrace = 0;
if (sourcing_name != NULL) {
char *p = (char *)sourcing_name;
char *q;
while ((q = strstr(p, "..")) != NULL) {
p = q + 2;
maxbacktrace++;
}
}
return maxbacktrace;
}
static void do_setdebugtracelevel(char_u *arg)
{
int 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)
{
if (sourcing_name != NULL) {
int i = 0;
int max = get_maxbacktrace_level();
char *cur = (char *)sourcing_name;
while (!got_int) {
char *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 %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
} else {
smsg(_("cmd: %s"), cmd);
}
}
/*
* ":debug".
*/

View File

@ -293,10 +293,11 @@ EXTERN int msg_no_more INIT(= FALSE); /* don't use more prompt, truncate
EXTERN char_u *sourcing_name INIT( = NULL); /* name of error message source */
EXTERN linenr_T sourcing_lnum INIT(= 0); /* line number of the source file */
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 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
/* Values for "do_profiling". */
#define PROF_NONE 0 /* profiling not started */

View File

@ -580,7 +580,7 @@ static int included_patches[] = {
1105,
// 1104 NA
// 1103 NA
// 1102,
1102,
1101,
// 1100 NA
// 1099 NA

View File

@ -0,0 +1,176 @@
-- Tests for backtrace debug commands.
local helpers = require('test.functional.helpers')
local feed, insert, source = helpers.feed, helpers.insert, helpers.source
local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect
describe('108', function()
before_each(clear)
it('is working', function()
execute('function! Foo()')
execute(' let var1 = 1')
execute(' let var2 = Bar(var1) + 9')
execute(' return var2')
execute('endfunction')
execute('function! Bar(var)')
execute(' let var1 = 2 + a:var')
execute(' let var2 = Bazz(var1) + 4')
execute(' return var2')
execute('endfunction')
execute('function! Bazz(var)')
execute(' let var1 = 3 + a:var')
execute(' let var3 = "another var"')
execute(' return var1')
execute('endfunction')
execute('new')
execute('debuggreedy')
execute('redir => out')
execute('debug echo Foo()')
feed('step<cr>')
feed('step<cr>')
feed('step<cr>')
feed('step<cr>')
feed('step<cr>')
feed('step<cr>')
feed([[echo "- show backtrace:\n"<cr>]])
feed('backtrace<cr>')
feed([[echo "\nshow variables on different levels:\n"<cr>]])
feed('echo var1<cr>')
feed('up<cr>')
feed('back<cr>')
feed('echo var1<cr>')
feed('u<cr>')
feed('bt<cr>')
feed('echo var1<cr>')
feed([[echo "\n- undefined vars:\n"<cr>]])
feed('step<cr>')
feed('frame 2<cr>')
feed('echo "undefined var3 on former level:"<cr>')
feed('echo var3<cr>')
feed('fr 0<cr>')
feed([[echo "here var3 is defined with \"another var\":"<cr>]])
feed('echo var3<cr>')
feed('step<cr>')
feed('step<cr>')
feed('step<cr>')
feed('up<cr>')
feed([[echo "\nundefined var2 on former level"<cr>]])
feed('echo var2<cr>')
feed('down<cr>')
feed('echo "here var2 is defined with 10:"<cr>')
feed('echo var2<cr>')
feed([[echo "\n- backtrace movements:\n"<cr>]])
feed('b<cr>')
feed([[echo "\nnext command cannot go down, we are on bottom\n"<cr>]])
feed('down<cr>')
feed('up<cr>')
feed([[echo "\nnext command cannot go up, we are on top\n"<cr>]])
feed('up<cr>')
feed('b<cr>')
feed('echo "fil is not frame or finish, it is file"<cr>')
feed('fil<cr>')
feed([[echo "\n- relative backtrace movement\n"<cr>]])
feed('fr -1<cr>')
feed('frame<cr>')
feed('fra +1<cr>')
feed('fram<cr>')
feed([[echo "\n- go beyond limits does not crash\n"<cr>]])
feed('fr 100<cr>')
feed('fra<cr>')
feed('frame -40<cr>')
feed('fram<cr>')
feed([[echo "\n- final result 19:"<cr>]])
feed('cont<cr>')
execute('0debuggreedy')
execute('redir END')
execute('$put =out')
-- Assert buffer contents.
expect([=[
- 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
]=])
end)
end)