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
e91d9a6f
Commit
e91d9a6f
authored
Jul 24, 2019
by
Oran Agra
Browse files
Extend modules API to allow modules report to redis INFO
this implements #6012
parent
bc5cb168
Changes
9
Hide whitespace changes
Inline
Side-by-side
runtest-moduleapi
View file @
e91d9a6f
...
@@ -13,4 +13,4 @@ then
...
@@ -13,4 +13,4 @@ then
fi
fi
make
-C
tests/modules
&&
\
make
-C
tests/modules
&&
\
$TCLSH
tests/test_helper.tcl
--single
unit/moduleapi/commandfilter
"
${
@
}
"
$TCLSH
tests/test_helper.tcl
--single
unit/moduleapi/commandfilter
--single
unit/moduleapi/infotest
"
${
@
}
"
src/debug.c
View file @
e91d9a6f
...
@@ -1337,6 +1337,12 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
...
@@ -1337,6 +1337,12 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
/* Log dump of processor registers */
/* Log dump of processor registers */
logRegisters
(
uc
);
logRegisters
(
uc
);
/* Log Modules INFO */
serverLogRaw
(
LL_WARNING
|
LL_RAW
,
"
\n
------ MODULES INFO OUTPUT ------
\n
"
);
infostring
=
modulesCollectInfo
(
sdsempty
(),
NULL
,
1
,
0
);
serverLogRaw
(
LL_WARNING
|
LL_RAW
,
infostring
);
sdsfree
(
infostring
);
#if defined(HAVE_PROC_MAPS)
#if defined(HAVE_PROC_MAPS)
/* Test memory */
/* Test memory */
serverLogRaw
(
LL_WARNING
|
LL_RAW
,
"
\n
------ FAST MEMORY TEST ------
\n
"
);
serverLogRaw
(
LL_WARNING
|
LL_RAW
,
"
\n
------ FAST MEMORY TEST ------
\n
"
);
...
...
src/module.c
View file @
e91d9a6f
...
@@ -40,6 +40,16 @@
...
@@ -40,6 +40,16 @@
* pointers that have an API the module can call with them)
* pointers that have an API the module can call with them)
* -------------------------------------------------------------------------- */
* -------------------------------------------------------------------------- */
typedef struct RedisModuleInfoCtx {
struct RedisModule *module;
sds requested_section;
sds info; /* info string we collected so far */
int sections; /* number of sections we collected so far */
int in_section; /* indication if we're in an active section or not */
} RedisModuleInfoCtx;
typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);
/* This structure represents a module inside the system. */
/* This structure represents a module inside the system. */
struct RedisModule {
struct RedisModule {
void *handle; /* Module dlopen() handle. */
void *handle; /* Module dlopen() handle. */
...
@@ -51,6 +61,7 @@ struct RedisModule {
...
@@ -51,6 +61,7 @@ struct RedisModule {
list *using; /* List of modules we use some APIs of. */
list *using; /* List of modules we use some APIs of. */
list *filters; /* List of filters the module has registered. */
list *filters; /* List of filters the module has registered. */
int in_call; /* RM_Call() nesting level */
int in_call; /* RM_Call() nesting level */
RedisModuleInfoFunc info_cb; /* callback for module to add INFO fields. */
};
};
typedef struct RedisModule RedisModule;
typedef struct RedisModule RedisModule;
...
@@ -4686,6 +4697,118 @@ int RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *k
...
@@ -4686,6 +4697,118 @@ int RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *k
return res ? REDISMODULE_OK : REDISMODULE_ERR;
return res ? REDISMODULE_OK : REDISMODULE_ERR;
}
}
/* --------------------------------------------------------------------------
* Modules Info fields
* -------------------------------------------------------------------------- */
/* Used to start a new section, before adding any fields. the section name will
* be prefixed by "<modulename>_" and must only include A-Z,a-z,0-9.
* When return value is REDISMODULE_ERR, the section should and will be skipped. */
int RM_AddInfoSection(RedisModuleInfoCtx *ctx, char *name) {
sds full_name = sdscatprintf(sdsdup(ctx->module->name), "_%s", name);
/* proceed only if:
* 1) no section was requested (emit all)
* 2) the module name was requested (emit all)
* 3) this specific section was requested. */
if (ctx->requested_section) {
if (strcasecmp(ctx->requested_section, full_name) &&
strcasecmp(ctx->requested_section, ctx->module->name)) {
sdsfree(full_name);
ctx->in_section = 0;
return REDISMODULE_ERR;
}
}
if (ctx->sections++) ctx->info = sdscat(ctx->info,"\r\n");
ctx->info = sdscatprintf(ctx->info, "# %s\r\n", full_name);
ctx->in_section = 1;
sdsfree(full_name);
return REDISMODULE_OK;
}
/* Used by RedisModuleInfoFunc to add info fields.
* Each field will be automatically prefixed by "<modulename>_".
* Field names or values must not include \r\n of ":" */
int RM_AddInfoFieldString(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) {
if (!ctx->in_section)
return REDISMODULE_ERR;
ctx->info = sdscatprintf(ctx->info,
"%s_%s:%s\r\n",
ctx->module->name,
field,
(sds)value->ptr);
return REDISMODULE_OK;
}
int RM_AddInfoFieldCString(RedisModuleInfoCtx *ctx, char *field, char *value) {
if (!ctx->in_section)
return REDISMODULE_ERR;
ctx->info = sdscatprintf(ctx->info,
"%s_%s:%s\r\n",
ctx->module->name,
field,
value);
return REDISMODULE_OK;
}
int RM_AddInfoFieldDouble(RedisModuleInfoCtx *ctx, char *field, double value) {
if (!ctx->in_section)
return REDISMODULE_ERR;
ctx->info = sdscatprintf(ctx->info,
"%s_%s:%.17g\r\n",
ctx->module->name,
field,
value);
return REDISMODULE_OK;
}
int RM_AddInfoFieldLongLong(RedisModuleInfoCtx *ctx, char *field, long long value) {
if (!ctx->in_section)
return REDISMODULE_ERR;
ctx->info = sdscatprintf(ctx->info,
"%s_%s:%lld\r\n",
ctx->module->name,
field,
value);
return REDISMODULE_OK;
}
int RM_AddInfoFieldULongLong(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) {
if (!ctx->in_section)
return REDISMODULE_ERR;
ctx->info = sdscatprintf(ctx->info,
"%s_%s:%llu\r\n",
ctx->module->name,
field,
value);
return REDISMODULE_OK;
}
int RM_RegisterInfoFunc(RedisModuleCtx *ctx, RedisModuleInfoFunc cb) {
ctx->module->info_cb = cb;
return REDISMODULE_OK;
}
sds modulesCollectInfo(sds info, sds section, int for_crash_report, int sections) {
dictIterator *di = dictGetIterator(modules);
dictEntry *de;
while ((de = dictNext(di)) != NULL) {
struct RedisModule *module = dictGetVal(de);
if (!module->info_cb)
continue;
RedisModuleInfoCtx info_ctx = {module, section, info, sections, 0};
module->info_cb(&info_ctx, for_crash_report);
info = info_ctx.info;
sections = info_ctx.sections;
}
dictReleaseIterator(di);
return info;
}
/* --------------------------------------------------------------------------
/* --------------------------------------------------------------------------
* Modules utility APIs
* Modules utility APIs
* -------------------------------------------------------------------------- */
* -------------------------------------------------------------------------- */
...
@@ -5490,4 +5613,11 @@ void moduleRegisterCoreAPI(void) {
...
@@ -5490,4 +5613,11 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(CommandFilterArgInsert);
REGISTER_API(CommandFilterArgInsert);
REGISTER_API(CommandFilterArgReplace);
REGISTER_API(CommandFilterArgReplace);
REGISTER_API(CommandFilterArgDelete);
REGISTER_API(CommandFilterArgDelete);
REGISTER_API(RegisterInfoFunc);
REGISTER_API(AddInfoSection);
REGISTER_API(AddInfoFieldString);
REGISTER_API(AddInfoFieldCString);
REGISTER_API(AddInfoFieldDouble);
REGISTER_API(AddInfoFieldLongLong);
REGISTER_API(AddInfoFieldULongLong);
}
}
src/redismodule.h
View file @
e91d9a6f
...
@@ -160,6 +160,7 @@ typedef struct RedisModuleDict RedisModuleDict;
...
@@ -160,6 +160,7 @@ typedef struct RedisModuleDict RedisModuleDict;
typedef
struct
RedisModuleDictIter
RedisModuleDictIter
;
typedef
struct
RedisModuleDictIter
RedisModuleDictIter
;
typedef
struct
RedisModuleCommandFilterCtx
RedisModuleCommandFilterCtx
;
typedef
struct
RedisModuleCommandFilterCtx
RedisModuleCommandFilterCtx
;
typedef
struct
RedisModuleCommandFilter
RedisModuleCommandFilter
;
typedef
struct
RedisModuleCommandFilter
RedisModuleCommandFilter
;
typedef
struct
RedisModuleInfoCtx
RedisModuleInfoCtx
;
typedef
int
(
*
RedisModuleCmdFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
);
typedef
int
(
*
RedisModuleCmdFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
);
typedef
void
(
*
RedisModuleDisconnectFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleBlockedClient
*
bc
);
typedef
void
(
*
RedisModuleDisconnectFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleBlockedClient
*
bc
);
...
@@ -173,6 +174,7 @@ typedef void (*RedisModuleTypeFreeFunc)(void *value);
...
@@ -173,6 +174,7 @@ typedef void (*RedisModuleTypeFreeFunc)(void *value);
typedef
void
(
*
RedisModuleClusterMessageReceiver
)(
RedisModuleCtx
*
ctx
,
const
char
*
sender_id
,
uint8_t
type
,
const
unsigned
char
*
payload
,
uint32_t
len
);
typedef
void
(
*
RedisModuleClusterMessageReceiver
)(
RedisModuleCtx
*
ctx
,
const
char
*
sender_id
,
uint8_t
type
,
const
unsigned
char
*
payload
,
uint32_t
len
);
typedef
void
(
*
RedisModuleTimerProc
)(
RedisModuleCtx
*
ctx
,
void
*
data
);
typedef
void
(
*
RedisModuleTimerProc
)(
RedisModuleCtx
*
ctx
,
void
*
data
);
typedef
void
(
*
RedisModuleCommandFilterFunc
)
(
RedisModuleCommandFilterCtx
*
filter
);
typedef
void
(
*
RedisModuleCommandFilterFunc
)
(
RedisModuleCommandFilterCtx
*
filter
);
typedef
void
(
*
RedisModuleInfoFunc
)(
RedisModuleInfoCtx
*
ctx
,
int
for_crash_report
);
#define REDISMODULE_TYPE_METHOD_VERSION 1
#define REDISMODULE_TYPE_METHOD_VERSION 1
typedef
struct
RedisModuleTypeMethods
{
typedef
struct
RedisModuleTypeMethods
{
...
@@ -317,6 +319,13 @@ RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictNext)(RedisModuleCtx *ct
...
@@ -317,6 +319,13 @@ RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictNext)(RedisModuleCtx *ct
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_DictPrev
)(
RedisModuleCtx
*
ctx
,
RedisModuleDictIter
*
di
,
void
**
dataptr
);
RedisModuleString
*
REDISMODULE_API_FUNC
(
RedisModule_DictPrev
)(
RedisModuleCtx
*
ctx
,
RedisModuleDictIter
*
di
,
void
**
dataptr
);
int
REDISMODULE_API_FUNC
(
RedisModule_DictCompareC
)(
RedisModuleDictIter
*
di
,
const
char
*
op
,
void
*
key
,
size_t
keylen
);
int
REDISMODULE_API_FUNC
(
RedisModule_DictCompareC
)(
RedisModuleDictIter
*
di
,
const
char
*
op
,
void
*
key
,
size_t
keylen
);
int
REDISMODULE_API_FUNC
(
RedisModule_DictCompare
)(
RedisModuleDictIter
*
di
,
const
char
*
op
,
RedisModuleString
*
key
);
int
REDISMODULE_API_FUNC
(
RedisModule_DictCompare
)(
RedisModuleDictIter
*
di
,
const
char
*
op
,
RedisModuleString
*
key
);
int
REDISMODULE_API_FUNC
(
RedisModule_RegisterInfoFunc
)(
RedisModuleCtx
*
ctx
,
RedisModuleInfoFunc
cb
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoSection
)(
RedisModuleInfoCtx
*
ctx
,
char
*
name
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldString
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
RedisModuleString
*
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldCString
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
char
*
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldDouble
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
double
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldLongLong
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
long
long
value
);
int
REDISMODULE_API_FUNC
(
RedisModule_AddInfoFieldULongLong
)(
RedisModuleInfoCtx
*
ctx
,
char
*
field
,
unsigned
long
long
value
);
/* Experimental APIs */
/* Experimental APIs */
#ifdef REDISMODULE_EXPERIMENTAL_API
#ifdef REDISMODULE_EXPERIMENTAL_API
...
@@ -490,6 +499,13 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
...
@@ -490,6 +499,13 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
DictPrev
);
REDISMODULE_GET_API
(
DictPrev
);
REDISMODULE_GET_API
(
DictCompare
);
REDISMODULE_GET_API
(
DictCompare
);
REDISMODULE_GET_API
(
DictCompareC
);
REDISMODULE_GET_API
(
DictCompareC
);
REDISMODULE_GET_API
(
RegisterInfoFunc
);
REDISMODULE_GET_API
(
AddInfoSection
);
REDISMODULE_GET_API
(
AddInfoFieldString
);
REDISMODULE_GET_API
(
AddInfoFieldCString
);
REDISMODULE_GET_API
(
AddInfoFieldDouble
);
REDISMODULE_GET_API
(
AddInfoFieldLongLong
);
REDISMODULE_GET_API
(
AddInfoFieldULongLong
);
#ifdef REDISMODULE_EXPERIMENTAL_API
#ifdef REDISMODULE_EXPERIMENTAL_API
REDISMODULE_GET_API
(
GetThreadSafeContext
);
REDISMODULE_GET_API
(
GetThreadSafeContext
);
...
...
src/server.c
View file @
e91d9a6f
...
@@ -3809,12 +3809,15 @@ sds genRedisInfoString(char *section) {
...
@@ -3809,12 +3809,15 @@ sds genRedisInfoString(char *section) {
time_t
uptime
=
server
.
unixtime
-
server
.
stat_starttime
;
time_t
uptime
=
server
.
unixtime
-
server
.
stat_starttime
;
int
j
;
int
j
;
struct
rusage
self_ru
,
c_ru
;
struct
rusage
self_ru
,
c_ru
;
int
allsections
=
0
,
defsections
=
0
;
int
allsections
=
0
,
defsections
=
0
,
everything
=
0
,
modules
=
0
;
int
sections
=
0
;
int
sections
=
0
;
if
(
section
==
NULL
)
section
=
"default"
;
if
(
section
==
NULL
)
section
=
"default"
;
allsections
=
strcasecmp
(
section
,
"all"
)
==
0
;
allsections
=
strcasecmp
(
section
,
"all"
)
==
0
;
defsections
=
strcasecmp
(
section
,
"default"
)
==
0
;
defsections
=
strcasecmp
(
section
,
"default"
)
==
0
;
everything
=
strcasecmp
(
section
,
"everything"
)
==
0
;
modules
=
strcasecmp
(
section
,
"modules"
)
==
0
;
if
(
everything
)
allsections
=
1
;
getrusage
(
RUSAGE_SELF
,
&
self_ru
);
getrusage
(
RUSAGE_SELF
,
&
self_ru
);
getrusage
(
RUSAGE_CHILDREN
,
&
c_ru
);
getrusage
(
RUSAGE_CHILDREN
,
&
c_ru
);
...
@@ -4357,6 +4360,17 @@ sds genRedisInfoString(char *section) {
...
@@ -4357,6 +4360,17 @@ sds genRedisInfoString(char *section) {
}
}
}
}
}
}
/* Get info from modules.
* if user asked for "everything" or "modules", or a specific section
* that's not found yet. */
if
(
everything
||
modules
||
(
!
allsections
&&
!
defsections
&&
sections
==
0
))
{
info
=
modulesCollectInfo
(
info
,
everything
||
modules
?
NULL
:
section
,
0
,
/* not a crash report */
sections
);
}
return
info
;
return
info
;
}
}
...
...
src/server.h
View file @
e91d9a6f
...
@@ -1528,6 +1528,7 @@ void moduleAcquireGIL(void);
...
@@ -1528,6 +1528,7 @@ void moduleAcquireGIL(void);
void
moduleReleaseGIL
(
void
);
void
moduleReleaseGIL
(
void
);
void
moduleNotifyKeyspaceEvent
(
int
type
,
const
char
*
event
,
robj
*
key
,
int
dbid
);
void
moduleNotifyKeyspaceEvent
(
int
type
,
const
char
*
event
,
robj
*
key
,
int
dbid
);
void
moduleCallCommandFilters
(
client
*
c
);
void
moduleCallCommandFilters
(
client
*
c
);
sds
modulesCollectInfo
(
sds
info
,
sds
section
,
int
for_crash_report
,
int
sections
);
/* Utils */
/* Utils */
long
long
ustime
(
void
);
long
long
ustime
(
void
);
...
...
tests/modules/Makefile
View file @
e91d9a6f
...
@@ -13,12 +13,17 @@ endif
...
@@ -13,12 +13,17 @@ endif
.SUFFIXES
:
.c .so .xo .o
.SUFFIXES
:
.c .so .xo .o
all
:
commandfilter.so
all
:
commandfilter.so
infotest.so
.c.xo
:
.c.xo
:
$(CC)
-I
../../src
$(CFLAGS)
$(SHOBJ_CFLAGS)
-fPIC
-c
$<
-o
$@
$(CC)
-I
../../src
$(CFLAGS)
$(SHOBJ_CFLAGS)
-fPIC
-c
$<
-o
$@
commandfilter.xo
:
../../src/redismodule.h
commandfilter.xo
:
../../src/redismodule.h
infotest.xo
:
../../src/redismodule.h
commandfilter.so
:
commandfilter.xo
commandfilter.so
:
commandfilter.xo
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
infotest.so
:
infotest.xo
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
tests/modules/infotest.c
0 → 100644
View file @
e91d9a6f
#include "redismodule.h"
#include <string.h>
void
InfoFunc
(
RedisModuleInfoCtx
*
ctx
,
int
for_crash_report
)
{
RedisModule_AddInfoSection
(
ctx
,
"Spanish"
);
RedisModule_AddInfoFieldCString
(
ctx
,
"uno"
,
"one"
);
RedisModule_AddInfoFieldLongLong
(
ctx
,
"dos"
,
2
);
RedisModule_AddInfoSection
(
ctx
,
"Italian"
);
RedisModule_AddInfoFieldLongLong
(
ctx
,
"due"
,
2
);
RedisModule_AddInfoFieldDouble
(
ctx
,
"tre"
,
3
.
3
);
if
(
for_crash_report
)
{
RedisModule_AddInfoSection
(
ctx
,
"Klingon"
);
RedisModule_AddInfoFieldCString
(
ctx
,
"one"
,
"wa’"
);
RedisModule_AddInfoFieldCString
(
ctx
,
"two"
,
"cha’"
);
RedisModule_AddInfoFieldCString
(
ctx
,
"three"
,
"wej"
);
}
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"infotest"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_RegisterInfoFunc
(
ctx
,
InfoFunc
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
tests/unit/moduleapi/infotest.tcl
0 → 100644
View file @
e91d9a6f
set testmodule
[
file normalize tests/modules/infotest.so
]
# Return value for INFO property
proc field
{
info property
}
{
if
{[
regexp
"
\r\n
$property:
(.*?)
\r\n
"
$info _ value
]}
{
set _ $value
}
}
start_server
{
tags
{
"modules"
}}
{
r module load $testmodule log-key 0
test
{
module info all
}
{
set info
[
r info all
]
# info all does not contain modules
assert
{
!
[
string match
"*Spanish*"
$info
]
}
assert
{
[
string match
"*used_memory*"
$info
]
}
}
test
{
module info everything
}
{
set info
[
r info everything
]
# info everything contains all default sections, but not ones for crash report
assert
{
[
string match
"*Spanish*"
$info
]
}
assert
{
[
string match
"*Italian*"
$info
]
}
assert
{
[
string match
"*used_memory*"
$info
]
}
assert
{
!
[
string match
"*Klingon*"
$info
]
}
field $info infotest_dos
}
{
2
}
test
{
module info modules
}
{
set info
[
r info modules
]
# info all does not contain modules
assert
{
[
string match
"*Spanish*"
$info
]
}
assert
{
!
[
string match
"*used_memory*"
$info
]
}
}
test
{
module info one module
}
{
set info
[
r info INFOTEST
]
# info all does not contain modules
assert
{
[
string match
"*Spanish*"
$info
]
}
assert
{
!
[
string match
"*used_memory*"
$info
]
}
}
test
{
module info one section
}
{
set info
[
r info INFOTEST_SPANISH
]
assert
{
!
[
string match
"*used_memory*"
$info
]
}
assert
{
!
[
string match
"*Italian*"
$info
]
}
field $info infotest_uno
}
{
one
}
# TODO: test crash report.
}
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