From 0ff010fa5777a2c9c8ef477079323828e2362781 Mon Sep 17 00:00:00 2001 From: Andrei Pelinescu-Onciul Date: Thu, 11 Mar 2010 19:38:04 +0100 Subject: [PATCH] malloc_test: new module for testing/debugging memory problems malloc_test is a new module for stressing the memory allocators and easily simulating out-of-memory conditions or memory leaks. Implemented RPCs: mt.mem_alloc size - allocates size bytes mt.mem_free [size] - frees at least size bytes. If size is missing frees everything allocated by any malloc_test test function. mt.mem_used - amount of currently allocated mem. by malloc_test. mt.mem_rnd_alloc min max total [unit] - allocates total bytes in chunks with the size randomly chosen between min and max. is optional and can be one of b-bytes, k - kb, m - mb, g -gb. mt.mem_test_start min max total min_int max_int total_time [unit] starts a malloc test that will take total_time to execute. Memory allocations will be performed at intervals randomly chosen between min_int and max_int (in ms). Each allocation will have a randomly chosen size between min and max bytes. After total bytes are allocated, everything is released/freed again and the allocations are restarted. The total_time is expressed in milliseconds. Several tests can be run in the same time. mt.mem_test_stop id - stops the test identified by id. mt.mem_test_destroy id - destroys the test identified by id (besides stopping it, it also frees all the data, including the statistics). mt.mem_test_destroy_all - destroys all the running or stopped tests. mt.mem_test_list [id] - Prints data about test id (running time, total allocations, errors a.s.o.). If id is missing, it will lists all the tests. Script functions: mt_mem_alloc(size) - equivalent to the mt.mem_alloc RPC. mt_mem_free(size) - equivalent to the mt.mem_free RPC. --- modules/malloc_test/Makefile | 15 + modules/malloc_test/doc/Makefile | 4 + modules/malloc_test/doc/functions.xml | 56 ++ modules/malloc_test/doc/malloc_test.xml | 47 ++ modules/malloc_test/doc/params.xml | 48 ++ modules/malloc_test/malloc_test.c | 887 ++++++++++++++++++++++++ 6 files changed, 1057 insertions(+) create mode 100644 modules/malloc_test/Makefile create mode 100644 modules/malloc_test/doc/Makefile create mode 100644 modules/malloc_test/doc/functions.xml create mode 100644 modules/malloc_test/doc/malloc_test.xml create mode 100644 modules/malloc_test/doc/params.xml create mode 100644 modules/malloc_test/malloc_test.c diff --git a/modules/malloc_test/Makefile b/modules/malloc_test/Makefile new file mode 100644 index 0000000000..a83936d77d --- /dev/null +++ b/modules/malloc_test/Makefile @@ -0,0 +1,15 @@ +# $Id$ +# +#malloc_test module makefile +# +# +# WARNING: do not run this directly, it should be run by the master Makefile + +include ../../Makefile.defs +auto_gen= +NAME=malloc_test.so +LIBS= + +DEFS+=-DSER_MOD_INTERFACE + +include ../../Makefile.modules diff --git a/modules/malloc_test/doc/Makefile b/modules/malloc_test/doc/Makefile new file mode 100644 index 0000000000..ea5c78836a --- /dev/null +++ b/modules/malloc_test/doc/Makefile @@ -0,0 +1,4 @@ +docs = malloc_test.xml + +docbook_dir=../../../docbook +include $(docbook_dir)/Makefile.module diff --git a/modules/malloc_test/doc/functions.xml b/modules/malloc_test/doc/functions.xml new file mode 100644 index 0000000000..8b69d02db5 --- /dev/null +++ b/modules/malloc_test/doc/functions.xml @@ -0,0 +1,56 @@ + + + +
+ + + + $Revision$ + $Date$ + + + + + Functions + +
+ <function>mt_mem_alloc(size)</function> + + Allocates size bytes. + + This is a debugging function for simulating memory + leaks or stressing the memory allocator. It should not + be used in production setups + + + <function>men_alloc</function> usage + +... +mem_alloc(1048576); # 1MB +... + + +
+ +
+ <function>mt_mem_free()</function> + + Frees all the memory allocated with mem_alloc() up to this + point. + + This is a debugging function for simulating memory + leaks or stressing the memory allocator. It should not + be used in production setups + + + <function>mem_free</function> usage + +... +mem_free(); +... + + +
+ +
diff --git a/modules/malloc_test/doc/malloc_test.xml b/modules/malloc_test/doc/malloc_test.xml new file mode 100644 index 0000000000..e53fe56db3 --- /dev/null +++ b/modules/malloc_test/doc/malloc_test.xml @@ -0,0 +1,47 @@ + +] +> + +
+ + + + Andrei + Pelinescu-Onciul + iptelorg GmbH +
+ andrei@iptel.org +
+
+
+ + 2010 + iptelorg GmbH + + + + $Revision$ + $Date$ + + +
+ + malloc_test Module + +
+ Overview + + This is a debugging/test module. It implements functions (both + script and rpcs) that can be used to stress the memory allocator + or force memory leaks. + + + This module should never be used in a production environment. + +
+ + +
diff --git a/modules/malloc_test/doc/params.xml b/modules/malloc_test/doc/params.xml new file mode 100644 index 0000000000..3fe8396551 --- /dev/null +++ b/modules/malloc_test/doc/params.xml @@ -0,0 +1,48 @@ + + + +
+ + + + $Revision$ + $Date$ + + + + Parameters + +
+ <varname>check_content</varname> + + When doing the tests, check also for the possibility of the + memory being overwritten. When activated, the allocated memory + will be filled with a special pattern, that will be checked on + free. + + + Default: 0 (off). + + + It can be changed also at runtime, via the rpc interface. + + + + Set <varname>check_content</varname> in the config file + + +modparam("malloc_test", "check_content", 1) + + + + + Set <varname>check_content</varname> at runtime via sercmd + + +$ sercmd cfg.set_now_int malloc_test check_content 1 + + +
+ +
diff --git a/modules/malloc_test/malloc_test.c b/modules/malloc_test/malloc_test.c new file mode 100644 index 0000000000..6cca8c8e86 --- /dev/null +++ b/modules/malloc_test/malloc_test.c @@ -0,0 +1,887 @@ +/*$Id$ + * + * Memory allocators debugging/test sip-router module. + * + * Copyright (C) 2010 iptelorg GmbH + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * History: + * -------- + * 2010-03-10 initial version (andrei) + */ + + +#include "../../sr_module.h" +#include "../../mem/mem.h" +#include "../../str.h" +#include "../../dprint.h" +#include "../../locking.h" +#include "../../atomic_ops.h" +#include "../../cfg/cfg.h" +#include "../../rpc.h" +#include "../../rand/fastrand.h" +#include "../../timer.h" +#include "../../mod_fix.h" + +MODULE_VERSION + +static int mt_mem_alloc_f(struct sip_msg*, char*,char*); +static int mt_mem_free_f(struct sip_msg*, char*,char*); +static int mod_init(void); +static void mod_destroy(void); + + +static cmd_export_t cmds[]={ + {"mt_mem_alloc", mt_mem_alloc_f, 1, fixup_var_int_1, + REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONSEND_ROUTE}, + {"mt_mem_free", mt_mem_free_f, 1, fixup_var_int_1, + REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONSEND_ROUTE}, + {0, 0, 0, 0, 0} +}; + + + +struct cfg_group_malloc_test { + int check_content; +}; + + +static struct cfg_group_malloc_test default_mt_cfg = { + 0 /* check_content, off by default */ +}; + +static void * mt_cfg = &default_mt_cfg; + +static cfg_def_t malloc_test_cfg_def[] = { + {"check_content", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0, + "check if allocated memory was overwritten by filling it with " + "a special pattern and checking it on free"}, + {0, 0, 0, 0, 0, 0} +}; + + + +static rpc_export_t mt_rpc[]; + + + +static param_export_t params[]={ + {"check_content", PARAM_INT, &default_mt_cfg.check_content}, + {0,0,0} +}; + + + +struct module_exports exports = { + "malloc_test", + cmds, + mt_rpc, /* RPC methods */ + params, + mod_init, /* module initialization function */ + 0, /* response function*/ + mod_destroy, /* destroy function */ + 0, /* oncancel function */ + 0 /* per-child init function */ +}; + + + +#define MC_F_CHECK_CONTENTS 1 + +struct mem_chunk{ + struct mem_chunk* next; + void* addr; + unsigned long size; + unsigned long flags; +}; + +struct allocated_list { + struct mem_chunk* chunks; + gen_lock_t lock; + volatile long size; +}; + +struct allocated_list* alloc_lst; + + +struct rnd_time_test { + unsigned long min; + unsigned long max; + unsigned long total; + unsigned long crt; + ticks_t min_intvrl; + ticks_t max_intvrl; + ticks_t stop_time; + ticks_t start_time; + unsigned long calls; + unsigned int errs; + unsigned int overfl; + struct rnd_time_test* next; + struct timer_ln timer; + int id; +}; + +struct rnd_time_test_lst { + struct rnd_time_test* tests; + gen_lock_t lock; + volatile int last_id; +}; + + +struct rnd_time_test_lst* rndt_lst; + +static unsigned long mem_unleak(unsigned long size); +static void mem_destroy_all_tests(); + +static int mod_init(void) +{ + WARN("This is a test/debugging module, don't use it in production\n"); + /* declare configuration */ + if (cfg_declare("malloc_test", malloc_test_cfg_def, &default_mt_cfg, + cfg_sizeof(malloc_test), &mt_cfg)){ + ERR("failed to register the configuration\n"); + goto error; + } + + alloc_lst = shm_malloc(sizeof(*alloc_lst)); + if (alloc_lst == 0) + goto error; + alloc_lst->chunks = 0; + atomic_set_long(&alloc_lst->size, 0); + if (lock_init(&alloc_lst->lock) == 0) + goto error; + rndt_lst = shm_malloc(sizeof(*rndt_lst)); + if (rndt_lst == 0) + goto error; + rndt_lst->tests = 0; + atomic_set_int(&rndt_lst->last_id, 0); + if (lock_init(&rndt_lst->lock) == 0) + goto error; + return 0; +error: + return -1; +} + + + +static void mod_destroy() +{ + if (rndt_lst) { + mem_destroy_all_tests(); + lock_destroy(&rndt_lst->lock); + shm_free(rndt_lst); + rndt_lst = 0; + } + if (alloc_lst) { + mem_unleak(-1); + lock_destroy(&alloc_lst->lock); + shm_free(alloc_lst); + alloc_lst = 0; + } +} + + + +/** record a memory chunk list entry. + * @param addr - address of the newly allocated memory + * @oaram size - size + * @return 0 on success, -1 on error (no more mem). + */ +static int mem_track(void* addr, unsigned long size) +{ + struct mem_chunk* mc; + unsigned long* d; + unsigned long r,i; + + mc = shm_malloc(sizeof(*mc)); + if (mc == 0) goto error; + mc->addr = addr; + mc->size = size; + mc->flags = 0; + if (cfg_get(malloc_test, mt_cfg, check_content)){ + mc->flags |= MC_F_CHECK_CONTENTS; + d = addr; + for (r = 0; r < size/sizeof(*d); r++){ + d[r]=~(unsigned long)d; + } + for (i=0; i< size % sizeof(*d); i++){ + ((char*)&d[r])[i]=~((unsigned long)d >> i*8); + } + } + lock_get(&alloc_lst->lock); + mc->next = alloc_lst->chunks; + alloc_lst->chunks = mc; + lock_release(&alloc_lst->lock); + atomic_add_long(&alloc_lst->size, size); + return 0; +error: + return -1; +} + + + +/** allocate memory. + * Allocates memory, but keeps track of it, so that mem_unleak() can + * free it. + * @param size - how many bytes + * @return 0 on success, -1 on error + */ +static int mem_leak(unsigned long size) +{ + void *d; + + d = shm_malloc(size); + if (d) { + if (mem_track(d, size) < 0){ + shm_free(d); + }else + return 0; + } + return -1; +} + + + +static void mem_chunk_free(struct mem_chunk* c) +{ + unsigned long* d; + unsigned long r,i; + int err; + + if (cfg_get(malloc_test, mt_cfg, check_content) && + c->flags & MC_F_CHECK_CONTENTS) { + d = c->addr; + err = 0; + for (r = 0; r < c->size/sizeof(*d); r++){ + if (d[r]!=~(unsigned long)d) + err++; + d[r] = r; /* fill it with something else */ + } + for (i=0; i< c->size % sizeof(*d); i++){ + if (((unsigned char*)&d[r])[i] != + (unsigned char)~((unsigned long)d >> i*8)) + err++; + ((char*)&d[r])[i] = (unsigned char)((unsigned long)d >> i*8); + } + if (err) + ERR("%d errors while checking %ld bytes at %p\n", err, c->size, d); + } + shm_free(c->addr); + c->addr = 0; + c->flags = 0; +} + + + +/** free memory. + * Frees previously allocated memory chunks until at least size bytes are + * released. Use -1 to free all, + * @param size - at least free size bytes. + * @return bytes_freed (>=0) + */ +static unsigned long mem_unleak(unsigned long size) +{ + struct mem_chunk** mc; + struct mem_chunk* t; + struct mem_chunk** min_chunk; + unsigned long freed; + + freed = 0; + min_chunk = 0; + lock_get(&alloc_lst->lock); + if (size>=atomic_get_long(&alloc_lst->size)){ + /* free all */ + for (mc = &alloc_lst->chunks; *mc; ){ + t = *mc; + mem_chunk_free(t); + freed += t->size; + *mc = t->next; + shm_free(t); + } + alloc_lst->chunks=0; + } else { + /* free at least size bytes, trying smaller chunks first */ + for (mc = &alloc_lst->chunks; *mc && (freed < size);) { + if ((*mc)->size <= (size - freed)) { + t = *mc; + mem_chunk_free(t); + freed += t->size; + *mc = t->next; + shm_free(t); + continue; + } else if (min_chunk == 0 || (*min_chunk)->size > (*mc)->size) { + /* find minimum remaining chunk */ + min_chunk = mc; + } + mc = &(*mc)->next; + } + if (size > freed && min_chunk) { + mc = min_chunk; + t = *mc; + mem_chunk_free(t); + freed += t->size; + *mc = (*mc)->next; + shm_free(t); + } + } + lock_release(&alloc_lst->lock); + atomic_add_long(&alloc_lst->size, -freed); + return freed; +} + + +#define MIN_ulong(a, b) \ + (unsigned long)((unsigned long)(a)<(unsigned long)(b)?(a):(b)) + +/* + * Randomly alloc. total_size bytes, in chunks of size between + * min & max. max - min should be smaller then 4G. + * @return < 0 if there were some alloc errors, 0 on success. + */ +static int mem_rnd_leak(unsigned long min, unsigned long max, + unsigned long total_size) +{ + unsigned long size; + unsigned long crt_size, crt_min; + int err; + + size = total_size; + err = 0; + while(size){ + crt_min = MIN_ulong(min, size); + crt_size = fastrand_max(MIN_ulong(max, size) - crt_min) + crt_min; + size -= crt_size; + err += mem_leak(crt_size) < 0; + } + return -err; +} + + + +/* test timer */ +static ticks_t tst_timer(ticks_t ticks, struct timer_ln* tl, void* data) +{ + struct rnd_time_test* tst; + ticks_t next_int; + ticks_t max_int; + unsigned long crt_size, crt_min, remaining; + + tst = data; + + next_int = 0; + max_int = 0; + + if (tst->total <= tst->crt) { + mem_unleak(tst->crt); + tst->crt = 0; + tst->overfl++; + } + remaining = tst->total - tst->crt; + crt_min = MIN_ulong(tst->min, remaining); + crt_size = fastrand_max(MIN_ulong(tst->max, remaining) - crt_min) + + crt_min; + if (mem_leak(crt_size) >= 0) + tst->crt += crt_size; + else + tst->errs ++; + tst->calls++; + + if (TICKS_GT(tst->stop_time, ticks)) { + next_int = fastrand_max(tst->max_intvrl - tst->min_intvrl) + + tst->min_intvrl; + max_int = tst->stop_time - ticks; + } else { + /* stop test */ + WARN("test %d time expired, stopping" + " (%d s runtime, %ld calls, %d overfl, %d errors," + " crt %ld bytes)\n", + tst->id, TICKS_TO_S(ticks - tst->start_time), + tst->calls, tst->overfl, tst->errs, tst->crt); + mem_unleak(tst->crt); + /* tst->crt = 0 */; + } + + /* 0 means stop stop, so if next_int == 0 => stop */ + return MIN_unsigned(next_int, max_int); +} + + +/* + * start a malloc test of a test_time length: + * - randomly between min_intvrl and max_intvrl, alloc. + * a random number of bytes, between min & max. + * - if total_size is reached, free everything. + * + * @returns 0 on success, -1 on error. + */ +static int mem_leak_time_test(unsigned long min, unsigned long max, + unsigned long total_size, + ticks_t min_intvrl, ticks_t max_intvrl, + ticks_t test_time) +{ + struct rnd_time_test* tst; + struct rnd_time_test* l; + ticks_t first_int; + + tst = shm_malloc(sizeof(*tst)); + if (tst == 0) + goto error; + memset(tst, 0, sizeof(*tst)); + tst->id = atomic_add_int(&rndt_lst->last_id, 1); + tst->min = min; + tst->max = max; + tst-> total = total_size; + tst->min_intvrl = min_intvrl; + tst->max_intvrl = max_intvrl; + tst->start_time = get_ticks_raw(); + tst->stop_time = get_ticks_raw() + test_time; + first_int = fastrand_max(max_intvrl - min_intvrl) + min_intvrl; + timer_init(&tst->timer, tst_timer, tst, 0); + lock_get(&rndt_lst->lock); + tst->next=rndt_lst->tests; + rndt_lst->tests=tst; + lock_release(&rndt_lst->lock); + if (timer_add(&tst->timer, MIN_unsigned(first_int, test_time)) < 0 ) + goto error; + return 0; +error: + if (tst) { + lock_get(&rndt_lst->lock); + for (l=rndt_lst->tests; l; l=l->next) + if (l->next == tst) { + l->next = tst->next; + break; + } + lock_release(&rndt_lst->lock); + shm_free(tst); + } + return -1; +} + + +static int is_mem_test_stopped(struct rnd_time_test* tst) +{ + return TICKS_LE(tst->stop_time, get_ticks_raw()); +} + +/** stops test tst. + * @return 0 on success, -1 on error (test already stopped) + */ +static int mem_test_stop_tst(struct rnd_time_test* tst) +{ + if (!is_mem_test_stopped(tst)) { + if (timer_del(&tst->timer) == 0) { + tst->stop_time=get_ticks_raw(); + return 0; + } + } + return -1; +} + + +/** stops test id. + * @return 0 on success, -1 on error (not found). + */ +static int mem_test_stop(int id) +{ + struct rnd_time_test* tst; + + lock_get(&rndt_lst->lock); + for (tst = rndt_lst->tests; tst; tst = tst->next) + if (tst->id == id) { + mem_test_stop_tst(tst); + break; + } + lock_release(&rndt_lst->lock); + return -(tst == 0); +} + + +static void mem_destroy_all_tests() +{ + struct rnd_time_test* tst; + struct rnd_time_test* nxt; + + lock_get(&rndt_lst->lock); + for (tst = rndt_lst->tests; tst;) { + nxt = tst->next; + mem_test_stop_tst(tst); + shm_free(tst); + tst = nxt; + } + rndt_lst->tests = 0; + lock_release(&rndt_lst->lock); +} + + +static int mem_test_destroy(int id) +{ + struct rnd_time_test* tst; + struct rnd_time_test** crt_lnk; + + lock_get(&rndt_lst->lock); + for (tst = 0, crt_lnk = &rndt_lst->tests; *crt_lnk; + crt_lnk = &(*crt_lnk)->next) + if ((*crt_lnk)->id == id) { + tst=*crt_lnk; + mem_test_stop_tst(tst); + *crt_lnk=tst->next; + shm_free(tst); + break; + } + lock_release(&rndt_lst->lock); + return -(tst == 0); +} + +/* script functions: */ + + +static int mt_mem_alloc_f(struct sip_msg* msg, char* sz, char* foo) +{ + int size; + + if (sz == 0 || get_int_fparam(&size, msg, (fparam_t*)sz) < 0) + return -1; + return mem_leak(size)>=0?1:-1; +} + + + +static int mt_mem_free_f(struct sip_msg* msg, char* sz, char* foo) +{ + int size; + unsigned long freed; + + size=-1; + if (sz != 0 && get_int_fparam(&size, msg, (fparam_t*)sz) < 0) + return -1; + freed=mem_unleak(size); + return (freed==0)?1:freed; +} + + + +/* RPC exports: */ + + + +/* helper functions, parses an optional b[ytes]|k|m|g to a numeric shift value + (e.g. b -> 0, k -> 10, ...) + returns bit shift value on success, -1 on error +*/ +static int rpc_get_size_mod(rpc_t* rpc, void* c) +{ + char* m; + + if (rpc->scan(c, "*s", &m) > 0) { + switch(*m) { + case 'b': + case 'B': + return 0; + case 'k': + case 'K': + return 10; + case 'm': + case 'M': + return 20; + case 'g': + case 'G': + return 30; + default: + rpc->fault(c, 500, "bad param use b|k|m|g"); + return -1; + } + } + return 0; +} + + + +static const char* rpc_mt_alloc_doc[2] = { + "Allocates the specified number of bytes (debugging/test function)." + "Use b|k|m|g to specify the desired size unit", + 0 +}; + +static void rpc_mt_alloc(rpc_t* rpc, void* c) +{ + int size; + int rs; + + if (rpc->scan(c, "d", &size) < 1) { + return; + } + rs=rpc_get_size_mod(rpc, c); + if (rs<0) + /* fault already generated on rpc_get_size_mod() error */ + return; + if (mem_leak((unsigned long)size << rs) < 0) { + rpc->fault(c, 400, "memory allocation failed"); + } + return; +} + + +static const char* rpc_mt_free_doc[2] = { + "Frees the specified number of bytes, previously allocated by one of the" + " other malloc_test functions (e.g. mt.mem_alloc or the script " + "mt_mem_alloc). Use b|k|m|g to specify the desired size unit." + "Returns the number of bytes freed (can be higher or" + " smaller then the requested size)", + 0 +}; + + +static void rpc_mt_free(rpc_t* rpc, void* c) +{ + int size; + int rs; + + size = -1; + rs = 0; + if (rpc->scan(c, "*d", &size) > 0) { + /* found size, look if a size modifier is present */ + rs=rpc_get_size_mod(rpc, c); + if (rs<0) + /* fault already generated on rpc_get_size_mod() error */ + return; + } + rpc->add(c, "d", (int)(mem_unleak((unsigned long)size << rs) >> rs)); + return; +} + + + +static const char* rpc_mt_used_doc[2] = { + "Returns how many bytes are currently allocated via the mem_alloc module" + " functions. Use b|k|m|g to specify the desired size unit.", + 0 +}; + + +static void rpc_mt_used(rpc_t* rpc, void* c) +{ + int rs; + + rs = 0; + rs=rpc_get_size_mod(rpc, c); + if (rs<0) + /* fault already generated on rpc_get_size_mod() error */ + return; + rpc->add(c, "d", (int)(atomic_get_long(&alloc_lst->size) >> rs)); + return; +} + + +static const char* rpc_mt_rnd_alloc_doc[2] = { + "Takes 4 parameters: min, max, total_size and an optional unit (b|k|m|g)." + " It will allocate total_size memory, in pieces of random size between" + "min .. max (inclusive).", + 0 +}; + + +static void rpc_mt_rnd_alloc(rpc_t* rpc, void* c) +{ + int min, max, total_size; + int rs; + + if (rpc->scan(c, "ddd", &min, &max, &total_size) < 3) { + return; + } + rs=rpc_get_size_mod(rpc, c); + if (rs<0) + /* fault already generated on rpc_get_size_mod() error */ + return; + if (min > max || min < 0 || max > total_size) { + rpc->fault(c, 400, "invalid parameter values"); + return; + } + if (mem_rnd_leak((unsigned long)min << rs, + (unsigned long)max << rs, + (unsigned long)total_size <fault(c, 400, "memory allocation failed"); + } + return; +} + + +static const char* rpc_mt_test_start_doc[2] = { + "Takes 7 parameters: min, max, total_size, min_interval, max_interval, " + "test_time and an optional size unit (b|k|m|g). All the time units are ms." + " It will run a memory allocation test for test_time ms. At a random" + " interval between min_interval and max_interval ms. it will allocate a" + " memory chunk with random size, between min and max. Each time total_size" + " is reached, it will free all the memory allocated and start again." + "Returns the test id (integer)", + 0 +}; + + +static void rpc_mt_test_start(rpc_t* rpc, void* c) +{ + int min, max, total_size; + int min_intvrl, max_intvrl, total_time; + int rs; + + if (rpc->scan(c, "dddddd", &min, &max, &total_size, + &min_intvrl, &max_intvrl, &total_time) < 6) { + return; + } + rs=rpc_get_size_mod(rpc, c); + if (rs<0) + /* fault already generated on rpc_get_size_mod() error */ + return; + if (min > max || min < 0 || max > total_size) { + rpc->fault(c, 400, "invalid size parameters values"); + return; + } + if (min_intvrl > max_intvrl || min_intvrl <= 0 || max_intvrl > total_time){ + rpc->fault(c, 400, "invalid time intervals values"); + return; + } + if (mem_leak_time_test((unsigned long)min << rs, + (unsigned long)max << rs, + (unsigned long)total_size <fault(c, 400, "memory allocation failed"); + } + return; +} + + +static const char* rpc_mt_test_stop_doc[2] = { + "Takes 1 parameter: the test id. It will stop the corresponding test." + "Note: the test is stopped, but not destroyed." , + 0 +}; + + +static void rpc_mt_test_stop(rpc_t* rpc, void* c) +{ + int id; + + if (rpc->scan(c, "d", &id) < 1) { + return; + } + if (mem_test_stop(id)<0) { + rpc->fault(c, 400, "test %d not found", id); + } + return; +} + + +static const char* rpc_mt_test_destroy_doc[2] = { + "Takes 1 parameter: the test id. It will destroy the corresponding test.", + 0 +}; + + +static void rpc_mt_test_destroy(rpc_t* rpc, void* c) +{ + int id; + + if (rpc->scan(c, "*d", &id) > 0 && id!=-1) { + if (mem_test_destroy(id) < 0 ) + rpc->fault(c, 400, "test %d not found", id); + } else { + mem_destroy_all_tests(); + } + return; +} + + +static const char* rpc_mt_test_destroy_all_doc[2] = { + "It will destroy all the tests (running or stopped).", + 0 +}; + + +static void rpc_mt_test_destroy_all(rpc_t* rpc, void* c) +{ + mem_destroy_all_tests(); + return; +} + + +static const char* rpc_mt_test_list_doc[2] = { + "If a test id parameter is provided it will list the corresponding test," + " else it will list all of them", + 0 +}; + + +static void rpc_mt_test_list(rpc_t* rpc, void* c) +{ + int id, rs; + struct rnd_time_test* tst; + void *h; + + rs = 0; + if (rpc->scan(c, "*d", &id) < 1) { + id = -1; + } else { + rs=rpc_get_size_mod(rpc, c); + if (rs < 0) + return; + } + lock_get(&rndt_lst->lock); + for (tst = rndt_lst->tests; tst; tst=tst->next) + if (tst->id == id || id == -1) { + rpc->add(c, "{", &h); + rpc->struct_add(h, "dddddddddd", + "ID ", tst->id, + "run time (s) ", (int)TICKS_TO_S(( + TICKS_LE(tst->stop_time, + get_ticks_raw()) ? + tst->stop_time : get_ticks_raw()) - + tst->start_time), + "remaining (s)", TICKS_LE(tst->stop_time, + get_ticks_raw()) ? 0 : + (int)TICKS_TO_S(tst->stop_time - + get_ticks_raw()), + "allocations ", (int)tst->calls, + "errors ", (int)tst->errs, + "overflows ", (int)tst->overfl, + "total alloc ", (int)((tst->crt + + tst->overfl * tst->total)>>rs), + "min ", (int)(tst->min>>rs), + "max ", (int)(tst->max>>rs), + "total ", (int)(tst->total>>rs) ); + if (id != -1) break; + } + lock_release(&rndt_lst->lock); + + return; +} + + +static rpc_export_t mt_rpc[] = { + {"mt.mem_alloc", rpc_mt_alloc, rpc_mt_alloc_doc, 0}, + {"mt.mem_free", rpc_mt_free, rpc_mt_free_doc, 0}, + {"mt.mem_used", rpc_mt_used, rpc_mt_used_doc, 0}, + {"mt.mem_rnd_alloc", rpc_mt_rnd_alloc, rpc_mt_rnd_alloc_doc, 0}, + {"mt.mem_test_start", rpc_mt_test_start, rpc_mt_test_start_doc, 0}, + {"mt.mem_test_stop", rpc_mt_test_stop, rpc_mt_test_stop_doc, 0}, + {"mt.mem_test_destroy", rpc_mt_test_destroy, rpc_mt_test_destroy_doc, 0}, + {"mt.mem_test_destroy_all", rpc_mt_test_destroy_all, + rpc_mt_test_destroy_all_doc, 0}, + {"mt.mem_test_list", rpc_mt_test_list, rpc_mt_test_list_doc, 0}, + {0, 0, 0, 0} +}; + -- 2.20.1