Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
d3d1fa94
Commit
d3d1fa94
authored
Nov 09, 2015
by
antirez
Browse files
Lua debugger: initial REPL.
parent
c494db89
Changes
2
Hide whitespace changes
Inline
Side-by-side
src/redis-cli.c
View file @
d3d1fa94
...
@@ -375,8 +375,15 @@ static int cliSelect(void) {
...
@@ -375,8 +375,15 @@ static int cliSelect(void) {
* even if there is already a connected socket. */
* even if there is already a connected socket. */
static
int
cliConnect
(
int
force
)
{
static
int
cliConnect
(
int
force
)
{
if
(
context
==
NULL
||
force
)
{
if
(
context
==
NULL
||
force
)
{
if
(
context
!=
NULL
)
if
(
context
!=
NULL
)
{
redisFree
(
context
);
redisFree
(
context
);
/* Disconnection from the server signals end of EVAL
* debugging session. */
if
(
config
.
eval_ldb
)
{
config
.
eval_ldb
=
0
;
cliRefreshPrompt
();
}
}
if
(
config
.
hostsocket
==
NULL
)
{
if
(
config
.
hostsocket
==
NULL
)
{
context
=
redisConnect
(
config
.
hostip
,
config
.
hostport
);
context
=
redisConnect
(
config
.
hostip
,
config
.
hostport
);
...
@@ -637,7 +644,8 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
...
@@ -637,7 +644,8 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
size_t
*
argvlen
;
size_t
*
argvlen
;
int
j
,
output_raw
;
int
j
,
output_raw
;
if
(
!
strcasecmp
(
command
,
"help"
)
||
!
strcasecmp
(
command
,
"?"
))
{
if
(
!
config
.
eval_ldb
&&
/* In debugging mode, let's pass "help" to Redis. */
(
!
strcasecmp
(
command
,
"help"
)
||
!
strcasecmp
(
command
,
"?"
)))
{
cliOutputHelp
(
--
argc
,
++
argv
);
cliOutputHelp
(
--
argc
,
++
argv
);
return
REDIS_OK
;
return
REDIS_OK
;
}
}
...
...
src/scripting.c
View file @
d3d1fa94
...
@@ -62,7 +62,9 @@ struct ldbState {
...
@@ -62,7 +62,9 @@ struct ldbState {
int
bp
[
LDB_BREAKPOINTS_MAX
];
/* An array of breakpoints line numbers. */
int
bp
[
LDB_BREAKPOINTS_MAX
];
/* An array of breakpoints line numbers. */
int
bpcount
;
/* Number of valid entries inside bp. */
int
bpcount
;
/* Number of valid entries inside bp. */
int
step
;
/* Stop at next line ragardless of breakpoints. */
int
step
;
/* Stop at next line ragardless of breakpoints. */
robj
*
src
;
/* Lua script source code. */
sds
*
src
;
/* Lua script source code split by line. */
int
lines
;
/* Number of lines in 'src'. */
sds
cbuf
;
/* Debugger client command buffer. */
}
ldb
;
}
ldb
;
/* ---------------------------------------------------------------------------
/* ---------------------------------------------------------------------------
...
@@ -1424,10 +1426,10 @@ void ldbInit(void) {
...
@@ -1424,10 +1426,10 @@ void ldbInit(void) {
ldb
.
fd
=
-
1
;
ldb
.
fd
=
-
1
;
ldb
.
active
=
0
;
ldb
.
active
=
0
;
ldb
.
logs
=
listCreate
();
ldb
.
logs
=
listCreate
();
ldb
.
traces
=
listCreate
();
listSetFreeMethod
(
ldb
.
logs
,(
void
(
*
)(
void
*
))
sdsfree
);
listSetFreeMethod
(
ldb
.
logs
,(
void
(
*
)(
void
*
))
sdsfree
);
listSetFreeMethod
(
ldb
.
traces
,
(
void
(
*
)(
void
*
))
sdsfree
);
ldb
.
src
=
NULL
;
ldb
.
src
=
NULL
;
ldb
.
lines
=
0
;
ldb
.
cbuf
=
sdsempty
();
}
}
/* Remove all the pending messages in the specified list. */
/* Remove all the pending messages in the specified list. */
...
@@ -1442,10 +1444,11 @@ void ldbFlushLog(list *log) {
...
@@ -1442,10 +1444,11 @@ void ldbFlushLog(list *log) {
void
ldbEnable
(
client
*
c
)
{
void
ldbEnable
(
client
*
c
)
{
c
->
flags
|=
CLIENT_LUA_DEBUG
;
c
->
flags
|=
CLIENT_LUA_DEBUG
;
ldbFlushLog
(
ldb
.
logs
);
ldbFlushLog
(
ldb
.
logs
);
ldbFlushLog
(
ldb
.
traces
);
ldb
.
fd
=
c
->
fd
;
ldb
.
fd
=
c
->
fd
;
ldb
.
step
=
0
;
ldb
.
step
=
1
;
ldb
.
bpcount
=
0
;
ldb
.
bpcount
=
0
;
sdsfree
(
ldb
.
cbuf
);
ldb
.
cbuf
=
sdsempty
();
}
}
void
ldbDisable
(
client
*
c
)
{
void
ldbDisable
(
client
*
c
)
{
...
@@ -1453,14 +1456,25 @@ void ldbDisable(client *c) {
...
@@ -1453,14 +1456,25 @@ void ldbDisable(client *c) {
}
}
/* Append a log entry to the specified LDB log. */
/* Append a log entry to the specified LDB log. */
void
ldbLog
(
list
*
log
,
sds
entry
)
{
void
ldbLog
(
sds
entry
)
{
listAddNodeTail
(
log
,
entry
);
listAddNodeTail
(
ldb
.
log
s
,
entry
);
}
}
/* Send ldb.logs and ldb.traces to the debugging client as a multi-bulk
/* Send ldb.logs to the debugging client as a multi-bulk reply
* reply consisting of simple strings. Log entries which include newlines
* consisting of simple strings. Log entries which include newlines have them
* have them replaced with spaces. The entries sent are also consumed. */
* replaced with spaces. The entries sent are also consumed. */
void
ldbWriteLogs
(
void
)
{
void
ldbSendLogs
(
void
)
{
sds
proto
=
sdsempty
();
proto
=
sdscatfmt
(
proto
,
"*%i
\r\n
"
,
(
int
)
listLength
(
ldb
.
logs
));
while
(
listLength
(
ldb
.
logs
))
{
listNode
*
ln
=
listFirst
(
ldb
.
logs
);
proto
=
sdscatlen
(
proto
,
"+"
,
1
);
sdsmapchars
(
ln
->
value
,
"
\r\n
"
,
" "
,
2
);
proto
=
sdscatsds
(
proto
,
ln
->
value
);
proto
=
sdscatlen
(
proto
,
"
\r\n
"
,
2
);
listDelNode
(
ldb
.
logs
,
ln
);
}
write
(
ldb
.
fd
,
proto
,
sdslen
(
proto
));
}
}
/* Start a debugging session before calling EVAL implementation.
/* Start a debugging session before calling EVAL implementation.
...
@@ -1495,9 +1509,12 @@ int ldbStartSession(client *c) {
...
@@ -1495,9 +1509,12 @@ int ldbStartSession(client *c) {
/* Setup our debugging session. */
/* Setup our debugging session. */
anetBlock
(
NULL
,
ldb
.
fd
);
anetBlock
(
NULL
,
ldb
.
fd
);
anetSendTimeout
(
NULL
,
ldb
.
fd
,
5000
);
ldb
.
active
=
1
;
ldb
.
active
=
1
;
ldb
.
src
=
c
->
argv
[
1
];
/* First argument of EVAL is the script itself. */
/* First argument of EVAL is the script itself. We split it into different
incrRefCount
(
ldb
.
src
);
* lines since this is the way the debugger accesses the source code. */
sds
srcstring
=
c
->
argv
[
1
]
->
ptr
;
ldb
.
src
=
sdssplitlen
(
srcstring
,
sdslen
(
srcstring
),
"
\n
"
,
1
,
&
ldb
.
lines
);
return
1
;
return
1
;
}
}
...
@@ -1513,8 +1530,16 @@ void ldbEndSession(client *c) {
...
@@ -1513,8 +1530,16 @@ void ldbEndSession(client *c) {
/* Otherwise let's restore client's state. */
/* Otherwise let's restore client's state. */
anetNonBlock
(
NULL
,
ldb
.
fd
);
anetNonBlock
(
NULL
,
ldb
.
fd
);
anetSendTimeout
(
NULL
,
ldb
.
fd
,
0
);
/* Close the client connectin after sending the final EVAL reply
* in order to signal the end of the debugging session. */
c
->
flags
|=
CLIENT_CLOSE_AFTER_REPLY
;
/* Cleanup. */
sdsfreesplitres
(
ldb
.
src
,
ldb
.
lines
);
ldb
.
lines
=
0
;
ldb
.
active
=
0
;
ldb
.
active
=
0
;
decrRefCount
(
ldb
.
src
);
}
}
/* Wrapper for EVAL / EVALSHA that enables debugging, and makes sure
/* Wrapper for EVAL / EVALSHA that enables debugging, and makes sure
...
@@ -1528,14 +1553,125 @@ void evalGenericCommandWithDebugging(client *c, int evalsha) {
...
@@ -1528,14 +1553,125 @@ void evalGenericCommandWithDebugging(client *c, int evalsha) {
}
}
}
}
/* Return a pointer to ldb.src source code line, considering line to be
* one-based, and returning a special string for out of range lines. */
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
];
}
/* Expect a valid multi-bulk command in the debugging client query buffer.
* On success the command is parsed and returned as an array of SDS strings,
* otherwise NULL is returned and there is to read more buffer. */
sds
*
ldbReplParseCommand
(
int
*
argcp
)
{
sds
*
argv
=
NULL
;
int
argc
=
0
;
if
(
sdslen
(
ldb
.
cbuf
)
==
0
)
return
NULL
;
/* Working on a copy is simpler in this case. We can modify it freely
* for the sake of simpler parsing. */
sds
copy
=
sdsdup
(
ldb
.
cbuf
);
char
*
p
=
copy
;
/* This Redis protocol parser is a joke... just the simplest thing that
* works in this context. It is also very forgiving regarding broken
* protocol. */
/* Seek and parse *<count>\r\n. */
p
=
strchr
(
p
,
'*'
);
if
(
!
p
)
goto
protoerr
;
char
*
plen
=
p
+
1
;
/* Multi bulk len pointer. */
p
=
strstr
(
p
,
"
\r\n
"
);
if
(
!
p
)
goto
protoerr
;
*
p
=
'\0'
;
p
+=
2
;
*
argcp
=
atoi
(
plen
);
if
(
*
argcp
<=
0
||
*
argcp
>
1024
)
goto
protoerr
;
/* Parse each argument. */
argv
=
zmalloc
(
sizeof
(
sds
)
*
(
*
argcp
));
argc
=
0
;
while
(
argc
<
*
argcp
)
{
if
(
*
p
!=
'$'
)
goto
protoerr
;
plen
=
p
+
1
;
/* Bulk string len pointer. */
p
=
strstr
(
p
,
"
\r\n
"
);
if
(
!
p
)
goto
protoerr
;
*
p
=
'\0'
;
p
+=
2
;
int
slen
=
atoi
(
plen
);
/* Length of this arg. */
if
(
slen
<=
0
||
slen
>
1024
)
goto
protoerr
;
argv
[
argc
++
]
=
sdsnewlen
(
p
,
slen
);
p
+=
slen
;
/* Skip the already parsed argument. */
if
(
p
[
0
]
!=
'\r'
||
p
[
1
]
!=
'\n'
)
goto
protoerr
;
p
+=
2
;
/* Skip \r\n. */
}
sdsfree
(
copy
);
return
argv
;
protoerr:
sdsfreesplitres
(
argv
,
argc
);
sdsfree
(
copy
);
return
NULL
;
}
/* Read debugging commands from client. */
void
ldbRepl
(
void
)
{
sds
*
argv
;
int
argc
;
/* We continue processing commands until a command that should return
* to the Lua interpreter is found. */
while
(
1
)
{
while
((
argv
=
ldbReplParseCommand
(
&
argc
))
==
NULL
)
{
char
buf
[
1024
];
int
nread
=
read
(
ldb
.
fd
,
buf
,
sizeof
(
buf
));
if
(
nread
<=
0
)
{
/* Make sure the script runs without user input since the
* client is no longer connected. */
ldb
.
step
=
0
;
ldb
.
bpcount
=
0
;
return
;
}
ldb
.
cbuf
=
sdscatlen
(
ldb
.
cbuf
,
buf
,
nread
);
}
/* Flush the old buffer. */
sdsfree
(
ldb
.
cbuf
);
ldb
.
cbuf
=
sdsempty
();
/* 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."
));
ldbSendLogs
();
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"s"
)
||
!
strcasecmp
(
argv
[
0
],
"step"
))
{
ldb
.
step
=
1
;
break
;
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"c"
)
||
!
strcasecmp
(
argv
[
0
],
"continue"
)){
break
;
}
else
{
ldbLog
(
sdsnew
(
"Unknown Redis Lua debugger command."
));
ldbSendLogs
();
}
/* Free the command vector. */
sdsfreesplitres
(
argv
,
argc
);
}
/* Free the current command argv if we break inside the while loop. */
sdsfreesplitres
(
argv
,
argc
);
}
/* This is the core of our Lua debugger, called each time Lua is about
/* This is the core of our Lua debugger, called each time Lua is about
* to start executing a new line. */
* to start executing a new line. */
void
luaLdbLineHook
(
lua_State
*
lua
,
lua_Debug
*
ar
)
{
void
luaLdbLineHook
(
lua_State
*
lua
,
lua_Debug
*
ar
)
{
lua_getstack
(
lua
,
0
,
ar
);
lua_getstack
(
lua
,
0
,
ar
);
lua_getinfo
(
lua
,
"Sl"
,
ar
);
lua_getinfo
(
lua
,
"Sl"
,
ar
);
if
(
strstr
(
ar
->
short_src
,
"user_script"
)
!=
NULL
)
if
(
strstr
(
ar
->
short_src
,
"user_script"
)
==
NULL
)
return
;
printf
(
"%s %d
\n
"
,
ar
->
short_src
,
(
int
)
ar
->
currentline
);
if
(
ldb
.
step
)
{
ldb
.
step
=
0
;
ldbLog
(
sdscatprintf
(
sdsempty
(),
"%d: %s"
,
(
int
)
ar
->
currentline
,
ldbGetSourceLine
(
ar
->
currentline
-
1
)));
ldbSendLogs
();
ldbRepl
();
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment