Lua debugger: log Redis commands. List command.

This commit is contained in:
antirez 2015-11-09 17:01:41 +01:00
parent d3d1fa9437
commit 5417217c87
2 changed files with 82 additions and 7 deletions

View File

@ -381,6 +381,7 @@ static int cliConnect(int force) {
* debugging session. */
if (config.eval_ldb) {
config.eval_ldb = 0;
config.output = OUTPUT_STANDARD;
cliRefreshPrompt();
}
}
@ -834,6 +835,7 @@ static int parseOptions(int argc, char **argv) {
config.eval = argv[++i];
} else if (!strcmp(argv[i],"--ldb")) {
config.eval_ldb = 1;
config.output = OUTPUT_RAW;
} else if (!strcmp(argv[i],"-c")) {
config.cluster_mode = 1;
} else if (!strcmp(argv[i],"-d") && !lastarg) {

View File

@ -50,6 +50,7 @@ void ldbDisable(client *c);
void ldbEnable(client *c);
void evalGenericCommandWithDebugging(client *c, int evalsha);
void luaLdbLineHook(lua_State *lua, lua_Debug *ar);
void ldbLog(sds entry);
/* Debugger shared state is stored inside this global structure. */
#define LDB_BREAKPOINTS_MAX 64
@ -64,6 +65,7 @@ struct ldbState {
int step; /* Stop at next line ragardless of breakpoints. */
sds *src; /* Lua script source code split by line. */
int lines; /* Number of lines in 'src'. */
int currentline; /* Current line number. */
sds cbuf; /* Debugger client command buffer. */
} ldb;
@ -211,6 +213,12 @@ char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply) {
void luaPushError(lua_State *lua, char *error) {
lua_Debug dbg;
/* If debugging is active and in step mode, log errors resulting from
* Redis commands. */
if (ldb.active && ldb.step) {
ldbLog(sdscatprintf(sdsempty(),"<error> %s",error));
}
lua_newtable(lua);
lua_pushstring(lua,"err");
@ -433,6 +441,21 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
c->argv = argv;
c->argc = argc;
/* Log the command if debugging is active. */
if (ldb.active && ldb.step) {
sds cmdlog = sdsnew("<redis>");
for (j = 0; j < c->argc; j++) {
if (j == 10) {
cmdlog = sdscatprintf(cmdlog," ... (%d more)",
c->argc-j-1);
} else {
cmdlog = sdscatlen(cmdlog," ",1);
cmdlog = sdscatsds(cmdlog,c->argv[j]->ptr);
}
}
ldbLog(cmdlog);
}
/* Command lookup */
cmd = lookupCommand(argv[0]->ptr);
if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) ||
@ -554,6 +577,15 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
}
if (raise_error && reply[0] != '-') raise_error = 0;
redisProtocolToLuaType(lua,reply);
/* If the debugger is active, log the reply from Redis. */
if (ldb.active && ldb.step) {
sds replycopy = sdsnew("<reply> ");
replycopy = sdscat(replycopy,reply); /* It's always null terminated. */
if (sdslen(replycopy) > 70) sdsrange(replycopy,0,69);
ldbLog(replycopy);
}
/* Sort the output array if needed, assuming it is a non-null multi bulk
* reply as expected. */
if ((cmd->flags & CMD_SORT_FOR_SCRIPT) &&
@ -1511,10 +1543,19 @@ int ldbStartSession(client *c) {
anetBlock(NULL,ldb.fd);
anetSendTimeout(NULL,ldb.fd,5000);
ldb.active = 1;
/* First argument of EVAL is the script itself. We split it into different
* lines since this is the way the debugger accesses the source code. */
sds srcstring = c->argv[1]->ptr;
sds srcstring = sdsdup(c->argv[1]->ptr);
size_t srclen = sdslen(srcstring);
while(srclen && (srcstring[srclen-1] == '\n' ||
srcstring[srclen-1] == '\r'))
{
srcstring[--srclen] = '\0';
}
sdssetlen(srcstring,srclen);
ldb.src = sdssplitlen(srcstring,sdslen(srcstring),"\n",1,&ldb.lines);
sdsfree(srcstring);
return 1;
}
@ -1558,7 +1599,7 @@ void evalGenericCommandWithDebugging(client *c, int evalsha) {
char *ldbGetSourceLine(int line) {
int idx = line-1;
if (idx < 0 || idx >= ldb.lines) return "<out of range source code line>";
return ldb.src[line];
return ldb.src[idx];
}
/* Expect a valid multi-bulk command in the debugging client query buffer.
@ -1610,6 +1651,27 @@ protoerr:
return NULL;
}
/* Implement the "list" command of the Lua debugger. If around is 0
* the whole file is listed, otherwise only a small portion of the file
* around the specified line is shown. When a line number is specified
* the amonut of context (lines before/after) is specified via the
* 'context' argument. */
void ldbList(int around, int context) {
int j;
for (j = 1; j <= ldb.lines; j++) {
if (around != 0 && abs(around-j) > context) continue;
char *line = ldbGetSourceLine(j);
int mark;
if (ldbIsBreakpoint(j))
mark = '#';
else
mark = (ldb.currentline == j) ? '*' : ':';
sds thisline = sdscatprintf(sdsempty(),"%d%c %s", j, mark, line);
ldbLog(thisline);
}
}
/* Read debugging commands from client. */
void ldbRepl(void) {
sds *argv;
@ -1637,15 +1699,25 @@ void ldbRepl(void) {
/* Execute the command. */
if (!strcasecmp(argv[0],"help")) {
ldbLog(sdsnew("Redis Lua debugger help:"));
ldbLog(sdsnew("[s]tep : run current line and stop again."));
ldbLog(sdsnew("[c]continue: run till next breakpoint."));
ldbLog(sdsnew("Redis Lua debugger help:"));
ldbLog(sdsnew("[s]tep run current line and stop again."));
ldbLog(sdsnew("[n]ext alias for step."));
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."));
ldbSendLogs();
} else if (!strcasecmp(argv[0],"s") || !strcasecmp(argv[0],"step")) {
} else if (!strcasecmp(argv[0],"s") || !strcasecmp(argv[0],"step") ||
!strcasecmp(argv[0],"n") || !strcasecmp(argv[0],"next")) {
ldb.step = 1;
break;
} else if (!strcasecmp(argv[0],"c") || !strcasecmp(argv[0],"continue")){
break;
} else if (!strcasecmp(argv[0],"l") || !strcasecmp(argv[0],"list")){
int around = 0, ctx = 5;
if (argc > 1) around = atoi(argv[1]);
if (argc > 2) ctx = atoi(argv[2]);
ldbList(around,ctx);
ldbSendLogs();
} else {
ldbLog(sdsnew("Unknown Redis Lua debugger command."));
ldbSendLogs();
@ -1667,9 +1739,10 @@ void luaLdbLineHook(lua_State *lua, lua_Debug *ar) {
if(strstr(ar->short_src,"user_script") == NULL) return;
if (ldb.step) {
ldb.currentline = ar->currentline;
ldb.step = 0;
ldbLog(sdscatprintf(sdsempty(),"%d: %s", (int)ar->currentline,
ldbGetSourceLine(ar->currentline-1)));
ldbGetSourceLine(ar->currentline)));
ldbSendLogs();
ldbRepl();
}