1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030 |
- /*$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;
- int realloc_p; /* realloc probability */
- };
- static struct cfg_group_malloc_test default_mt_cfg = {
- 0, /* check_content, off by default */
- 0 /* realloc probability, 0 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."},
- {"realloc_p", CFG_VAR_INT | CFG_ATOMIC, 0, 90, 0, 0,
- "realloc probability in percents. During tests and mem_rnd_alloc"
- " realloc_p percents of the allocations will be made by realloc'ing"
- " and existing chunk. The maximum value is limited to 90, to avoid"
- " very long mem_rnd_alloc runs (a realloc might also free memory)." },
- {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;
- volatile int no;
- };
- 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 long reallocs;
- 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);
- atomic_set_int(&alloc_lst->no, 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[r];
- }
- for (i=0; i< size % sizeof(*d); i++){
- ((char*)&d[r])[i]=~((unsigned long)&d[r] >> 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);
- atomic_inc_int(&alloc_lst->no);
- 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;
- }
- /* realloc a chunk, unsafe (requires external locking) version.
- * @return 0 on success, -1 on error
- */
- static int _mem_chunk_realloc_unsafe(struct mem_chunk *c, unsigned long size)
- {
- unsigned long* d;
- int r, i;
-
- d = shm_realloc(c->addr, size);
- if (d) {
- if (cfg_get(malloc_test, mt_cfg, check_content) &&
- c->flags & MC_F_CHECK_CONTENTS) {
- /* re-fill the test patterns (the address might have changed
- and they depend on it) */
- for (r = 0; r < size/sizeof(*d); r++){
- d[r]=~(unsigned long)&d[r];
- }
- for (i=0; i< size % sizeof(*d); i++){
- ((char*)&d[r])[i]=~((unsigned long)&d[r] >> i*8);
- }
- }
- c->addr = d;
- c->size = size;
- 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[r])
- err++;
- d[r] = (unsigned long)&d[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[r] >> i*8))
- err++;
- ((char*)&d[r])[i] = (unsigned char)((unsigned long)&d[r] >> 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;
- unsigned int no;
-
- freed = 0;
- no = 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;
- no++;
- *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;
- no++;
- *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;
- no++;
- *mc = (*mc)->next;
- shm_free(t);
- }
- }
- lock_release(&alloc_lst->lock);
- atomic_add_long(&alloc_lst->size, -freed);
- atomic_add_int(&alloc_lst->no, -no);
- return freed;
- }
- /** realloc randomly size bytes.
- * Chooses randomly a previously allocated chunk and realloc's it.
- * @param size - size.
- * @param diff - filled with difference, >= 0 means more bytes were alloc.,
- * < 0 means bytes were freed.
- * @return >= 0 on success, -1 on error/ not found
- * (empty list is a valid error reason)
- */
- static int mem_rnd_realloc(unsigned long size, long* diff)
- {
- struct mem_chunk* t;
- int ret;
- int target, i;
-
- *diff = 0;
- ret = -1;
- lock_get(&alloc_lst->lock);
- target = fastrand_max(atomic_get_int(&alloc_lst->no));
- for (t = alloc_lst->chunks, i=0; t; t=t->next, i++ ){
- if (target == i) {
- *diff = (long)size - (long)t->size;
- if ((ret=_mem_chunk_realloc_unsafe(t, size)) < 0)
- *diff = 0;
- break;
- }
- }
- lock_release(&alloc_lst->lock);
- atomic_add_long(&alloc_lst->size, *diff);
- return ret;
- }
- #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;
- long diff;
- int err, p;
-
- 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;
- p = cfg_get(malloc_test, mt_cfg, realloc_p);
- if (p && ((fastrand_max(99) +1) <= p)){
- if (mem_rnd_realloc(crt_size, &diff) == 0){
- size -= diff;
- continue;
- } /* else fallback to normal alloc. */
- }
- 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;
- long diff;
- int p;
-
- 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;
- p = cfg_get(malloc_test, mt_cfg, realloc_p);
- if (p && ((fastrand_max(99) +1) <= p)) {
- if (mem_rnd_realloc(crt_size, &diff) == 0){
- tst->crt -= diff;
- tst->reallocs++;
- goto skip_alloc;
- }
- }
- if (mem_leak(crt_size) >= 0)
- tst->crt += crt_size;
- else
- tst->errs ++;
- skip_alloc:
- 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 test id (>=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;
- int id;
-
- tst = shm_malloc(sizeof(*tst));
- if (tst == 0)
- goto error;
- memset(tst, 0, sizeof(*tst));
- id = 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 id;
- 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_realloc_doc[2] = {
- "Reallocates the specified number of bytes from a pre-allocated"
- " randomly selected memory chunk. If no pre-allocated memory"
- " chunks exists, it will fail."
- " Make sure mt.mem_used is non 0 or call mt.mem_alloc prior to calling"
- " this function."
- " Returns the difference in bytes (<0 if bytes were freed, >0 if more"
- " bytes were allocated)."
- "Use b|k|m|g to specify the desired size unit",
- 0
- };
- static void rpc_mt_realloc(rpc_t* rpc, void* c)
- {
- int size;
- int rs;
- long diff;
-
- 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_rnd_realloc((unsigned long)size << rs, &diff) < 0) {
- rpc->fault(c, 400, "memory allocation failed");
- }
- rpc->add(c, "d", diff >> rs);
- 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 memory chunks and 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", atomic_get_int(&alloc_lst->no));
- 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;
- int err;
-
- 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 ((err=mem_rnd_leak((unsigned long)min << rs,
- (unsigned long)max << rs,
- (unsigned long)total_size <<rs )) < 0) {
- rpc->fault(c, 400, "memory allocation failed (%d errors)", -err);
- }
- 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;
- int id;
-
- 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 ((id=mem_leak_time_test((unsigned long)min << rs,
- (unsigned long)max << rs,
- (unsigned long)total_size <<rs,
- MS_TO_TICKS(min_intvrl),
- MS_TO_TICKS(max_intvrl),
- MS_TO_TICKS(total_time)
- )) < 0) {
- rpc->fault(c, 400, "memory allocation failed");
- } else {
- rpc->add(c, "d", id);
- }
- 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. Use b |k | m | g as a second parameter"
- " for the size units (default bytes)",
- 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, "ddddddddddd",
- "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()),
- "total calls ", (int)tst->calls,
- "reallocs ", (int)tst->reallocs,
- "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_realloc", rpc_mt_realloc, rpc_mt_realloc_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}
- };
|