aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/debugger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/debugger.c')
-rw-r--r--src/nvim/debugger.c271
1 files changed, 150 insertions, 121 deletions
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 36df62b502..3dfbcd1f81 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -5,17 +5,33 @@
///
/// Vim script debugger functions
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "nvim/ascii.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
+#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/keycodes.h"
+#include "nvim/macros.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
#include "nvim/os/os.h"
+#include "nvim/path.h"
#include "nvim/pos.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
@@ -33,7 +49,7 @@ static char *debug_newval = NULL;
struct debuggy {
int dbg_nr; ///< breakpoint number
int dbg_type; ///< DBG_FUNC or DBG_FILE or DBG_EXPR
- char_u *dbg_name; ///< function, expression or file name
+ char *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
@@ -60,7 +76,7 @@ void do_debug(char *cmd)
bool typeahead_saved = false;
int save_ignore_script = 0;
int n;
- char_u *cmdline = NULL;
+ char *cmdline = NULL;
char *p;
char *tail = NULL;
static int last_cmd = 0;
@@ -114,6 +130,7 @@ void do_debug(char *cmd)
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
@@ -128,10 +145,15 @@ void do_debug(char *cmd)
ignore_script = true;
}
+ // don't debug any function call, e.g. from an expression mapping
+ n = debug_break_level;
+ debug_break_level = -1;
+
xfree(cmdline);
- cmdline = (char_u *)getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
- CALLBACK_NONE);
+ cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE);
+ debug_break_level = n;
if (typeahead_saved) {
restore_typeahead(&typeaheadbuf);
ignore_script = save_ignore_script;
@@ -144,7 +166,7 @@ void do_debug(char *cmd)
// If this is a debug command, set "last_cmd".
// If not, reset "last_cmd".
// For a blank line use previous command.
- p = skipwhite((char *)cmdline);
+ p = skipwhite(cmdline);
if (*p != NUL) {
switch (*p) {
case 'c':
@@ -239,14 +261,14 @@ void do_debug(char *cmd)
last_cmd = CMD_STEP;
break;
case CMD_BACKTRACE:
- do_showbacktrace((char_u *)cmd);
+ do_showbacktrace(cmd);
continue;
case CMD_FRAME:
if (*p == NUL) {
- do_showbacktrace((char_u *)cmd);
+ do_showbacktrace(cmd);
} else {
p = skipwhite(p);
- do_setdebugtracelevel((char_u *)p);
+ do_setdebugtracelevel(p);
}
continue;
case CMD_UP:
@@ -266,7 +288,7 @@ void do_debug(char *cmd)
// don't debug this command
n = debug_break_level;
debug_break_level = -1;
- (void)do_cmdline((char *)cmdline, getexline, NULL, DOCMD_VERBOSE|DOCMD_EXCRESET);
+ (void)do_cmdline(cmdline, getexline, NULL, DOCMD_VERBOSE|DOCMD_EXCRESET);
debug_break_level = n;
}
lines_left = Rows - 1;
@@ -295,20 +317,22 @@ static int get_maxbacktrace_level(char *sname)
{
int maxbacktrace = 0;
- if (sname != NULL) {
- char *p = sname;
- char *q;
- while ((q = strstr(p, "..")) != NULL) {
- p = q + 2;
- maxbacktrace++;
- }
+ if (sname == NULL) {
+ return 0;
+ }
+
+ char *p = sname;
+ char *q;
+ while ((q = strstr(p, "..")) != NULL) {
+ p = q + 2;
+ maxbacktrace++;
}
return maxbacktrace;
}
-static void do_setdebugtracelevel(char_u *arg)
+static void do_setdebugtracelevel(char *arg)
{
- int level = atoi((char *)arg);
+ int level = atoi(arg);
if (*arg == '+' || level < 0) {
debug_backtrace_level += level;
} else {
@@ -335,7 +359,7 @@ static void do_checkbacktracelevel(void)
}
}
-static void do_showbacktrace(char_u *cmd)
+static void do_showbacktrace(char *cmd)
{
char *sname = estack_sfile(ESTACK_NONE);
int max = get_maxbacktrace_level(sname);
@@ -436,20 +460,21 @@ void dbg_check_breakpoint(exarg_T *eap)
/// @return true when the debug mode is entered this time.
bool dbg_check_skipped(exarg_T *eap)
{
- if (debug_skipped) {
- // Save the value of got_int and reset it. We don't want a previous
- // interruption cause flushing the input buffer.
- int prev_got_int = got_int;
- got_int = false;
- debug_breakpoint_name = debug_skipped_name;
- // eap->skip is true
- eap->skip = false;
- dbg_check_breakpoint(eap);
- eap->skip = true;
- got_int |= prev_got_int;
- return true;
- }
- return false;
+ if (!debug_skipped) {
+ return false;
+ }
+
+ // Save the value of got_int and reset it. We don't want a previous
+ // interruption cause flushing the input buffer.
+ int prev_got_int = got_int;
+ got_int = false;
+ debug_breakpoint_name = debug_skipped_name;
+ // eap->skip is true
+ eap->skip = false;
+ dbg_check_breakpoint(eap);
+ eap->skip = true;
+ got_int |= prev_got_int;
+ return true;
}
static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL };
@@ -470,7 +495,7 @@ static typval_T *eval_expr_no_emsg(struct debuggy *const bp)
{
// Disable error messages, a bad expression would make Vim unusable.
emsg_off++;
- typval_T *const tv = eval_expr((char *)bp->dbg_name);
+ typval_T *const tv = eval_expr(bp->dbg_name);
emsg_off--;
return tv;
}
@@ -482,9 +507,9 @@ static typval_T *eval_expr_no_emsg(struct debuggy *const bp)
///
/// @param arg
/// @param gap either &dbg_breakp or &prof_ga
-static int dbg_parsearg(char_u *arg, garray_T *gap)
+static int dbg_parsearg(char *arg, garray_T *gap)
{
- char *p = (char *)arg;
+ char *p = arg;
char *q;
bool here = false;
@@ -493,18 +518,18 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
// Find "func" or "file".
- if (STRNCMP(p, "func", 4) == 0) {
+ if (strncmp(p, "func", 4) == 0) {
bp->dbg_type = DBG_FUNC;
- } else if (STRNCMP(p, "file", 4) == 0) {
+ } else if (strncmp(p, "file", 4) == 0) {
bp->dbg_type = DBG_FILE;
- } else if (gap != &prof_ga && STRNCMP(p, "here", 4) == 0) {
+ } else if (gap != &prof_ga && strncmp(p, "here", 4) == 0) {
if (curbuf->b_ffname == NULL) {
emsg(_(e_noname));
return FAIL;
}
bp->dbg_type = DBG_FILE;
here = true;
- } else if (gap != &prof_ga && STRNCMP(p, "expr", 4) == 0) {
+ } else if (gap != &prof_ga && strncmp(p, "expr", 4) == 0) {
bp->dbg_type = DBG_EXPR;
} else {
semsg(_(e_invarg2), p);
@@ -531,11 +556,11 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
}
if (bp->dbg_type == DBG_FUNC) {
- bp->dbg_name = vim_strsave((char_u *)p);
+ bp->dbg_name = xstrdup(p);
} else if (here) {
- bp->dbg_name = vim_strsave((char_u *)curbuf->b_ffname);
+ bp->dbg_name = xstrdup(curbuf->b_ffname);
} else if (bp->dbg_type == DBG_EXPR) {
- bp->dbg_name = vim_strsave((char_u *)p);
+ bp->dbg_name = xstrdup(p);
bp->dbg_val = eval_expr_no_emsg(bp);
} else {
// Expand the file name in the same way as do_source(). This means
@@ -551,10 +576,10 @@ static int dbg_parsearg(char_u *arg, garray_T *gap)
return FAIL;
}
if (*p != '*') {
- bp->dbg_name = (char_u *)fix_fname(p);
+ bp->dbg_name = fix_fname(p);
xfree(p);
} else {
- bp->dbg_name = (char_u *)p;
+ bp->dbg_name = p;
}
}
@@ -572,33 +597,35 @@ void ex_breakadd(exarg_T *eap)
gap = &prof_ga;
}
- if (dbg_parsearg((char_u *)eap->arg, gap) == OK) {
- struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
- bp->dbg_forceit = eap->forceit;
+ if (dbg_parsearg(eap->arg, gap) != OK) {
+ return;
+ }
- if (bp->dbg_type != DBG_EXPR) {
- char *pat = file_pat_to_reg_pat((char *)bp->dbg_name, NULL, NULL, false);
- if (pat != NULL) {
- bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
- xfree(pat);
+ struct debuggy *bp = &DEBUGGY(gap, gap->ga_len);
+ bp->dbg_forceit = eap->forceit;
+
+ if (bp->dbg_type != DBG_EXPR) {
+ char *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);
+ xfree(pat);
+ }
+ if (pat == NULL || bp->dbg_prog == NULL) {
+ xfree(bp->dbg_name);
+ } else {
+ if (bp->dbg_lnum == 0) { // default line number is 1
+ bp->dbg_lnum = 1;
}
- if (pat == NULL || bp->dbg_prog == NULL) {
- xfree(bp->dbg_name);
- } else {
- if (bp->dbg_lnum == 0) { // default line number is 1
- bp->dbg_lnum = 1;
- }
- if (eap->cmdidx != CMD_profile) {
- DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
- debug_tick++;
- }
- gap->ga_len++;
+ if (eap->cmdidx != CMD_profile) {
+ DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
+ debug_tick++;
}
- } else {
- // DBG_EXPR
- 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++;
}
}
@@ -639,14 +666,14 @@ void ex_breakdel(exarg_T *eap)
del_all = true;
} else {
// ":breakdel {func|file|expr} [lnum] {name}"
- if (dbg_parsearg((char_u *)eap->arg, gap) == FAIL) {
+ if (dbg_parsearg(eap->arg, gap) == FAIL) {
return;
}
bp = &DEBUGGY(gap, gap->ga_len);
for (int 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
+ && strcmp(bp->dbg_name, bpi->dbg_name) == 0
&& (bp->dbg_lnum == bpi->dbg_lnum
|| (bp->dbg_lnum == 0
&& (best_lnum == 0
@@ -660,32 +687,33 @@ void ex_breakdel(exarg_T *eap)
if (todel < 0) {
semsg(_("E161: Breakpoint not found: %s"), eap->arg);
- } else {
- while (!GA_EMPTY(gap)) {
- xfree(DEBUGGY(gap, todel).dbg_name);
- if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
- && DEBUGGY(gap, todel).dbg_val != NULL) {
- tv_free(DEBUGGY(gap, todel).dbg_val);
- }
- vim_regfree(DEBUGGY(gap, todel).dbg_prog);
- gap->ga_len--;
- if (todel < gap->ga_len) {
- memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
- (size_t)(gap->ga_len - todel) * sizeof(struct debuggy));
- }
- if (eap->cmdidx == CMD_breakdel) {
- debug_tick++;
- }
- if (!del_all) {
- break;
- }
- }
+ return;
+ }
- // If all breakpoints were removed clear the array.
- if (GA_EMPTY(gap)) {
- ga_clear(gap);
+ while (!GA_EMPTY(gap)) {
+ xfree(DEBUGGY(gap, todel).dbg_name);
+ if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
+ && DEBUGGY(gap, todel).dbg_val != NULL) {
+ tv_free(DEBUGGY(gap, todel).dbg_val);
+ }
+ vim_regfree(DEBUGGY(gap, todel).dbg_prog);
+ gap->ga_len--;
+ if (todel < gap->ga_len) {
+ memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
+ (size_t)(gap->ga_len - todel) * sizeof(struct debuggy));
+ }
+ if (eap->cmdidx == CMD_breakdel) {
+ debug_tick++;
+ }
+ if (!del_all) {
+ break;
}
}
+
+ // If all breakpoints were removed clear the array.
+ if (GA_EMPTY(gap)) {
+ ga_clear(gap);
+ }
}
/// ":breaklist".
@@ -693,21 +721,22 @@ void ex_breaklist(exarg_T *eap)
{
if (GA_EMPTY(&dbg_breakp)) {
msg(_("No breakpoints defined"));
- } else {
- for (int i = 0; i < dbg_breakp.ga_len; i++) {
- struct debuggy *bp = &BREAKP(i);
- if (bp->dbg_type == DBG_FILE) {
- home_replace(NULL, (char *)bp->dbg_name, (char *)NameBuff, MAXPATHL, true);
- }
- if (bp->dbg_type != DBG_EXPR) {
- smsg(_("%3d %s %s line %" PRId64),
- bp->dbg_nr,
- bp->dbg_type == DBG_FUNC ? "func" : "file",
- bp->dbg_type == DBG_FUNC ? bp->dbg_name : (char_u *)NameBuff,
- (int64_t)bp->dbg_lnum);
- } else {
- smsg(_("%3d expr %s"), bp->dbg_nr, bp->dbg_name);
- }
+ return;
+ }
+
+ for (int i = 0; i < dbg_breakp.ga_len; i++) {
+ struct debuggy *bp = &BREAKP(i);
+ if (bp->dbg_type == DBG_FILE) {
+ home_replace(NULL, bp->dbg_name, (char *)NameBuff, MAXPATHL, true);
+ }
+ if (bp->dbg_type != DBG_EXPR) {
+ smsg(_("%3d %s %s line %" PRId64),
+ bp->dbg_nr,
+ bp->dbg_type == DBG_FUNC ? "func" : "file",
+ bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
+ (int64_t)bp->dbg_lnum);
+ } else {
+ smsg(_("%3d expr %s"), bp->dbg_nr, bp->dbg_name);
}
}
}
@@ -720,7 +749,7 @@ void ex_breaklist(exarg_T *eap)
/// @param after after this line number
linenr_T dbg_find_breakpoint(bool file, char *fname, linenr_T after)
{
- return debuggy_find(file, (char_u *)fname, after, &dbg_breakp, NULL);
+ return debuggy_find(file, fname, after, &dbg_breakp, NULL);
}
/// @param file true for a file, false for a function
@@ -730,7 +759,7 @@ linenr_T dbg_find_breakpoint(bool file, char *fname, linenr_T after)
/// @returns true if profiling is on for a function or sourced file.
bool has_profiling(bool file, char *fname, bool *fp)
{
- return debuggy_find(file, (char_u *)fname, (linenr_T)0, &prof_ga, fp)
+ return debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
!= (linenr_T)0;
}
@@ -741,11 +770,11 @@ bool has_profiling(bool file, char *fname, bool *fp)
/// @param after after this line number
/// @param gap either &dbg_breakp or &prof_ga
/// @param fp if not NULL: return forceit
-static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T *gap, bool *fp)
+static linenr_T debuggy_find(bool file, char *fname, linenr_T after, garray_T *gap, bool *fp)
{
struct debuggy *bp;
linenr_T lnum = 0;
- char_u *name = fname;
+ char *name = fname;
int prev_got_int;
// Return quickly when there are no breakpoints.
@@ -754,8 +783,8 @@ static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T
}
// Replace K_SNR in function name with "<SNR>".
- if (!file && fname[0] == K_SPECIAL) {
- name = xmalloc(STRLEN(fname) + 3);
+ if (!file && (uint8_t)fname[0] == K_SPECIAL) {
+ name = xmalloc(strlen(fname) + 3);
STRCPY(name, "<SNR>");
STRCPY(name + 5, fname + 3);
}
@@ -773,7 +802,7 @@ static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T
// while matching should abort it.
prev_got_int = got_int;
got_int = false;
- if (vim_regexec_prog(&bp->dbg_prog, false, name, (colnr_T)0)) {
+ if (vim_regexec_prog(&bp->dbg_prog, false, (char_u *)name, (colnr_T)0)) {
lnum = bp->dbg_lnum;
if (fp != NULL) {
*fp = bp->dbg_forceit;
@@ -786,26 +815,26 @@ static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T
typval_T *const tv = eval_expr_no_emsg(bp);
if (tv != NULL) {
if (bp->dbg_val == NULL) {
- debug_oldval = typval_tostring(NULL);
+ debug_oldval = typval_tostring(NULL, true);
bp->dbg_val = tv;
- debug_newval = typval_tostring(bp->dbg_val);
+ debug_newval = typval_tostring(bp->dbg_val, true);
line = true;
} else {
if (typval_compare(tv, bp->dbg_val, EXPR_IS, false) == OK
&& tv->vval.v_number == false) {
line = true;
- debug_oldval = typval_tostring(bp->dbg_val);
+ debug_oldval = typval_tostring(bp->dbg_val, true);
// Need to evaluate again, typval_compare() overwrites "tv".
typval_T *const v = eval_expr_no_emsg(bp);
- debug_newval = typval_tostring(v);
+ debug_newval = typval_tostring(v, true);
tv_free(bp->dbg_val);
bp->dbg_val = v;
}
tv_free(tv);
}
} else if (bp->dbg_val != NULL) {
- debug_oldval = typval_tostring(bp->dbg_val);
- debug_newval = typval_tostring(NULL);
+ debug_oldval = typval_tostring(bp->dbg_val, true);
+ debug_newval = typval_tostring(NULL, true);
tv_free(bp->dbg_val);
bp->dbg_val = NULL;
line = true;