summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2015-11-10 11:45:59 +0100
committerantirez <antirez@gmail.com>2015-11-17 15:43:21 +0100
commit1f8d6144237e10fe91b71b883578a0083b95920c (patch)
treee1fd15614566a379630e88a1de7ddc82df7e60b5
parent5c4f492844d50c29ebf303bc9bd52c6db07fe377 (diff)
downloadredis-1f8d6144237e10fe91b71b883578a0083b95920c.tar.gz
Lua debugger: breakpoints.
-rw-r--r--src/redis-cli.c54
-rw-r--r--src/scripting.c106
2 files changed, 148 insertions, 12 deletions
diff --git a/src/redis-cli.c b/src/redis-cli.c
index 1eaf865bf..13da858dd 100644
--- a/src/redis-cli.c
+++ b/src/redis-cli.c
@@ -490,6 +490,51 @@ static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
return out;
}
+int isColorTerm(void) {
+ char *t = getenv("TERM");
+ return t != NULL && strstr(t,"xterm") != NULL;
+}
+
+/* Helpe function for sdsCatColorizedLdbReply() appending colorize strings
+ * to an SDS string. */
+sds sdscatcolor(sds o, char *s, size_t len, char *color) {
+ if (!isColorTerm()) return sdscatlen(o,s,len);
+
+ int bold = strstr(color,"bold") != NULL;
+ int ccode = 37; /* Defaults to white. */
+ if (strstr(color,"red")) ccode = 31;
+ else if (strstr(color,"red")) ccode = 31;
+ else if (strstr(color,"green")) ccode = 32;
+ else if (strstr(color,"yellow")) ccode = 33;
+ else if (strstr(color,"blue")) ccode = 34;
+ else if (strstr(color,"magenta")) ccode = 35;
+ else if (strstr(color,"cyan")) ccode = 36;
+ else if (strstr(color,"white")) ccode = 37;
+
+ o = sdscatfmt(o,"\033[%i;%i;49m",bold,ccode);
+ o = sdscatlen(o,s,len);
+ o = sdscat(o,"\033[0m");
+ return o;
+}
+
+/* Colorize Lua debugger status replies according to the prefix they
+ * have. */
+sds sdsCatColorizedLdbReply(sds o, char *s, size_t len) {
+ char *color = "white";
+
+ if (strstr(s,"<redis>")) color = "green";
+ if (strstr(s,"<reply>")) color = "cyan";
+ if (strstr(s,"<error>")) color = "red";
+ if (strstr(s,"<value>")) color = "magenta";
+ if (isdigit(s[0])) {
+ char *p = s+1;
+ while(isdigit(*p)) p++;
+ if (*p == '*') color = "yellow"; /* Current line. */
+ else if (*p == '#') color = "bold"; /* Break point. */
+ }
+ return sdscatcolor(o,s,len,color);
+}
+
static sds cliFormatReplyRaw(redisReply *r) {
sds out = sdsempty(), tmp;
size_t i;
@@ -504,7 +549,14 @@ static sds cliFormatReplyRaw(redisReply *r) {
break;
case REDIS_REPLY_STATUS:
case REDIS_REPLY_STRING:
- out = sdscatlen(out,r->str,r->len);
+ if (r->type == REDIS_REPLY_STATUS && config.eval_ldb) {
+ /* The Lua debugger replies with arrays of simple (status)
+ * strings. We colorize the output for more fun if this
+ * is a debugging session. */
+ out = sdsCatColorizedLdbReply(out,r->str,r->len);
+ } else {
+ out = sdscatlen(out,r->str,r->len);
+ }
break;
case REDIS_REPLY_INTEGER:
out = sdscatprintf(out,"%lld",r->integer);
diff --git a/src/scripting.c b/src/scripting.c
index c3a8aea08..f33e6c332 100644
--- a/src/scripting.c
+++ b/src/scripting.c
@@ -1602,8 +1602,39 @@ char *ldbGetSourceLine(int line) {
return ldb.src[idx];
}
+/* Return true if there is a breakpoint in the specified line. */
int ldbIsBreakpoint(int line) {
- /* TODO: fixme. */
+ int j;
+
+ for (j = 0; j < ldb.bpcount; j++)
+ if (ldb.bp[j] == line) return 1;
+ return 0;
+}
+
+/* Add the specified breakpoint. Ignore it if we already reached the max.
+ * Returns 1 if the breakpoint was added (or was already set). 0 if there is
+ * no space for the breakpoint or if the line is invalid. */
+int ldbAddBreakpoint(int line) {
+ if (line <= 0 || line > ldb.lines) return 0;
+ if (!ldbIsBreakpoint(line) && ldb.bpcount != LDB_BREAKPOINTS_MAX) {
+ ldb.bp[ldb.bpcount++] = line;
+ return 1;
+ }
+ return 0;
+}
+
+/* Remove the specified breakpoint, returning 1 if the operation was
+ * performed or 0 if there was no such breakpoint. */
+int ldbDelBreakpoint(int line) {
+ int j;
+
+ for (j = 0; j < ldb.bpcount; j++) {
+ if (ldb.bp[j] == line) {
+ ldb.bpcount--;
+ memmove(ldb.bp+j,ldb.bp+j+1,ldb.bpcount-j);
+ return 1;
+ }
+ }
return 0;
}
@@ -1668,10 +1699,10 @@ void ldbList(int around, int context) {
if (around != 0 && abs(around-j) > context) continue;
char *line = ldbGetSourceLine(j);
int mark;
- if (ldbIsBreakpoint(j))
- mark = '#';
+ if (ldb.currentline == j)
+ mark = '*';
else
- mark = (ldb.currentline == j) ? '*' : ':';
+ mark = ldbIsBreakpoint(j) ? '#' : ':';
sds thisline = sdscatprintf(sdsempty(),"%d%c %s", j, mark, line);
ldbLog(thisline);
}
@@ -1723,8 +1754,9 @@ void ldbLogStackValue(lua_State *lua) {
/* Implements the "print" command of the Lua debugger. It scans for Lua
* var "varname" starting from the current stack frame up to the top stack
* frame. The first matching variable is printed. */
-void ldbPrint(lua_State *lua, lua_Debug *orig_ar, char *varname) {
+void ldbPrint(lua_State *lua, char *varname) {
lua_Debug ar;
+
int l = 0; /* Stack level. */
while (lua_getstack(lua,l,&ar) != 0) {
l++;
@@ -1743,8 +1775,52 @@ void ldbPrint(lua_State *lua, lua_Debug *orig_ar, char *varname) {
ldbLog(sdsnew("No such variable."));
}
+/* Implements the break command to list, add and remove breakpoints. */
+void ldbBreak(sds *argv, int argc) {
+ if (argc == 1) {
+ if (ldb.bpcount == 0) {
+ ldbLog(sdsnew("No breakpoints set. Use 'b <line>' to add one."));
+ return;
+ } else {
+ ldbLog(sdscatfmt(sdsempty(),"%i breakpoints set:",ldb.bpcount));
+ int j;
+ for (j = 0; j < ldb.bpcount; j++) {
+ ldbLog(sdscatfmt(sdsempty(),"%i# %s", ldb.bp[j],
+ ldbGetSourceLine(ldb.bp[j])));
+ }
+ }
+ } else {
+ int j;
+ for (j = 1; j < argc; j++) {
+ char *arg = argv[j];
+ long line;
+ if (!string2l(arg,sdslen(arg),&line)) {
+ ldbLog(sdscatfmt(sdsempty(),"Invalid argument:'%s'",arg));
+ } else {
+ if (line == 0) {
+ ldb.bpcount = 0;
+ ldbLog(sdsnew("All breakpoints removed."));
+ } else if (line > 0) {
+ if (ldb.bpcount == LDB_BREAKPOINTS_MAX) {
+ ldbLog(sdsnew("Too many breakpoints set."));
+ } else if (ldbAddBreakpoint(line)) {
+ ldbList(line,1);
+ } else {
+ ldbLog(sdsnew("Wrong line number."));
+ }
+ } else if (line < 0) {
+ if (ldbDelBreakpoint(line))
+ ldbLog(sdsnew("Breakpoint removed."));
+ else
+ ldbLog(sdsnew("No breakpoint in the specified line."));
+ }
+ }
+ }
+ }
+}
+
/* Read debugging commands from client. */
-void ldbRepl(lua_State *lua, lua_Debug *ar) {
+void ldbRepl(lua_State *lua) {
sds *argv;
int argc;
@@ -1777,6 +1853,10 @@ ldbLog(sdsnew("[c]continue Run till next breakpoint."));
ldbLog(sdsnew("[l]list [line] List source code, around [line] if specified"));
ldbLog(sdsnew(" you can use another arg for context size."));
ldbLog(sdsnew("[p]rint <var> Show the value of the specified local variable."));
+ldbLog(sdsnew("[b]eark Show all breakpoints."));
+ldbLog(sdsnew("[b]eark <line> Add a breakpoint to the specified line."));
+ldbLog(sdsnew("[b]eark -<line> Remove breakpoint from the specified line."));
+ldbLog(sdsnew("[b]eark 0 Remove all breakpoints."));
ldbSendLogs();
} else if (!strcasecmp(argv[0],"s") || !strcasecmp(argv[0],"step") ||
!strcasecmp(argv[0],"n") || !strcasecmp(argv[0],"next")) {
@@ -1784,10 +1864,13 @@ ldbLog(sdsnew("[p]rint <var> Show the value of the specified local variable.")
break;
} else if (!strcasecmp(argv[0],"c") || !strcasecmp(argv[0],"continue")){
break;
+ } else if (!strcasecmp(argv[0],"b") || !strcasecmp(argv[0],"break")){
+ ldbBreak(argv,argc);
+ ldbSendLogs();
} else if (argc == 2 &&
(!strcasecmp(argv[0],"p") || !strcasecmp(argv[0],"print")))
{
- ldbPrint(lua,ar,argv[1]);
+ ldbPrint(lua,argv[1]);
ldbSendLogs();
} else if (!strcasecmp(argv[0],"l") || !strcasecmp(argv[0],"list")){
int around = 0, ctx = 5;
@@ -1816,13 +1899,14 @@ void luaLdbLineHook(lua_State *lua, lua_Debug *ar) {
lua_getinfo(lua,"Sl",ar);
if(strstr(ar->short_src,"user_script") == NULL) return;
- if (ldb.step) {
+ if (ldb.step || ldbIsBreakpoint(ar->currentline)) {
ldb.currentline = ar->currentline;
ldb.step = 0;
- ldbLog(sdscatprintf(sdsempty(),"%d: %s", (int)ar->currentline,
- ldbGetSourceLine(ar->currentline)));
+ int mark = ldbIsBreakpoint(ldb.currentline) ? '#' : '*';
+ ldbLog(sdscatprintf(sdsempty(),"%d%c %s", (int)ar->currentline,
+ mark, ldbGetSourceLine(ar->currentline)));
ldbSendLogs();
- ldbRepl(lua,ar);
+ ldbRepl(lua);
}
}