Merge pull request #6270 from oranagra/modules_info
Extend modules API to allow modules report to redis INFO
This commit is contained in:
commit
2e2fe98f9c
@ -13,4 +13,4 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
make -C tests/modules && \
|
make -C tests/modules && \
|
||||||
$TCLSH tests/test_helper.tcl --single unit/moduleapi/commandfilter --single unit/moduleapi/fork --single unit/moduleapi/testrdb "${@}"
|
$TCLSH tests/test_helper.tcl --single unit/moduleapi/commandfilter --single unit/moduleapi/fork --single unit/moduleapi/testrdb --single unit/moduleapi/infotest "${@}"
|
||||||
|
@ -1365,6 +1365,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");
|
||||||
|
215
src/module.c
215
src/module.c
@ -42,6 +42,17 @@
|
|||||||
* 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 */
|
||||||
|
int in_dict_field; /* indication that we're curreintly appending to a dict */
|
||||||
|
} 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. */
|
||||||
@ -54,6 +65,7 @@ struct RedisModule {
|
|||||||
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 */
|
||||||
int options; /* Module options and capabilities. */
|
int options; /* Module options and capabilities. */
|
||||||
|
RedisModuleInfoFunc info_cb; /* callback for module to add INFO fields. */
|
||||||
};
|
};
|
||||||
typedef struct RedisModule RedisModule;
|
typedef struct RedisModule RedisModule;
|
||||||
|
|
||||||
@ -4796,6 +4808,194 @@ 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
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
int RM_InfoEndDictField(RedisModuleInfoCtx *ctx);
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
* NULL or empty string indicates the default section (only <modulename>) is used.
|
||||||
|
* When return value is REDISMODULE_ERR, the section should and will be skipped. */
|
||||||
|
int RM_InfoAddSection(RedisModuleInfoCtx *ctx, char *name) {
|
||||||
|
sds full_name = sdsdup(ctx->module->name);
|
||||||
|
if (name != NULL && strlen(name) > 0)
|
||||||
|
full_name = sdscatfmt(full_name, "_%s", name);
|
||||||
|
|
||||||
|
/* Implicitly end dicts, instead of returning an error which is likely un checked. */
|
||||||
|
if (ctx->in_dict_field)
|
||||||
|
RM_InfoEndDictField(ctx);
|
||||||
|
|
||||||
|
/* 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 = sdscatfmt(ctx->info, "# %S\r\n", full_name);
|
||||||
|
ctx->in_section = 1;
|
||||||
|
sdsfree(full_name);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Starts a dict field, similar to the ones in INFO KEYSPACE. Use normal
|
||||||
|
* RedisModule_InfoAddField* functions to add the items to this field, and
|
||||||
|
* terminate with RedisModule_InfoEndDictField. */
|
||||||
|
int RM_InfoBeginDictField(RedisModuleInfoCtx *ctx, char *name) {
|
||||||
|
if (!ctx->in_section)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
/* Implicitly end dicts, instead of returning an error which is likely un checked. */
|
||||||
|
if (ctx->in_dict_field)
|
||||||
|
RM_InfoEndDictField(ctx);
|
||||||
|
ctx->info = sdscatfmt(ctx->info,
|
||||||
|
"%s_%s:",
|
||||||
|
ctx->module->name,
|
||||||
|
name);
|
||||||
|
ctx->in_dict_field = 1;
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ends a dict field, see RedisModule_InfoBeginDictField */
|
||||||
|
int RM_InfoEndDictField(RedisModuleInfoCtx *ctx) {
|
||||||
|
if (!ctx->in_dict_field)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
/* trim the last ',' if found. */
|
||||||
|
if (ctx->info[sdslen(ctx->info)-1]==',')
|
||||||
|
sdsIncrLen(ctx->info, -1);
|
||||||
|
ctx->info = sdscat(ctx->info, "\r\n");
|
||||||
|
ctx->in_dict_field = 0;
|
||||||
|
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_InfoAddFieldString(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) {
|
||||||
|
if (!ctx->in_section)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
if (ctx->in_dict_field) {
|
||||||
|
ctx->info = sdscatfmt(ctx->info,
|
||||||
|
"%s=%S,",
|
||||||
|
field,
|
||||||
|
(sds)value->ptr);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
ctx->info = sdscatfmt(ctx->info,
|
||||||
|
"%s_%s:%S\r\n",
|
||||||
|
ctx->module->name,
|
||||||
|
field,
|
||||||
|
(sds)value->ptr);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RM_InfoAddFieldCString(RedisModuleInfoCtx *ctx, char *field, char *value) {
|
||||||
|
if (!ctx->in_section)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
if (ctx->in_dict_field) {
|
||||||
|
ctx->info = sdscatfmt(ctx->info,
|
||||||
|
"%s=%s,",
|
||||||
|
field,
|
||||||
|
value);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
ctx->info = sdscatfmt(ctx->info,
|
||||||
|
"%s_%s:%s\r\n",
|
||||||
|
ctx->module->name,
|
||||||
|
field,
|
||||||
|
value);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RM_InfoAddFieldDouble(RedisModuleInfoCtx *ctx, char *field, double value) {
|
||||||
|
if (!ctx->in_section)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
if (ctx->in_dict_field) {
|
||||||
|
ctx->info = sdscatprintf(ctx->info,
|
||||||
|
"%s=%.17g,",
|
||||||
|
field,
|
||||||
|
value);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
ctx->info = sdscatprintf(ctx->info,
|
||||||
|
"%s_%s:%.17g\r\n",
|
||||||
|
ctx->module->name,
|
||||||
|
field,
|
||||||
|
value);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RM_InfoAddFieldLongLong(RedisModuleInfoCtx *ctx, char *field, long long value) {
|
||||||
|
if (!ctx->in_section)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
if (ctx->in_dict_field) {
|
||||||
|
ctx->info = sdscatfmt(ctx->info,
|
||||||
|
"%s=%I,",
|
||||||
|
field,
|
||||||
|
value);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
ctx->info = sdscatfmt(ctx->info,
|
||||||
|
"%s_%s:%I\r\n",
|
||||||
|
ctx->module->name,
|
||||||
|
field,
|
||||||
|
value);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RM_InfoAddFieldULongLong(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) {
|
||||||
|
if (!ctx->in_section)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
if (ctx->in_dict_field) {
|
||||||
|
ctx->info = sdscatfmt(ctx->info,
|
||||||
|
"%s=%U,",
|
||||||
|
field,
|
||||||
|
value);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
ctx->info = sdscatfmt(ctx->info,
|
||||||
|
"%s_%s:%U\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);
|
||||||
|
/* Implicitly end dicts (no way to handle errors, and we must add the newline). */
|
||||||
|
if (info_ctx.in_dict_field)
|
||||||
|
RM_InfoEndDictField(&info_ctx);
|
||||||
|
info = info_ctx.info;
|
||||||
|
sections = info_ctx.sections;
|
||||||
|
}
|
||||||
|
dictReleaseIterator(di);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
* Modules utility APIs
|
* Modules utility APIs
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
@ -5512,9 +5712,9 @@ sds genModulesInfoString(sds info) {
|
|||||||
sds usedby = genModulesInfoStringRenderModulesList(module->usedby);
|
sds usedby = genModulesInfoStringRenderModulesList(module->usedby);
|
||||||
sds using = genModulesInfoStringRenderModulesList(module->using);
|
sds using = genModulesInfoStringRenderModulesList(module->using);
|
||||||
sds options = genModulesInfoStringRenderModuleOptions(module);
|
sds options = genModulesInfoStringRenderModuleOptions(module);
|
||||||
info = sdscatprintf(info,
|
info = sdscatfmt(info,
|
||||||
"module:name=%s,ver=%d,api=%d,filters=%d,"
|
"module:name=%S,ver=%i,api=%i,filters=%i,"
|
||||||
"usedby=%s,using=%s,options=%s\r\n",
|
"usedby=%S,using=%S,options=%S\r\n",
|
||||||
name, module->ver, module->apiver,
|
name, module->ver, module->apiver,
|
||||||
(int)listLength(module->filters), usedby, using, options);
|
(int)listLength(module->filters), usedby, using, options);
|
||||||
sdsfree(usedby);
|
sdsfree(usedby);
|
||||||
@ -5761,4 +5961,13 @@ void moduleRegisterCoreAPI(void) {
|
|||||||
REGISTER_API(Fork);
|
REGISTER_API(Fork);
|
||||||
REGISTER_API(ExitFromChild);
|
REGISTER_API(ExitFromChild);
|
||||||
REGISTER_API(KillForkChild);
|
REGISTER_API(KillForkChild);
|
||||||
|
REGISTER_API(RegisterInfoFunc);
|
||||||
|
REGISTER_API(InfoAddSection);
|
||||||
|
REGISTER_API(InfoBeginDictField);
|
||||||
|
REGISTER_API(InfoEndDictField);
|
||||||
|
REGISTER_API(InfoAddFieldString);
|
||||||
|
REGISTER_API(InfoAddFieldCString);
|
||||||
|
REGISTER_API(InfoAddFieldDouble);
|
||||||
|
REGISTER_API(InfoAddFieldLongLong);
|
||||||
|
REGISTER_API(InfoAddFieldULongLong);
|
||||||
}
|
}
|
||||||
|
@ -167,6 +167,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);
|
||||||
@ -183,6 +184,7 @@ typedef void (*RedisModuleClusterMessageReceiver)(RedisModuleCtx *ctx, const cha
|
|||||||
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 (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);
|
typedef void (*RedisModuleForkDoneHandler) (int exitcode, int bysignal, void *user_data);
|
||||||
|
typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report);
|
||||||
|
|
||||||
#define REDISMODULE_TYPE_METHOD_VERSION 2
|
#define REDISMODULE_TYPE_METHOD_VERSION 2
|
||||||
typedef struct RedisModuleTypeMethods {
|
typedef struct RedisModuleTypeMethods {
|
||||||
@ -333,6 +335,15 @@ 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_InfoAddSection)(RedisModuleInfoCtx *ctx, char *name);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, char *name);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value);
|
||||||
|
|
||||||
/* Experimental APIs */
|
/* Experimental APIs */
|
||||||
#ifdef REDISMODULE_EXPERIMENTAL_API
|
#ifdef REDISMODULE_EXPERIMENTAL_API
|
||||||
@ -512,6 +523,15 @@ 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(InfoAddSection);
|
||||||
|
REDISMODULE_GET_API(InfoBeginDictField);
|
||||||
|
REDISMODULE_GET_API(InfoEndDictField);
|
||||||
|
REDISMODULE_GET_API(InfoAddFieldString);
|
||||||
|
REDISMODULE_GET_API(InfoAddFieldCString);
|
||||||
|
REDISMODULE_GET_API(InfoAddFieldDouble);
|
||||||
|
REDISMODULE_GET_API(InfoAddFieldLongLong);
|
||||||
|
REDISMODULE_GET_API(InfoAddFieldULongLong);
|
||||||
|
|
||||||
#ifdef REDISMODULE_EXPERIMENTAL_API
|
#ifdef REDISMODULE_EXPERIMENTAL_API
|
||||||
REDISMODULE_GET_API(GetThreadSafeContext);
|
REDISMODULE_GET_API(GetThreadSafeContext);
|
||||||
|
16
src/server.c
16
src/server.c
@ -3861,12 +3861,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);
|
||||||
@ -4425,6 +4428,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1555,6 +1555,7 @@ void ModuleForkDoneHandler(int exitcode, int bysignal);
|
|||||||
int TerminateModuleForkChild(int child_pid, int wait);
|
int TerminateModuleForkChild(int child_pid, int wait);
|
||||||
ssize_t rdbSaveModulesAux(rio *rdb, int when);
|
ssize_t rdbSaveModulesAux(rio *rdb, int when);
|
||||||
int moduleAllDatatypesHandleErrors();
|
int moduleAllDatatypesHandleErrors();
|
||||||
|
sds modulesCollectInfo(sds info, sds section, int for_crash_report, int sections);
|
||||||
|
|
||||||
/* Utils */
|
/* Utils */
|
||||||
long long ustime(void);
|
long long ustime(void);
|
||||||
|
@ -13,7 +13,7 @@ endif
|
|||||||
|
|
||||||
.SUFFIXES: .c .so .xo .o
|
.SUFFIXES: .c .so .xo .o
|
||||||
|
|
||||||
all: commandfilter.so testrdb.so fork.so
|
all: commandfilter.so testrdb.so fork.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 $@
|
||||||
@ -21,6 +21,7 @@ all: commandfilter.so testrdb.so fork.so
|
|||||||
commandfilter.xo: ../../src/redismodule.h
|
commandfilter.xo: ../../src/redismodule.h
|
||||||
fork.xo: ../../src/redismodule.h
|
fork.xo: ../../src/redismodule.h
|
||||||
testrdb.xo: ../../src/redismodule.h
|
testrdb.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
|
||||||
@ -30,3 +31,7 @@ fork.so: fork.xo
|
|||||||
|
|
||||||
testrdb.so: testrdb.xo
|
testrdb.so: testrdb.xo
|
||||||
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
|
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
|
||||||
|
|
||||||
|
infotest.so: infotest.xo
|
||||||
|
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc
|
||||||
|
|
||||||
|
41
tests/modules/infotest.c
Normal file
41
tests/modules/infotest.c
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include "redismodule.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void InfoFunc(RedisModuleInfoCtx *ctx, int for_crash_report) {
|
||||||
|
RedisModule_InfoAddSection(ctx, "");
|
||||||
|
RedisModule_InfoAddFieldLongLong(ctx, "global", -2);
|
||||||
|
|
||||||
|
RedisModule_InfoAddSection(ctx, "Spanish");
|
||||||
|
RedisModule_InfoAddFieldCString(ctx, "uno", "one");
|
||||||
|
RedisModule_InfoAddFieldLongLong(ctx, "dos", 2);
|
||||||
|
|
||||||
|
RedisModule_InfoAddSection(ctx, "Italian");
|
||||||
|
RedisModule_InfoAddFieldLongLong(ctx, "due", 2);
|
||||||
|
RedisModule_InfoAddFieldDouble(ctx, "tre", 3.3);
|
||||||
|
|
||||||
|
RedisModule_InfoAddSection(ctx, "keyspace");
|
||||||
|
RedisModule_InfoBeginDictField(ctx, "db0");
|
||||||
|
RedisModule_InfoAddFieldLongLong(ctx, "keys", 3);
|
||||||
|
RedisModule_InfoAddFieldLongLong(ctx, "expires", 1);
|
||||||
|
RedisModule_InfoEndDictField(ctx);
|
||||||
|
|
||||||
|
if (for_crash_report) {
|
||||||
|
RedisModule_InfoAddSection(ctx, "Klingon");
|
||||||
|
RedisModule_InfoAddFieldCString(ctx, "one", "wa’");
|
||||||
|
RedisModule_InfoAddFieldCString(ctx, "two", "cha’");
|
||||||
|
RedisModule_InfoAddFieldCString(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;
|
||||||
|
}
|
63
tests/unit/moduleapi/infotest.tcl
Normal file
63
tests/unit/moduleapi/infotest.tcl
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
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 "*infotest_*" $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 "*infotest_global*" $info] }
|
||||||
|
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 "*infotest_global*" $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] }
|
||||||
|
field $info infotest_global
|
||||||
|
} {-2}
|
||||||
|
|
||||||
|
test {module info one section} {
|
||||||
|
set info [r info INFOTEST_SPANISH]
|
||||||
|
assert { ![string match "*used_memory*" $info] }
|
||||||
|
assert { ![string match "*Italian*" $info] }
|
||||||
|
assert { ![string match "*infotest_global*" $info] }
|
||||||
|
field $info infotest_uno
|
||||||
|
} {one}
|
||||||
|
|
||||||
|
test {module info dict} {
|
||||||
|
set info [r info infotest_keyspace]
|
||||||
|
set keyspace [field $info infotest_db0]
|
||||||
|
set keys [scan [regexp -inline {keys\=([\d]*)} $keyspace] keys=%d]
|
||||||
|
} {3}
|
||||||
|
|
||||||
|
# TODO: test crash report.
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user