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
c494db89
Commit
c494db89
authored
Nov 06, 2015
by
antirez
Browse files
Lua debugger: foundations implemented.
parent
7cfdccd9
Changes
5
Hide whitespace changes
Inline
Side-by-side
src/aof.c
View file @
c494db89
...
@@ -218,8 +218,9 @@ void stopAppendOnly(void) {
...
@@ -218,8 +218,9 @@ void stopAppendOnly(void) {
serverLog
(
LL_NOTICE
,
"Killing running AOF rewrite child: %ld"
,
serverLog
(
LL_NOTICE
,
"Killing running AOF rewrite child: %ld"
,
(
long
)
server
.
aof_child_pid
);
(
long
)
server
.
aof_child_pid
);
if
(
kill
(
server
.
aof_child_pid
,
SIGUSR1
)
!=
-
1
)
if
(
kill
(
server
.
aof_child_pid
,
SIGUSR1
)
!=
-
1
)
{
wait3
(
&
statloc
,
0
,
NULL
);
while
(
wait3
(
&
statloc
,
0
,
NULL
)
!=
server
.
aof_child_pid
);
}
/* reset the buffer accumulating changes while the child saves */
/* reset the buffer accumulating changes while the child saves */
aofRewriteBufferReset
();
aofRewriteBufferReset
();
aofRemoveTempFile
(
server
.
aof_child_pid
);
aofRemoveTempFile
(
server
.
aof_child_pid
);
...
...
src/redis-cli.c
View file @
c494db89
...
@@ -111,6 +111,7 @@ static struct config {
...
@@ -111,6 +111,7 @@ static struct config {
sds
mb_delim
;
sds
mb_delim
;
char
prompt
[
128
];
char
prompt
[
128
];
char
*
eval
;
char
*
eval
;
int
eval_ldb
;
int
last_cmd_type
;
int
last_cmd_type
;
}
config
;
}
config
;
...
@@ -141,6 +142,7 @@ static long long mstime(void) {
...
@@ -141,6 +142,7 @@ static long long mstime(void) {
static
void
cliRefreshPrompt
(
void
)
{
static
void
cliRefreshPrompt
(
void
)
{
int
len
;
int
len
;
if
(
config
.
eval_ldb
)
return
;
if
(
config
.
hostsocket
!=
NULL
)
if
(
config
.
hostsocket
!=
NULL
)
len
=
snprintf
(
config
.
prompt
,
sizeof
(
config
.
prompt
),
"redis %s"
,
len
=
snprintf
(
config
.
prompt
,
sizeof
(
config
.
prompt
),
"redis %s"
,
config
.
hostsocket
);
config
.
hostsocket
);
...
@@ -822,6 +824,8 @@ static int parseOptions(int argc, char **argv) {
...
@@ -822,6 +824,8 @@ static int parseOptions(int argc, char **argv) {
config
.
bigkeys
=
1
;
config
.
bigkeys
=
1
;
}
else
if
(
!
strcmp
(
argv
[
i
],
"--eval"
)
&&
!
lastarg
)
{
}
else
if
(
!
strcmp
(
argv
[
i
],
"--eval"
)
&&
!
lastarg
)
{
config
.
eval
=
argv
[
++
i
];
config
.
eval
=
argv
[
++
i
];
}
else
if
(
!
strcmp
(
argv
[
i
],
"--ldb"
))
{
config
.
eval_ldb
=
1
;
}
else
if
(
!
strcmp
(
argv
[
i
],
"-c"
))
{
}
else
if
(
!
strcmp
(
argv
[
i
],
"-c"
))
{
config
.
cluster_mode
=
1
;
config
.
cluster_mode
=
1
;
}
else
if
(
!
strcmp
(
argv
[
i
],
"-d"
)
&&
!
lastarg
)
{
}
else
if
(
!
strcmp
(
argv
[
i
],
"-d"
)
&&
!
lastarg
)
{
...
@@ -904,6 +908,7 @@ static void usage(void) {
...
@@ -904,6 +908,7 @@ static void usage(void) {
" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.
\n
"
" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.
\n
"
" The test will run for the specified amount of seconds.
\n
"
" The test will run for the specified amount of seconds.
\n
"
" --eval <file> Send an EVAL command using the Lua script at <file>.
\n
"
" --eval <file> Send an EVAL command using the Lua script at <file>.
\n
"
" --ldb Used with --eval enable the Redis Lua debugger.
\n
"
" --help Output this help and exit.
\n
"
" --help Output this help and exit.
\n
"
" --version Output version and exit.
\n
"
" --version Output version and exit.
\n
"
"
\n
"
"
\n
"
...
@@ -1071,6 +1076,12 @@ static int evalMode(int argc, char **argv) {
...
@@ -1071,6 +1076,12 @@ static int evalMode(int argc, char **argv) {
}
}
fclose
(
fp
);
fclose
(
fp
);
/* If we are debugging a script, enable the Lua debugger. */
if
(
config
.
eval_ldb
)
{
redisReply
*
reply
=
redisCommand
(
context
,
"SCRIPT DEBUG yes"
);
if
(
reply
)
freeReplyObject
(
reply
);
}
/* Create our argument vector */
/* Create our argument vector */
argv2
=
zmalloc
(
sizeof
(
sds
)
*
(
argc
+
3
));
argv2
=
zmalloc
(
sizeof
(
sds
)
*
(
argc
+
3
));
argv2
[
0
]
=
sdsnew
(
"EVAL"
);
argv2
[
0
]
=
sdsnew
(
"EVAL"
);
...
@@ -1086,7 +1097,12 @@ static int evalMode(int argc, char **argv) {
...
@@ -1086,7 +1097,12 @@ static int evalMode(int argc, char **argv) {
argv2
[
2
]
=
sdscatprintf
(
sdsempty
(),
"%d"
,
keys
);
argv2
[
2
]
=
sdscatprintf
(
sdsempty
(),
"%d"
,
keys
);
/* Call it */
/* Call it */
return
issueCommand
(
argc
+
3
-
got_comma
,
argv2
);
int
retval
=
issueCommand
(
argc
+
3
-
got_comma
,
argv2
);
if
(
config
.
eval_ldb
)
{
strncpy
(
config
.
prompt
,
"lua debugger> "
,
sizeof
(
config
.
prompt
));
repl
();
}
return
retval
;
}
}
/*------------------------------------------------------------------------------
/*------------------------------------------------------------------------------
...
@@ -2210,6 +2226,7 @@ int main(int argc, char **argv) {
...
@@ -2210,6 +2226,7 @@ int main(int argc, char **argv) {
config
.
stdinarg
=
0
;
config
.
stdinarg
=
0
;
config
.
auth
=
NULL
;
config
.
auth
=
NULL
;
config
.
eval
=
NULL
;
config
.
eval
=
NULL
;
config
.
eval_ldb
=
0
;
config
.
last_cmd_type
=
-
1
;
config
.
last_cmd_type
=
-
1
;
spectrum_palette
=
spectrum_palette_color
;
spectrum_palette
=
spectrum_palette_color
;
...
...
src/scripting.c
View file @
c494db89
...
@@ -45,6 +45,25 @@ char *redisProtocolToLuaType_Error(lua_State *lua, char *reply);
...
@@ -45,6 +45,25 @@ char *redisProtocolToLuaType_Error(lua_State *lua, char *reply);
char
*
redisProtocolToLuaType_MultiBulk
(
lua_State
*
lua
,
char
*
reply
);
char
*
redisProtocolToLuaType_MultiBulk
(
lua_State
*
lua
,
char
*
reply
);
int
redis_math_random
(
lua_State
*
L
);
int
redis_math_random
(
lua_State
*
L
);
int
redis_math_randomseed
(
lua_State
*
L
);
int
redis_math_randomseed
(
lua_State
*
L
);
void
ldbInit
(
void
);
void
ldbDisable
(
client
*
c
);
void
ldbEnable
(
client
*
c
);
void
evalGenericCommandWithDebugging
(
client
*
c
,
int
evalsha
);
void
luaLdbLineHook
(
lua_State
*
lua
,
lua_Debug
*
ar
);
/* Debugger shared state is stored inside this global structure. */
#define LDB_BREAKPOINTS_MAX 64
struct
ldbState
{
int
fd
;
/* Socket of the debugging client. */
int
active
;
/* Are we debugging EVAL right now? */
int
forked
;
/* Is this a fork()ed debugging session? */
list
*
logs
;
/* List of messages to send to the client. */
list
*
traces
;
/* Messages about Redis commands executed since last stop.*/
int
bp
[
LDB_BREAKPOINTS_MAX
];
/* An array of breakpoints line numbers. */
int
bpcount
;
/* Number of valid entries inside bp. */
int
step
;
/* Stop at next line ragardless of breakpoints. */
robj
*
src
;
/* Lua script source code. */
}
ldb
;
/* ---------------------------------------------------------------------------
/* ---------------------------------------------------------------------------
* Utility functions.
* Utility functions.
...
@@ -821,6 +840,7 @@ void scriptingInit(int setup) {
...
@@ -821,6 +840,7 @@ void scriptingInit(int setup) {
server
.
lua_timedout
=
0
;
server
.
lua_timedout
=
0
;
server
.
lua_always_replicate_commands
=
0
;
/* Only DEBUG can change it.*/
server
.
lua_always_replicate_commands
=
0
;
/* Only DEBUG can change it.*/
server
.
lua_time_limit
=
LUA_SCRIPT_TIME_LIMIT
;
server
.
lua_time_limit
=
LUA_SCRIPT_TIME_LIMIT
;
ldbInit
();
}
}
luaLoadLibraries
(
lua
);
luaLoadLibraries
(
lua
);
...
@@ -1038,15 +1058,6 @@ int redis_math_randomseed (lua_State *L) {
...
@@ -1038,15 +1058,6 @@ int redis_math_randomseed (lua_State *L) {
return
0
;
return
0
;
}
}
/* ---------------------------------------------------------------------------
* LDB: Redis Lua debugging facilities
* ------------------------------------------------------------------------- */
/* Enable debug mode of Lua scripts for this client. */
void
ldbEnable
(
client
*
c
)
{
c
->
flags
|=
CLIENT_LUA_DEBUG
;
}
/* ---------------------------------------------------------------------------
/* ---------------------------------------------------------------------------
* EVAL and SCRIPT commands implementation
* EVAL and SCRIPT commands implementation
* ------------------------------------------------------------------------- */
* ------------------------------------------------------------------------- */
...
@@ -1214,13 +1225,21 @@ void evalGenericCommand(client *c, int evalsha) {
...
@@ -1214,13 +1225,21 @@ void evalGenericCommand(client *c, int evalsha) {
/* Set a hook in order to be able to stop the script execution if it
/* Set a hook in order to be able to stop the script execution if it
* is running for too much time.
* is running for too much time.
* We set the hook only if the time limit is enabled as the hook will
* We set the hook only if the time limit is enabled as the hook will
* make the Lua script execution slower. */
* make the Lua script execution slower.
*
* If we are debugging, we set instead a "line" hook so that the
* debugger is call-back at every line executed by the script. */
server
.
lua_caller
=
c
;
server
.
lua_caller
=
c
;
server
.
lua_time_start
=
mstime
();
server
.
lua_time_start
=
mstime
();
server
.
lua_kill
=
0
;
server
.
lua_kill
=
0
;
if
(
server
.
lua_time_limit
>
0
&&
server
.
masterhost
==
NULL
)
{
if
(
server
.
lua_time_limit
>
0
&&
server
.
masterhost
==
NULL
&&
ldb
.
active
==
0
)
{
lua_sethook
(
lua
,
luaMaskCountHook
,
LUA_MASKCOUNT
,
100000
);
lua_sethook
(
lua
,
luaMaskCountHook
,
LUA_MASKCOUNT
,
100000
);
delhook
=
1
;
delhook
=
1
;
}
else
if
(
ldb
.
active
)
{
lua_sethook
(
server
.
lua
,
luaLdbLineHook
,
LUA_MASKLINE
,
0
);
delhook
=
1
;
}
}
/* At this point whether this script was never seen before or if it was
/* At this point whether this script was never seen before or if it was
...
@@ -1229,7 +1248,7 @@ void evalGenericCommand(client *c, int evalsha) {
...
@@ -1229,7 +1248,7 @@ void evalGenericCommand(client *c, int evalsha) {
err
=
lua_pcall
(
lua
,
0
,
1
,
-
2
);
err
=
lua_pcall
(
lua
,
0
,
1
,
-
2
);
/* Perform some cleanup that we need to do both on error and success. */
/* Perform some cleanup that we need to do both on error and success. */
if
(
delhook
)
lua_sethook
(
lua
,
luaMaskCountHook
,
0
,
0
);
/* Disable hook */
if
(
delhook
)
lua_sethook
(
lua
,
NULL
,
0
,
0
);
/* Disable hook */
if
(
server
.
lua_timedout
)
{
if
(
server
.
lua_timedout
)
{
server
.
lua_timedout
=
0
;
server
.
lua_timedout
=
0
;
/* Restore the readable handler that was unregistered when the
/* Restore the readable handler that was unregistered when the
...
@@ -1308,7 +1327,10 @@ void evalGenericCommand(client *c, int evalsha) {
...
@@ -1308,7 +1327,10 @@ void evalGenericCommand(client *c, int evalsha) {
}
}
void
evalCommand
(
client
*
c
)
{
void
evalCommand
(
client
*
c
)
{
evalGenericCommand
(
c
,
0
);
if
(
!
(
c
->
flags
&
CLIENT_LUA_DEBUG
))
evalGenericCommand
(
c
,
0
);
else
evalGenericCommandWithDebugging
(
c
,
0
);
}
}
void
evalShaCommand
(
client
*
c
)
{
void
evalShaCommand
(
client
*
c
)
{
...
@@ -1320,7 +1342,12 @@ void evalShaCommand(client *c) {
...
@@ -1320,7 +1342,12 @@ void evalShaCommand(client *c) {
addReply
(
c
,
shared
.
noscripterr
);
addReply
(
c
,
shared
.
noscripterr
);
return
;
return
;
}
}
evalGenericCommand
(
c
,
1
);
if
(
!
(
c
->
flags
&
CLIENT_LUA_DEBUG
))
evalGenericCommand
(
c
,
1
);
else
{
addReplyError
(
c
,
"Please use EVAL instead of EVALSHA for debugging"
);
return
;
}
}
}
void
scriptCommand
(
client
*
c
)
{
void
scriptCommand
(
client
*
c
)
{
...
@@ -1366,12 +1393,149 @@ void scriptCommand(client *c) {
...
@@ -1366,12 +1393,149 @@ void scriptCommand(client *c) {
server
.
lua_kill
=
1
;
server
.
lua_kill
=
1
;
addReply
(
c
,
shared
.
ok
);
addReply
(
c
,
shared
.
ok
);
}
}
}
else
if
(
c
->
argc
==
2
&&
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"debug"
))
{
}
else
if
(
c
->
argc
==
3
&&
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"debug"
))
{
ldbEnable
(
c
);
if
(
clientHasPendingReplies
(
c
))
{
addReply
(
c
,
shared
.
ok
);
addReplyError
(
c
,
"SCRIPT DEBUG must be called outside a pipeline"
);
return
;
}
if
(
!
strcasecmp
(
c
->
argv
[
2
]
->
ptr
,
"no"
))
{
ldbDisable
(
c
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
2
]
->
ptr
,
"yes"
))
{
ldbEnable
(
c
);
addReply
(
c
,
shared
.
ok
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
2
]
->
ptr
,
"sync"
))
{
ldbEnable
(
c
);
addReply
(
c
,
shared
.
ok
);
c
->
flags
|=
CLIENT_LUA_DEBUG_SYNC
;
}
else
{
addReplyError
(
c
,
"Use SCRIPT DEBUG yes/async/no"
);
}
}
else
{
}
else
{
addReplyError
(
c
,
"Unknown SCRIPT subcommand or wrong # of args."
);
addReplyError
(
c
,
"Unknown SCRIPT subcommand or wrong # of args."
);
}
}
}
}
/* ---------------------------------------------------------------------------
* LDB: Redis Lua debugging facilities
* ------------------------------------------------------------------------- */
/* Initialize Lua debugger data structures. */
void
ldbInit
(
void
)
{
ldb
.
fd
=
-
1
;
ldb
.
active
=
0
;
ldb
.
logs
=
listCreate
();
ldb
.
traces
=
listCreate
();
listSetFreeMethod
(
ldb
.
logs
,(
void
(
*
)(
void
*
))
sdsfree
);
listSetFreeMethod
(
ldb
.
traces
,
(
void
(
*
)(
void
*
))
sdsfree
);
ldb
.
src
=
NULL
;
}
/* Remove all the pending messages in the specified list. */
void
ldbFlushLog
(
list
*
log
)
{
listNode
*
ln
;
while
((
ln
=
listFirst
(
log
))
!=
NULL
)
listDelNode
(
log
,
ln
);
}
/* Enable debug mode of Lua scripts for this client. */
void
ldbEnable
(
client
*
c
)
{
c
->
flags
|=
CLIENT_LUA_DEBUG
;
ldbFlushLog
(
ldb
.
logs
);
ldbFlushLog
(
ldb
.
traces
);
ldb
.
fd
=
c
->
fd
;
ldb
.
step
=
0
;
ldb
.
bpcount
=
0
;
}
void
ldbDisable
(
client
*
c
)
{
c
->
flags
&=
~
(
CLIENT_LUA_DEBUG
|
CLIENT_LUA_DEBUG_SYNC
);
}
/* Append a log entry to the specified LDB log. */
void
ldbLog
(
list
*
log
,
sds
entry
)
{
listAddNodeTail
(
log
,
entry
);
}
/* Send ldb.logs and ldb.traces to the debugging client as a multi-bulk
* reply consisting of simple strings. Log entries which include newlines
* have them replaced with spaces. The entries sent are also consumed. */
void
ldbWriteLogs
(
void
)
{
}
/* Start a debugging session before calling EVAL implementation.
* The techique we use is to capture the client socket file descriptor,
* in order to perform direct I/O with it from within Lua hooks. This
* way we don't have to re-enter Redis in order to handle I/O.
*
* The function returns 1 if the caller should proceed to call EVAL,
* and 0 if instead the caller should abort the operation (this happens
* for the parent in a forked session, since it's up to the children
* to continue, or when fork returned an error).
*
* The caller should call ldbEndSession() only if ldbStartSession()
* returned 1. */
int
ldbStartSession
(
client
*
c
)
{
ldb
.
forked
=
(
c
->
flags
&
CLIENT_LUA_DEBUG_SYNC
)
==
0
;
if
(
ldb
.
forked
)
{
pid_t
cp
=
fork
();
if
(
cp
==
-
1
)
{
addReplyError
(
c
,
"Fork() failed: can't run EVAL in debugging mode."
);
return
0
;
}
else
if
(
cp
==
0
)
{
/* Child */
serverLog
(
LL_WARNING
,
"Redis forked for debugging eval"
);
closeListeningSockets
(
0
);
}
else
{
/* Parent */
freeClientAsync
(
c
);
/* Close the client in the parent side. */
return
0
;
}
}
/* Setup our debugging session. */
anetBlock
(
NULL
,
ldb
.
fd
);
ldb
.
active
=
1
;
ldb
.
src
=
c
->
argv
[
1
];
/* First argument of EVAL is the script itself. */
incrRefCount
(
ldb
.
src
);
return
1
;
}
/* End a debugging session after the EVAL call with debugging enabled
* returned. */
void
ldbEndSession
(
client
*
c
)
{
/* If it's a fork()ed session, we just exit. */
if
(
ldb
.
forked
)
{
writeToClient
(
c
->
fd
,
c
,
0
);
serverLog
(
LL_WARNING
,
"Lua debugging session child exiting"
);
exitFromChild
(
0
);
}
/* Otherwise let's restore client's state. */
anetNonBlock
(
NULL
,
ldb
.
fd
);
ldb
.
active
=
0
;
decrRefCount
(
ldb
.
src
);
}
/* Wrapper for EVAL / EVALSHA that enables debugging, and makes sure
* that when EVAL returns, whatever happened, the session is ended. */
void
evalGenericCommandWithDebugging
(
client
*
c
,
int
evalsha
)
{
if
(
ldbStartSession
(
c
))
{
evalGenericCommand
(
c
,
evalsha
);
ldbEndSession
(
c
);
}
else
{
ldbDisable
(
c
);
}
}
/* This is the core of our Lua debugger, called each time Lua is about
* to start executing a new line. */
void
luaLdbLineHook
(
lua_State
*
lua
,
lua_Debug
*
ar
)
{
lua_getstack
(
lua
,
0
,
ar
);
lua_getinfo
(
lua
,
"Sl"
,
ar
);
if
(
strstr
(
ar
->
short_src
,
"user_script"
)
!=
NULL
)
printf
(
"%s %d
\n
"
,
ar
->
short_src
,
(
int
)
ar
->
currentline
);
}
src/server.c
View file @
c494db89
...
@@ -1202,7 +1202,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -1202,7 +1202,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
backgroundRewriteDoneHandler
(
exitcode
,
bysignal
);
backgroundRewriteDoneHandler
(
exitcode
,
bysignal
);
}
else
{
}
else
{
serverLog
(
LL_WARNING
,
serverLog
(
LL_WARNING
,
"Warning, detected child with unmatched pid: %ld"
,
"Warning, detected child with unmatched pid: %ld"
" (EVAL forked debugging session?)"
,
(
long
)
pid
);
(
long
)
pid
);
}
}
updateDictResizePolicy
();
updateDictResizePolicy
();
...
...
src/server.h
View file @
c494db89
...
@@ -251,6 +251,7 @@ typedef long long mstime_t; /* millisecond time type. */
...
@@ -251,6 +251,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define CLIENT_REPLY_SKIP_NEXT (1<<23)
/* Set CLIENT_REPLY_SKIP for next cmd */
#define CLIENT_REPLY_SKIP_NEXT (1<<23)
/* Set CLIENT_REPLY_SKIP for next cmd */
#define CLIENT_REPLY_SKIP (1<<24)
/* Don't send just this reply. */
#define CLIENT_REPLY_SKIP (1<<24)
/* Don't send just this reply. */
#define CLIENT_LUA_DEBUG (1<<25)
/* Run EVAL in debug mode. */
#define CLIENT_LUA_DEBUG (1<<25)
/* Run EVAL in debug mode. */
#define CLIENT_LUA_DEBUG_SYNC (1<<26)
/* EVAL debugging without fork() */
/* Client block type (btype field in client structure)
/* Client block type (btype field in client structure)
* if CLIENT_BLOCKED flag is set. */
* if CLIENT_BLOCKED flag is set. */
...
@@ -1142,6 +1143,7 @@ int processEventsWhileBlocked(void);
...
@@ -1142,6 +1143,7 @@ int processEventsWhileBlocked(void);
int
handleClientsWithPendingWrites
(
void
);
int
handleClientsWithPendingWrites
(
void
);
int
clientHasPendingReplies
(
client
*
c
);
int
clientHasPendingReplies
(
client
*
c
);
void
unlinkClient
(
client
*
c
);
void
unlinkClient
(
client
*
c
);
int
writeToClient
(
int
fd
,
client
*
c
,
int
handler_installed
);
#ifdef __GNUC__
#ifdef __GNUC__
void
addReplyErrorFormat
(
client
*
c
,
const
char
*
fmt
,
...)
void
addReplyErrorFormat
(
client
*
c
,
const
char
*
fmt
,
...)
...
...
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