Skip to content

Commit f1f60f8

Browse files
committed
patch 7.4.1102
Problem: Debugger has no stack backtrace support. Solution: Add "backtrace", "frame", "up" and "down" commands. (Alberto Fanjul, closes #433)
1 parent e39b3d9 commit f1f60f8

8 files changed

Lines changed: 382 additions & 8 deletions

File tree

runtime/doc/repeat.txt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*repeat.txt* For Vim version 7.4. Last change: 2015 Apr 13
1+
*repeat.txt* For Vim version 7.4. Last change: 2016 Jan 16
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -483,16 +483,44 @@ Additionally, these commands can be used:
483483
finish Finish the current script or user function and come
484484
back to debug mode for the command after the one that
485485
sourced or called it.
486+
*>bt*
487+
*>backtrace*
488+
*>where*
489+
backtrace Show the call stacktrace for current debugging session.
490+
bt
491+
where
492+
*>frame*
493+
frame N Goes to N bactrace level. + and - signs make movement
494+
relative. E.g., ":frame +3" goes three frames up.
495+
*>up*
496+
up Goes one level up from call stacktrace.
497+
*>down*
498+
down Goes one level down from call stacktrace.
486499

487500
About the additional commands in debug mode:
488501
- There is no command-line completion for them, you get the completion for the
489502
normal Ex commands only.
490-
- You can shorten them, up to a single character: "c", "n", "s" and "f".
503+
- You can shorten them, up to a single character, unless more then one command
504+
starts with the same letter. "f" stands for "finish", use "fr" for "frame".
491505
- Hitting <CR> will repeat the previous one. When doing another command, this
492506
is reset (because it's not clear what you want to repeat).
493507
- When you want to use the Ex command with the same name, prepend a colon:
494508
":cont", ":next", ":finish" (or shorter).
495509

510+
The backtrace shows the hierarchy of function calls, e.g.:
511+
>bt ~
512+
3 function One[3] ~
513+
2 Two[3] ~
514+
->1 Three[3] ~
515+
0 Four ~
516+
line 1: let four = 4 ~
517+
518+
The "->" points to the current frame. Use "up", "down" and "frame N" to
519+
select another frame.
520+
521+
In the current frame you can evaluate the local function variables. There is
522+
no way to see the command at the current line yet.
523+
496524

497525
DEFINING BREAKPOINTS
498526
*:breaka* *:breakadd*

src/eval.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@ static char_u *get_tv_string_buf_chk __ARGS((typval_T *varp, char_u *buf));
812812
static dictitem_T *find_var __ARGS((char_u *name, hashtab_T **htp, int no_autoload));
813813
static dictitem_T *find_var_in_ht __ARGS((hashtab_T *ht, int htname, char_u *varname, int no_autoload));
814814
static hashtab_T *find_var_ht __ARGS((char_u *name, char_u **varname));
815+
static funccall_T *get_funccal __ARGS((void));
815816
static void vars_clear_ext __ARGS((hashtab_T *ht, int free_val));
816817
static void delete_var __ARGS((hashtab_T *ht, hashitem_T *hi));
817818
static void list_one_var __ARGS((dictitem_T *v, char_u *prefix, int *first));
@@ -21735,7 +21736,7 @@ find_var_ht(name, varname)
2173521736

2173621737
if (current_funccal == NULL)
2173721738
return &globvarht; /* global variable */
21738-
return &current_funccal->l_vars.dv_hashtab; /* l: variable */
21739+
return &get_funccal()->l_vars.dv_hashtab; /* l: variable */
2173921740
}
2174021741
*varname = name + 2;
2174121742
if (*name == 'g') /* global variable */
@@ -21756,15 +21757,41 @@ find_var_ht(name, varname)
2175621757
if (*name == 'v') /* v: variable */
2175721758
return &vimvarht;
2175821759
if (*name == 'a' && current_funccal != NULL) /* function argument */
21759-
return &current_funccal->l_avars.dv_hashtab;
21760+
return &get_funccal()->l_avars.dv_hashtab;
2176021761
if (*name == 'l' && current_funccal != NULL) /* local function variable */
21761-
return &current_funccal->l_vars.dv_hashtab;
21762+
return &get_funccal()->l_vars.dv_hashtab;
2176221763
if (*name == 's' /* script variable */
2176321764
&& current_SID > 0 && current_SID <= ga_scripts.ga_len)
2176421765
return &SCRIPT_VARS(current_SID);
2176521766
return NULL;
2176621767
}
2176721768

21769+
/*
21770+
* Get function call environment based on bactrace debug level
21771+
*/
21772+
static funccall_T *
21773+
get_funccal()
21774+
{
21775+
int i;
21776+
funccall_T *funccal;
21777+
funccall_T *temp_funccal;
21778+
21779+
funccal = current_funccal;
21780+
if (debug_backtrace_level > 0)
21781+
{
21782+
for (i = 0; i < debug_backtrace_level; i++)
21783+
{
21784+
temp_funccal = funccal->caller;
21785+
if (temp_funccal)
21786+
funccal = temp_funccal;
21787+
else
21788+
/* backtrace level overflow. reset to max */
21789+
debug_backtrace_level = i;
21790+
}
21791+
}
21792+
return funccal;
21793+
}
21794+
2176821795
/*
2176921796
* Get the string value of a (global/local) variable.
2177021797
* Note: see get_tv_string() for how long the pointer remains valid.

src/ex_cmds2.c

Lines changed: 147 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ typedef struct sn_prl_S
6868
#if defined(FEAT_EVAL) || defined(PROTO)
6969
static int debug_greedy = FALSE; /* batch mode debugging: don't save
7070
and restore typeahead. */
71+
static int get_maxbacktrace_level(void);
72+
static void do_setdebugtracelevel(char_u *arg);
73+
static void do_checkbacktracelevel(void);
74+
static void do_showbacktrace(char_u *cmd);
7175

7276
/*
7377
* do_debug(): Debug mode.
@@ -101,6 +105,10 @@ do_debug(cmd)
101105
#define CMD_FINISH 4
102106
#define CMD_QUIT 5
103107
#define CMD_INTERRUPT 6
108+
#define CMD_BACKTRACE 7
109+
#define CMD_FRAME 8
110+
#define CMD_UP 9
111+
#define CMD_DOWN 10
104112

105113
#ifdef ALWAYS_USE_GUI
106114
/* Can't do this when there is no terminal for input/output. */
@@ -178,6 +186,7 @@ do_debug(cmd)
178186
# endif
179187

180188
cmdline_row = msg_row;
189+
msg_starthere();
181190
if (cmdline != NULL)
182191
{
183192
/* If this is a debug command, set "last_cmd".
@@ -197,15 +206,40 @@ do_debug(cmd)
197206
case 's': last_cmd = CMD_STEP;
198207
tail = "tep";
199208
break;
200-
case 'f': last_cmd = CMD_FINISH;
201-
tail = "inish";
209+
case 'f':
210+
last_cmd = 0;
211+
if (p[1] == 'r')
212+
{
213+
last_cmd = CMD_FRAME;
214+
tail = "rame";
215+
}
216+
else
217+
{
218+
last_cmd = CMD_FINISH;
219+
tail = "inish";
220+
}
202221
break;
203222
case 'q': last_cmd = CMD_QUIT;
204223
tail = "uit";
205224
break;
206225
case 'i': last_cmd = CMD_INTERRUPT;
207226
tail = "nterrupt";
208227
break;
228+
case 'b': last_cmd = CMD_BACKTRACE;
229+
if (p[1] == 't')
230+
tail = "t";
231+
else
232+
tail = "acktrace";
233+
break;
234+
case 'w': last_cmd = CMD_BACKTRACE;
235+
tail = "here";
236+
break;
237+
case 'u': last_cmd = CMD_UP;
238+
tail = "p";
239+
break;
240+
case 'd': last_cmd = CMD_DOWN;
241+
tail = "own";
242+
break;
209243
default: last_cmd = 0;
210244
}
211245
if (last_cmd != 0)
@@ -217,7 +251,7 @@ do_debug(cmd)
217251
++p;
218252
++tail;
219253
}
220-
if (ASCII_ISALPHA(*p))
254+
if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME)
221255
last_cmd = 0;
222256
}
223257
}
@@ -250,7 +284,31 @@ do_debug(cmd)
250284
/* Do not repeat ">interrupt" cmd, continue stepping. */
251285
last_cmd = CMD_STEP;
252286
break;
287+
case CMD_BACKTRACE:
288+
do_showbacktrace(cmd);
289+
continue;
290+
case CMD_FRAME:
291+
if (*p == NUL)
292+
{
293+
do_showbacktrace(cmd);
294+
}
295+
else
296+
{
297+
p = skipwhite(p);
298+
do_setdebugtracelevel(p);
299+
}
300+
continue;
301+
case CMD_UP:
302+
debug_backtrace_level++;
303+
do_checkbacktracelevel();
304+
continue;
305+
case CMD_DOWN:
306+
debug_backtrace_level--;
307+
do_checkbacktracelevel();
308+
continue;
253309
}
310+
/* Going out reset backtrace_level */
311+
debug_backtrace_level = 0;
254312
break;
255313
}
256314

@@ -285,6 +343,92 @@ do_debug(cmd)
285343
debug_did_msg = TRUE;
286344
}
287345

346+
static int
347+
get_maxbacktrace_level(void)
348+
{
349+
char *p, *q;
350+
int maxbacktrace = 1;
351+
352+
maxbacktrace = 0;
353+
if (sourcing_name != NULL)
354+
{
355+
p = (char *)sourcing_name;
356+
while ((q = strstr(p, "..")) != NULL)
357+
{
358+
p = q + 2;
359+
maxbacktrace++;
360+
}
361+
}
362+
return maxbacktrace;
363+
}
364+
365+
static void
366+
do_setdebugtracelevel(char_u *arg)
367+
{
368+
int level;
369+
370+
level = atoi((char *)arg);
371+
if (*arg == '+' || level < 0)
372+
debug_backtrace_level += level;
373+
else
374+
debug_backtrace_level = level;
375+
376+
do_checkbacktracelevel();
377+
}
378+
379+
static void
380+
do_checkbacktracelevel(void)
381+
{
382+
if (debug_backtrace_level < 0)
383+
{
384+
debug_backtrace_level = 0;
385+
MSG(_("frame is zero"));
386+
}
387+
else
388+
{
389+
int max = get_maxbacktrace_level();
390+
391+
if (debug_backtrace_level > max)
392+
{
393+
debug_backtrace_level = max;
394+
smsg((char_u *)_("frame at highest level: %d"), max);
395+
}
396+
}
397+
}
398+
399+
static void
400+
do_showbacktrace(char_u *cmd)
401+
{
402+
char *cur;
403+
char *next;
404+
int i = 0;
405+
int max = get_maxbacktrace_level();
406+
407+
if (sourcing_name != NULL)
408+
{
409+
cur = (char *)sourcing_name;
410+
while (!got_int)
411+
{
412+
next = strstr(cur, "..");
413+
if (next != NULL)
414+
*next = NUL;
415+
if (i == max - debug_backtrace_level)
416+
smsg((char_u *)"->%d %s", max - i, cur);
417+
else
418+
smsg((char_u *)" %d %s", max - i, cur);
419+
++i;
420+
if (next == NULL)
421+
break;
422+
*next = '.';
423+
cur = next + 2;
424+
}
425+
}
426+
if (sourcing_lnum != 0)
427+
smsg((char_u *)_("line %ld: %s"), (long)sourcing_lnum, cmd);
428+
else
429+
smsg((char_u *)_("cmd: %s"), cmd);
430+
}
431+
288432
/*
289433
* ":debug".
290434
*/

src/globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ EXTERN int ex_nesting_level INIT(= 0); /* nesting level */
231231
EXTERN int debug_break_level INIT(= -1); /* break below this level */
232232
EXTERN int debug_did_msg INIT(= FALSE); /* did "debug mode" message */
233233
EXTERN int debug_tick INIT(= 0); /* breakpoint change count */
234+
EXTERN int debug_backtrace_level INIT(= 0); /* breakpoint backtrace level */
234235
# ifdef FEAT_PROFILE
235236
EXTERN int do_profiling INIT(= PROF_NONE); /* PROF_ values */
236237
# endif

src/testdir/Make_all.mak

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ SCRIPTS_ALL = \
8989
test105.out \
9090
test106.out \
9191
test107.out \
92+
test108.out \
9293
test_argument_0count.out \
9394
test_argument_count.out \
9495
test_autocmd_option.out \

0 commit comments

Comments
 (0)