malloc_test.c

00001 /*$Id$
00002  *
00003  * Memory allocators debugging/test sip-router module.
00004  *
00005  * Copyright (C) 2010 iptelorg GmbH
00006  *
00007  * Permission to use, copy, modify, and distribute this software for any
00008  * purpose with or without fee is hereby granted, provided that the above
00009  * copyright notice and this permission notice appear in all copies.
00010  *
00011  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00012  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00013  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00014  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00015  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00016  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00017  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00018  */
00019 /*
00020  * History:
00021  * --------
00022  *  2010-03-10  initial version (andrei)
00023  */
00024 
00025 
00026 #include "../../sr_module.h"
00027 #include "../../mem/mem.h"
00028 #include "../../str.h"
00029 #include "../../dprint.h"
00030 #include "../../locking.h"
00031 #include "../../atomic_ops.h"
00032 #include "../../cfg/cfg.h"
00033 #include "../../rpc.h"
00034 #include "../../rand/fastrand.h"
00035 #include "../../timer.h"
00036 #include "../../mod_fix.h"
00037 
00038 MODULE_VERSION
00039 
00040 static int mt_mem_alloc_f(struct sip_msg*, char*,char*);
00041 static int mt_mem_free_f(struct sip_msg*, char*,char*);
00042 static int mod_init(void);
00043 static void mod_destroy(void);
00044 
00045 
00046 static cmd_export_t cmds[]={
00047         {"mt_mem_alloc", mt_mem_alloc_f, 1, fixup_var_int_1,
00048                 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONSEND_ROUTE},
00049         {"mt_mem_free", mt_mem_free_f, 1, fixup_var_int_1,
00050                 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONSEND_ROUTE},
00051         {0, 0, 0, 0, 0}
00052 };
00053 
00054 
00055 
00056 struct cfg_group_malloc_test {
00057         int check_content;
00058         int realloc_p; /* realloc probability */
00059 };
00060 
00061 
00062 static struct cfg_group_malloc_test default_mt_cfg = {
00063         0, /* check_content, off by default */
00064         0  /* realloc probability, 0 by default */
00065 };
00066 
00067 static void * mt_cfg = &default_mt_cfg;
00068 
00069 static cfg_def_t malloc_test_cfg_def[] = {
00070         {"check_content", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
00071                 "check if allocated memory was overwritten by filling it with "
00072                 "a special pattern and checking it on free."},
00073         {"realloc_p", CFG_VAR_INT | CFG_ATOMIC, 0, 90, 0, 0,
00074                 "realloc probability in percents. During tests and mem_rnd_alloc"
00075                 " realloc_p percents of the allocations will be made by realloc'ing"
00076                 " and existing chunk. The maximum value is limited to 90, to avoid"
00077                 " very long mem_rnd_alloc runs (a realloc might also free memory)." },
00078         {0, 0, 0, 0, 0, 0}
00079 };
00080 
00081 
00082 
00083 static rpc_export_t mt_rpc[];
00084 
00085 
00086 
00087 static param_export_t params[]={
00088         {"check_content", PARAM_INT, &default_mt_cfg.check_content},
00089         {0,0,0}
00090 };
00091 
00092 
00093 
00094 struct module_exports exports = {
00095         "malloc_test",
00096         cmds,
00097         mt_rpc,        /* RPC methods */
00098         params,
00099         mod_init, /* module initialization function */
00100         0,        /* response function*/
00101         mod_destroy, /* destroy function */
00102         0,        /* oncancel function */
00103         0         /* per-child init function */
00104 };
00105 
00106 
00107 
00108 #define MC_F_CHECK_CONTENTS 1
00109 
00110 struct mem_chunk{
00111         struct mem_chunk* next;
00112         void* addr;
00113         unsigned long size;
00114         unsigned long flags;
00115 };
00116 
00117 struct allocated_list {
00118         struct mem_chunk* chunks;
00119         gen_lock_t lock;
00120         volatile long size;
00121         volatile int no;
00122 };
00123 
00124 struct allocated_list* alloc_lst;
00125 
00126 
00127 struct rnd_time_test {
00128         unsigned long min;
00129         unsigned long max;
00130         unsigned long total;
00131         unsigned long crt;
00132         ticks_t min_intvrl;
00133         ticks_t max_intvrl;
00134         ticks_t stop_time;
00135         ticks_t start_time;
00136         unsigned long calls;
00137         unsigned long reallocs;
00138         unsigned int errs;
00139         unsigned int overfl;
00140         struct rnd_time_test* next;
00141         struct timer_ln timer;
00142         int id;
00143 };
00144 
00145 struct rnd_time_test_lst {
00146         struct rnd_time_test* tests;
00147         gen_lock_t lock;
00148         volatile int last_id;
00149 };
00150 
00151 
00152 struct rnd_time_test_lst* rndt_lst;
00153 
00154 static unsigned long mem_unleak(unsigned long size);
00155 static void mem_destroy_all_tests();
00156 
00157 static int mod_init(void)
00158 {
00159         WARN("This is a test/debugging module, don't use it in production\n");
00160         /* declare configuration */
00161         if (cfg_declare("malloc_test", malloc_test_cfg_def, &default_mt_cfg,
00162                                         cfg_sizeof(malloc_test), &mt_cfg)){
00163                 ERR("failed to register the configuration\n");
00164                 goto error;
00165         }
00166         
00167         alloc_lst = shm_malloc(sizeof(*alloc_lst));
00168         if (alloc_lst == 0)
00169                 goto error;
00170         alloc_lst->chunks = 0;
00171         atomic_set_long(&alloc_lst->size, 0);
00172         atomic_set_int(&alloc_lst->no, 0);
00173         if (lock_init(&alloc_lst->lock) == 0)
00174                 goto error;
00175         rndt_lst = shm_malloc(sizeof(*rndt_lst));
00176         if (rndt_lst == 0)
00177                 goto error;
00178         rndt_lst->tests = 0;
00179         atomic_set_int(&rndt_lst->last_id, 0);
00180         if (lock_init(&rndt_lst->lock) == 0)
00181                 goto error;
00182         return 0;
00183 error:
00184         return -1;
00185 }
00186 
00187 
00188 
00189 static void mod_destroy()
00190 {
00191         if (rndt_lst) {
00192                 mem_destroy_all_tests();
00193                 lock_destroy(&rndt_lst->lock);
00194                 shm_free(rndt_lst);
00195                 rndt_lst = 0;
00196         }
00197         if (alloc_lst) {
00198                 mem_unleak(-1);
00199                 lock_destroy(&alloc_lst->lock);
00200                 shm_free(alloc_lst);
00201                 alloc_lst = 0;
00202         }
00203 }
00204 
00205 
00206 
00212 static int mem_track(void* addr, unsigned long size)
00213 {
00214         struct mem_chunk* mc;
00215         unsigned long* d;
00216         unsigned long r,i;
00217         
00218         mc = shm_malloc(sizeof(*mc));
00219         if (mc == 0) goto error;
00220         mc->addr = addr;
00221         mc->size = size;
00222         mc->flags = 0;
00223         if (cfg_get(malloc_test, mt_cfg, check_content)){
00224                 mc->flags |=  MC_F_CHECK_CONTENTS;
00225                 d = addr;
00226                 for (r = 0; r < size/sizeof(*d); r++){
00227                         d[r]=~(unsigned long)&d[r];
00228                 }
00229                 for (i=0; i< size % sizeof(*d); i++){
00230                         ((char*)&d[r])[i]=~((unsigned long)&d[r] >> i*8);
00231                 }
00232         }
00233         lock_get(&alloc_lst->lock);
00234                 mc->next = alloc_lst->chunks;
00235                 alloc_lst->chunks = mc;
00236         lock_release(&alloc_lst->lock);
00237         atomic_add_long(&alloc_lst->size, size);
00238         atomic_inc_int(&alloc_lst->no);
00239         return 0;
00240 error:
00241         return -1;
00242 }
00243 
00244 
00245 
00252 static int mem_leak(unsigned long size)
00253 {
00254         void *d;
00255         
00256         d = shm_malloc(size);
00257         if (d) {
00258                 if (mem_track(d, size) < 0){
00259                         shm_free(d);
00260                 }else
00261                         return 0;
00262         }
00263         return -1;
00264 }
00265 
00266 
00267 
00268 /* realloc a chunk, unsafe (requires external locking) version.
00269  * @return 0 on success, -1 on error
00270  */
00271 static int _mem_chunk_realloc_unsafe(struct mem_chunk *c, unsigned long size)
00272 {
00273         unsigned long* d;
00274         int r, i;
00275         
00276         d = shm_realloc(c->addr, size);
00277         if (d) {
00278                 if (cfg_get(malloc_test, mt_cfg, check_content) &&
00279                                 c->flags & MC_F_CHECK_CONTENTS) {
00280                         /* re-fill the test patterns (the address might have changed
00281                            and they depend on it) */
00282                         for (r = 0; r < size/sizeof(*d); r++){
00283                                 d[r]=~(unsigned long)&d[r];
00284                         }
00285                         for (i=0; i< size % sizeof(*d); i++){
00286                                 ((char*)&d[r])[i]=~((unsigned long)&d[r] >> i*8);
00287                         }
00288                 }
00289                 c->addr = d;
00290                 c->size = size;
00291                 return 0;
00292         }
00293         return -1;
00294 }
00295 
00296 
00297 
00298 static void mem_chunk_free(struct mem_chunk* c)
00299 {
00300         unsigned long* d;
00301         unsigned long r,i;
00302         int err;
00303 
00304         if (cfg_get(malloc_test, mt_cfg, check_content) &&
00305                         c->flags & MC_F_CHECK_CONTENTS) {
00306                 d = c->addr;
00307                 err = 0;
00308                 for (r = 0; r < c->size/sizeof(*d); r++){
00309                         if (d[r]!=~(unsigned long)&d[r])
00310                                 err++;
00311                         d[r] = (unsigned long)&d[r]; /* fill it with something else */
00312                 }
00313                 for (i=0; i< c->size % sizeof(*d); i++){
00314                         if (((unsigned char*)&d[r])[i] !=
00315                                         (unsigned char)~((unsigned long)&d[r] >> i*8))
00316                                 err++;
00317                         ((char*)&d[r])[i] = (unsigned char)((unsigned long)&d[r] >> i*8);
00318                 }
00319                 if (err)
00320                         ERR("%d errors while checking %ld bytes at %p\n", err, c->size, d);
00321         }
00322         shm_free(c->addr);
00323         c->addr = 0;
00324         c->flags = 0;
00325 }
00326 
00327 
00328 
00335 static unsigned long mem_unleak(unsigned long size)
00336 {
00337         struct mem_chunk** mc;
00338         struct mem_chunk* t;
00339         struct mem_chunk** min_chunk;
00340         unsigned long freed;
00341         unsigned int no;
00342         
00343         freed = 0;
00344         no = 0;
00345         min_chunk = 0;
00346         lock_get(&alloc_lst->lock);
00347         if (size>=atomic_get_long(&alloc_lst->size)){
00348                 /* free all */
00349                 for (mc = &alloc_lst->chunks; *mc; ){
00350                         t = *mc;
00351                         mem_chunk_free(t);
00352                         freed += t->size;
00353                         no++;
00354                         *mc = t->next;
00355                         shm_free(t);
00356                 }
00357                 alloc_lst->chunks=0;
00358         } else {
00359                 /* free at least size bytes, trying smaller chunks first */
00360                 for (mc = &alloc_lst->chunks; *mc && (freed < size);) {
00361                         if ((*mc)->size <= (size - freed)) {
00362                                 t = *mc;
00363                                 mem_chunk_free(t);
00364                                 freed += t->size;
00365                                 no++;
00366                                 *mc = t->next;
00367                                 shm_free(t);
00368                                 continue;
00369                         } else if (min_chunk == 0 || (*min_chunk)->size > (*mc)->size) {
00370                                 /* find minimum remaining chunk  */
00371                                 min_chunk = mc;
00372                         }
00373                         mc = &(*mc)->next;
00374                 }
00375                 if (size > freed && min_chunk) {
00376                         mc = min_chunk;
00377                         t = *mc;
00378                         mem_chunk_free(t);
00379                         freed += t->size;
00380                         no++;
00381                         *mc = (*mc)->next;
00382                         shm_free(t);
00383                 }
00384         }
00385         lock_release(&alloc_lst->lock);
00386         atomic_add_long(&alloc_lst->size, -freed);
00387         atomic_add_int(&alloc_lst->no, -no);
00388         return freed;
00389 }
00390 
00391 
00392 
00401 static int mem_rnd_realloc(unsigned long size, long* diff)
00402 {
00403         struct mem_chunk* t;
00404         int ret;
00405         int target, i;
00406         
00407         *diff = 0;
00408         ret = -1;
00409         lock_get(&alloc_lst->lock);
00410                 target = fastrand_max(atomic_get_int(&alloc_lst->no));
00411                 for (t = alloc_lst->chunks, i=0; t; t=t->next, i++ ){
00412                         if (target == i) {
00413                                 *diff = (long)size - (long)t->size;
00414                                 if ((ret=_mem_chunk_realloc_unsafe(t, size)) < 0)
00415                                         *diff = 0;
00416                                 break;
00417                         }
00418                 }
00419         lock_release(&alloc_lst->lock);
00420         atomic_add_long(&alloc_lst->size, *diff);
00421         return ret;
00422 }
00423 
00424 
00425 
00426 #define MIN_ulong(a, b) \
00427         (unsigned long)((unsigned long)(a)<(unsigned long)(b)?(a):(b))
00428 
00429 /*
00430  * Randomly alloc. total_size bytes, in chunks of size between
00431  * min & max. max - min should be smaller then 4G.
00432  * @return < 0 if there were some alloc errors, 0 on success.
00433  */
00434 static int mem_rnd_leak(unsigned long min, unsigned long max,
00435                                                 unsigned long total_size)
00436 {
00437         unsigned long size;
00438         unsigned long crt_size, crt_min;
00439         long diff;
00440         int err, p;
00441         
00442         size = total_size;
00443         err = 0;
00444         while(size){
00445                 crt_min = MIN_ulong(min, size);
00446                 crt_size = fastrand_max(MIN_ulong(max, size) - crt_min) + crt_min;
00447                 p = cfg_get(malloc_test, mt_cfg, realloc_p);
00448                 if (p && ((fastrand_max(99) +1) <= p)){
00449                         if (mem_rnd_realloc(crt_size, &diff) == 0){
00450                                 size -= diff;
00451                                 continue;
00452                         } /* else fallback to normal alloc. */
00453                 }
00454                 size -= crt_size;
00455                 err += mem_leak(crt_size) < 0;
00456         }
00457         return -err;
00458 }
00459 
00460 
00461 
00462 /* test timer */
00463 static ticks_t tst_timer(ticks_t ticks, struct timer_ln* tl, void* data)
00464 {
00465         struct rnd_time_test* tst;
00466         ticks_t next_int;
00467         ticks_t max_int;
00468         unsigned long crt_size, crt_min, remaining;
00469         long diff;
00470         int p;
00471         
00472         tst = data;
00473         
00474         next_int = 0;
00475         max_int = 0;
00476         
00477         if (tst->total <= tst->crt) {
00478                 mem_unleak(tst->crt);
00479                 tst->crt = 0;
00480                 tst->overfl++;
00481         }
00482         remaining = tst->total - tst->crt;
00483         crt_min = MIN_ulong(tst->min, remaining);
00484         crt_size = fastrand_max(MIN_ulong(tst->max, remaining) - crt_min) +
00485                                 crt_min;
00486         p = cfg_get(malloc_test, mt_cfg, realloc_p);
00487         if (p && ((fastrand_max(99) +1) <= p)) {
00488                 if (mem_rnd_realloc(crt_size, &diff) == 0){
00489                         tst->crt -= diff;
00490                         tst->reallocs++;
00491                         goto skip_alloc;
00492                 }
00493         }
00494         if (mem_leak(crt_size) >= 0)
00495                 tst->crt += crt_size;
00496         else
00497                 tst->errs ++;
00498 skip_alloc:
00499         tst->calls++;
00500         
00501         if (TICKS_GT(tst->stop_time, ticks)) {
00502                 next_int = fastrand_max(tst->max_intvrl - tst->min_intvrl) +
00503                                 tst->min_intvrl;
00504                 max_int = tst->stop_time - ticks;
00505         } else {
00506                 /* stop test */
00507                 WARN("test %d time expired, stopping"
00508                                 " (%d s runtime, %ld calls, %d overfl, %d errors,"
00509                                 " crt %ld bytes)\n",
00510                                 tst->id, TICKS_TO_S(ticks - tst->start_time),
00511                                 tst->calls, tst->overfl, tst->errs, tst->crt);
00512                 mem_unleak(tst->crt);
00513                 /* tst->crt = 0 */;
00514         }
00515         
00516         /* 0 means stop stop, so if next_int == 0 => stop */
00517         return MIN_unsigned(next_int, max_int);
00518 }
00519 
00520 
00521 /*
00522  * start a malloc test of a test_time length:
00523  *  - randomly between min_intvrl and max_intvrl, alloc.
00524  *    a random number of bytes, between min & max.
00525  *  - if total_size is reached, free everything.
00526  *
00527  * @returns test id (>=0) on success, -1 on error.
00528  */
00529 static int mem_leak_time_test(unsigned long min, unsigned long max,
00530                                                                 unsigned long total_size,
00531                                                                 ticks_t min_intvrl, ticks_t max_intvrl,
00532                                                                 ticks_t test_time)
00533 {
00534         struct rnd_time_test* tst;
00535         struct rnd_time_test* l;
00536         ticks_t first_int;
00537         int id;
00538         
00539         tst = shm_malloc(sizeof(*tst));
00540         if (tst == 0)
00541                 goto error;
00542         memset(tst, 0, sizeof(*tst));
00543         id = tst->id = atomic_add_int(&rndt_lst->last_id, 1);
00544         tst->min = min;
00545         tst->max = max;
00546         tst-> total = total_size;
00547         tst->min_intvrl = min_intvrl;
00548         tst->max_intvrl = max_intvrl;
00549         tst->start_time = get_ticks_raw();
00550         tst->stop_time = get_ticks_raw() + test_time;
00551         first_int = fastrand_max(max_intvrl - min_intvrl) + min_intvrl;
00552         timer_init(&tst->timer, tst_timer, tst, 0);
00553         lock_get(&rndt_lst->lock);
00554                 tst->next=rndt_lst->tests;
00555                 rndt_lst->tests=tst;
00556         lock_release(&rndt_lst->lock);
00557         if (timer_add(&tst->timer, MIN_unsigned(first_int, test_time)) < 0 )
00558                 goto error;
00559         return id;
00560 error:
00561         if (tst) {
00562                 lock_get(&rndt_lst->lock);
00563                         for (l=rndt_lst->tests; l; l=l->next)
00564                                 if (l->next == tst) {
00565                                         l->next = tst->next;
00566                                         break;
00567                                 }
00568                 lock_release(&rndt_lst->lock);
00569                 shm_free(tst);
00570         }
00571         return -1;
00572 }
00573 
00574 
00575 static int is_mem_test_stopped(struct rnd_time_test* tst)
00576 {
00577         return TICKS_LE(tst->stop_time, get_ticks_raw());
00578 }
00579 
00583 static int mem_test_stop_tst(struct rnd_time_test* tst)
00584 {
00585         if (!is_mem_test_stopped(tst)) {
00586                 if (timer_del(&tst->timer) == 0) {
00587                         tst->stop_time=get_ticks_raw();
00588                         return 0;
00589                 }
00590         }
00591         return -1;
00592 }
00593 
00594 
00598 static int mem_test_stop(int id)
00599 {
00600         struct rnd_time_test* tst;
00601         
00602         lock_get(&rndt_lst->lock);
00603                 for (tst = rndt_lst->tests; tst; tst = tst->next)
00604                         if (tst->id == id) {
00605                                 mem_test_stop_tst(tst);
00606                                 break;
00607                         }
00608         lock_release(&rndt_lst->lock);
00609         return -(tst == 0);
00610 }
00611 
00612 
00613 static void mem_destroy_all_tests()
00614 {
00615         struct rnd_time_test* tst;
00616         struct rnd_time_test* nxt;
00617         
00618         lock_get(&rndt_lst->lock);
00619                 for (tst = rndt_lst->tests; tst;) {
00620                         nxt = tst->next;
00621                         mem_test_stop_tst(tst);
00622                         shm_free(tst);
00623                         tst = nxt;
00624                 }
00625                 rndt_lst->tests = 0;
00626         lock_release(&rndt_lst->lock);
00627 }
00628 
00629 
00630 static int mem_test_destroy(int id)
00631 {
00632         struct rnd_time_test* tst;
00633         struct rnd_time_test** crt_lnk;
00634         
00635         lock_get(&rndt_lst->lock);
00636                 for (tst = 0, crt_lnk = &rndt_lst->tests; *crt_lnk;
00637                                 crt_lnk = &(*crt_lnk)->next)
00638                         if ((*crt_lnk)->id == id) {
00639                                 tst=*crt_lnk;
00640                                 mem_test_stop_tst(tst);
00641                                 *crt_lnk=tst->next;
00642                                 shm_free(tst);
00643                                 break;
00644                         }
00645         lock_release(&rndt_lst->lock);
00646         return -(tst == 0);
00647 }
00648 
00649 /* script functions: */
00650 
00651 
00652 static int mt_mem_alloc_f(struct sip_msg* msg, char* sz, char* foo)
00653 {
00654         int size;
00655         
00656         if (sz == 0 || get_int_fparam(&size, msg, (fparam_t*)sz) < 0)
00657                 return -1;
00658         return mem_leak(size)>=0?1:-1;
00659 }
00660 
00661 
00662 
00663 static int mt_mem_free_f(struct sip_msg* msg, char* sz, char* foo)
00664 {
00665         int size;
00666         unsigned long freed;
00667         
00668         size=-1;
00669         if (sz != 0 && get_int_fparam(&size, msg, (fparam_t*)sz) < 0)
00670                 return -1;
00671         freed=mem_unleak(size);
00672         return (freed==0)?1:freed;
00673 }
00674 
00675 
00676 
00677 /* RPC exports: */
00678 
00679 
00680 
00681 /* helper functions, parses an optional b[ytes]|k|m|g to a numeric shift value
00682    (e.g. b -> 0, k -> 10, ...)
00683    returns bit shift value on success, -1 on error
00684 */
00685 static int rpc_get_size_mod(rpc_t* rpc, void* c)
00686 {
00687         char* m;
00688         
00689         if (rpc->scan(c, "*s", &m) > 0) {
00690                 switch(*m) {
00691                         case 'b':
00692                         case 'B':
00693                                 return 0;
00694                         case 'k':
00695                         case 'K':
00696                                 return 10;
00697                         case 'm':
00698                         case 'M':
00699                                 return 20;
00700                         case 'g':
00701                         case 'G':
00702                                 return 30;
00703                         default:
00704                                 rpc->fault(c, 500, "bad param use b|k|m|g");
00705                                 return -1;
00706                 }
00707         }
00708         return 0;
00709 }
00710 
00711 
00712 
00713 static const char* rpc_mt_alloc_doc[2] = {
00714         "Allocates the specified number of bytes (debugging/test function)."
00715         "Use b|k|m|g to specify the desired size unit",
00716         0
00717 };
00718 
00719 static void rpc_mt_alloc(rpc_t* rpc, void* c)
00720 {
00721         int size;
00722         int rs;
00723         
00724         if (rpc->scan(c, "d", &size) < 1) {
00725                 return;
00726         }
00727         rs=rpc_get_size_mod(rpc, c);
00728         if (rs<0)
00729                 /* fault already generated on rpc_get_size_mod() error */
00730                 return;
00731         if (mem_leak((unsigned long)size << rs) < 0) {
00732                 rpc->fault(c, 400, "memory allocation failed");
00733         }
00734         return;
00735 }
00736 
00737 
00738 static const char* rpc_mt_realloc_doc[2] = {
00739         "Reallocates the specified number of bytes from a pre-allocated"
00740         " randomly selected memory chunk. If no pre-allocated memory"
00741         " chunks exists, it will fail."
00742         " Make sure mt.mem_used is non 0 or call mt.mem_alloc prior to calling"
00743         " this function."
00744         " Returns the difference in bytes (<0 if bytes were freed, >0 if more"
00745         " bytes were allocated)."
00746         "Use b|k|m|g to specify the desired size unit",
00747         0
00748 };
00749 
00750 static void rpc_mt_realloc(rpc_t* rpc, void* c)
00751 {
00752         int size;
00753         int rs;
00754         long diff;
00755         
00756         if (rpc->scan(c, "d", &size) < 1) {
00757                 return;
00758         }
00759         rs=rpc_get_size_mod(rpc, c);
00760         if (rs<0)
00761                 /* fault already generated on rpc_get_size_mod() error */
00762                 return;
00763         if (mem_rnd_realloc((unsigned long)size << rs, &diff) < 0) {
00764                 rpc->fault(c, 400, "memory allocation failed");
00765         }
00766         rpc->add(c, "d", diff >> rs);
00767         return;
00768 }
00769 
00770 
00771 static const char* rpc_mt_free_doc[2] = {
00772         "Frees the specified number of bytes, previously allocated by one of the"
00773         " other malloc_test functions (e.g. mt.mem_alloc or the script "
00774         "mt_mem_alloc). Use b|k|m|g to specify the desired size unit."
00775         "Returns the number of bytes freed (can be higher or"
00776          " smaller then the requested size)",
00777         0
00778 };
00779 
00780 
00781 static void rpc_mt_free(rpc_t* rpc, void* c)
00782 {
00783         int size;
00784         int rs;
00785         
00786         size = -1;
00787         rs = 0;
00788         if (rpc->scan(c, "*d", &size) > 0) {
00789                 /* found size, look if a size modifier is present */
00790                 rs=rpc_get_size_mod(rpc, c);
00791                 if (rs<0)
00792                         /* fault already generated on rpc_get_size_mod() error */
00793                         return;
00794         }
00795         rpc->add(c, "d", (int)(mem_unleak((unsigned long)size << rs) >> rs));
00796         return;
00797 }
00798 
00799 
00800 
00801 static const char* rpc_mt_used_doc[2] = {
00802         "Returns how many memory chunks and how many bytes are currently"
00803         " allocated via the mem_alloc module functions."
00804         " Use b|k|m|g to specify the desired size unit.",
00805         0
00806 };
00807 
00808 
00809 static void rpc_mt_used(rpc_t* rpc, void* c)
00810 {
00811         int rs;
00812         
00813         rs = 0;
00814         rs=rpc_get_size_mod(rpc, c);
00815         if (rs<0)
00816                 /* fault already generated on rpc_get_size_mod() error */
00817                 return;
00818         rpc->add(c, "d", atomic_get_int(&alloc_lst->no));
00819         rpc->add(c, "d", (int)(atomic_get_long(&alloc_lst->size) >> rs));
00820         return;
00821 }
00822 
00823 
00824 static const char* rpc_mt_rnd_alloc_doc[2] = {
00825         "Takes 4 parameters: min, max, total_size and an optional unit (b|k|m|g)."
00826         " It will allocate total_size memory, in pieces of random size between"
00827         "min .. max (inclusive).",
00828         0
00829 };
00830 
00831 
00832 static void rpc_mt_rnd_alloc(rpc_t* rpc, void* c)
00833 {
00834         int min, max, total_size;
00835         int rs;
00836         int err;
00837         
00838         if (rpc->scan(c, "ddd", &min, &max, &total_size) < 3) {
00839                 return;
00840         }
00841         rs=rpc_get_size_mod(rpc, c);
00842         if (rs<0)
00843                 /* fault already generated on rpc_get_size_mod() error */
00844                 return;
00845         if (min > max || min < 0 || max > total_size) {
00846                 rpc->fault(c, 400, "invalid parameter values");
00847                 return;
00848         }
00849         if ((err=mem_rnd_leak((unsigned long)min << rs,
00850                                                  (unsigned long)max << rs,
00851                                                  (unsigned long)total_size <<rs )) < 0) {
00852                 rpc->fault(c, 400, "memory allocation failed (%d errors)", -err);
00853         }
00854         return;
00855 }
00856 
00857 
00858 static const char* rpc_mt_test_start_doc[2] = {
00859         "Takes 7 parameters: min, max, total_size, min_interval, max_interval, "
00860         "test_time and an optional size unit (b|k|m|g). All the time units are ms."
00861         " It will run a memory allocation test for test_time ms. At a random"
00862         " interval between min_interval and max_interval ms. it will allocate a"
00863         " memory chunk with random size, between min and max. Each time total_size"
00864         " is reached, it will free all the memory allocated and start again."
00865         "Returns the test id (integer)",
00866         0
00867 };
00868 
00869 
00870 static void rpc_mt_test_start(rpc_t* rpc, void* c)
00871 {
00872         int min, max, total_size;
00873         int min_intvrl, max_intvrl, total_time;
00874         int rs;
00875         int id;
00876         
00877         if (rpc->scan(c, "dddddd", &min, &max, &total_size,
00878                                                                 &min_intvrl, &max_intvrl, &total_time) < 6) {
00879                 return;
00880         }
00881         rs=rpc_get_size_mod(rpc, c);
00882         if (rs<0)
00883                 /* fault already generated on rpc_get_size_mod() error */
00884                 return;
00885         if (min > max || min < 0 || max > total_size) {
00886                 rpc->fault(c, 400, "invalid size parameters values");
00887                 return;
00888         }
00889         if (min_intvrl > max_intvrl || min_intvrl <= 0 || max_intvrl > total_time){
00890                 rpc->fault(c, 400, "invalid time intervals values");
00891                 return;
00892         }
00893         if ((id=mem_leak_time_test((unsigned long)min << rs,
00894                                          (unsigned long)max << rs,
00895                                          (unsigned long)total_size <<rs,
00896                                          MS_TO_TICKS(min_intvrl),
00897                                          MS_TO_TICKS(max_intvrl),
00898                                          MS_TO_TICKS(total_time)
00899                                          )) < 0) {
00900                 rpc->fault(c, 400, "memory allocation failed");
00901         } else {
00902                 rpc->add(c, "d", id);
00903         }
00904         return;
00905 }
00906 
00907 
00908 static const char* rpc_mt_test_stop_doc[2] = {
00909         "Takes 1 parameter: the test id. It will stop the corresponding test."
00910         "Note: the test is stopped, but not destroyed." ,
00911         0
00912 };
00913 
00914 
00915 static void rpc_mt_test_stop(rpc_t* rpc, void* c)
00916 {
00917         int id;
00918         
00919         if (rpc->scan(c, "d", &id) < 1) {
00920                 return;
00921         }
00922         if (mem_test_stop(id)<0) {
00923                 rpc->fault(c, 400, "test %d not found", id);
00924         }
00925         return;
00926 }
00927 
00928 
00929 static const char* rpc_mt_test_destroy_doc[2] = {
00930         "Takes 1 parameter: the test id. It will destroy the corresponding test.",
00931         0
00932 };
00933 
00934 
00935 static void rpc_mt_test_destroy(rpc_t* rpc, void* c)
00936 {
00937         int id;
00938         
00939         if (rpc->scan(c, "*d", &id) > 0 && id!=-1) {
00940                 if (mem_test_destroy(id) < 0 )
00941                         rpc->fault(c, 400, "test %d not found", id);
00942         } else {
00943                 mem_destroy_all_tests();
00944         }
00945         return;
00946 }
00947 
00948 
00949 static const char* rpc_mt_test_destroy_all_doc[2] = {
00950         "It will destroy all the tests (running or stopped).",
00951         0
00952 };
00953 
00954 
00955 static void rpc_mt_test_destroy_all(rpc_t* rpc, void* c)
00956 {
00957         mem_destroy_all_tests();
00958         return;
00959 }
00960 
00961 
00962 static const char* rpc_mt_test_list_doc[2] = {
00963         "If a test id parameter is provided it will list the corresponding test,"
00964         " else it will list all of them. Use b |k | m | g as a second parameter"
00965         " for the size units (default bytes)",
00966         0
00967 };
00968 
00969 
00970 static void rpc_mt_test_list(rpc_t* rpc, void* c)
00971 {
00972         int id, rs;
00973         struct rnd_time_test* tst;
00974         void *h;
00975         
00976         rs = 0;
00977         if (rpc->scan(c, "*d", &id) < 1) {
00978                 id = -1;
00979         } else {
00980                 rs=rpc_get_size_mod(rpc, c);
00981                 if (rs < 0)
00982                         return;
00983         }
00984         lock_get(&rndt_lst->lock);
00985                 for (tst = rndt_lst->tests; tst; tst=tst->next)
00986                         if (tst->id == id || id == -1) {
00987                                 rpc->add(c, "{", &h);
00988                                 rpc->struct_add(h, "ddddddddddd",
00989                                                 "ID           ",  tst->id,
00990                                                 "run time (s) ", (int)TICKS_TO_S((
00991                                                                                         TICKS_LE(tst->stop_time,
00992                                                                                                         get_ticks_raw()) ?
00993                                                                                         tst->stop_time : get_ticks_raw()) -
00994                                                                                                         tst->start_time),
00995                                                 "remaining (s)", TICKS_LE(tst->stop_time,
00996                                                                                                 get_ticks_raw()) ? 0 :
00997                                                                                 (int)TICKS_TO_S(tst->stop_time -
00998                                                                                                                 get_ticks_raw()),
00999                                                 "total calls  ", (int)tst->calls,
01000                                                 "reallocs     ", (int)tst->reallocs,
01001                                                 "errors       ", (int)tst->errs,
01002                                                 "overflows    ", (int)tst->overfl,
01003                                                 "total alloc  ", (int)((tst->crt +
01004                                                                                         tst->overfl * tst->total)>>rs),
01005                                                 "min          ", (int)(tst->min>>rs),
01006                                                 "max          ", (int)(tst->max>>rs),
01007                                                 "total        ", (int)(tst->total>>rs) );
01008                                 if (id != -1) break;
01009                         }
01010         lock_release(&rndt_lst->lock);
01011         
01012         return;
01013 }
01014 
01015 
01016 static rpc_export_t mt_rpc[] = {
01017         {"mt.mem_alloc", rpc_mt_alloc, rpc_mt_alloc_doc, 0},
01018         {"mt.mem_free", rpc_mt_free, rpc_mt_free_doc, 0},
01019         {"mt.mem_realloc", rpc_mt_realloc, rpc_mt_realloc_doc, 0},
01020         {"mt.mem_used", rpc_mt_used, rpc_mt_used_doc, 0},
01021         {"mt.mem_rnd_alloc", rpc_mt_rnd_alloc, rpc_mt_rnd_alloc_doc, 0},
01022         {"mt.mem_test_start", rpc_mt_test_start, rpc_mt_test_start_doc, 0},
01023         {"mt.mem_test_stop", rpc_mt_test_stop, rpc_mt_test_stop_doc, 0},
01024         {"mt.mem_test_destroy", rpc_mt_test_destroy, rpc_mt_test_destroy_doc, 0},
01025         {"mt.mem_test_destroy_all", rpc_mt_test_destroy_all,
01026                                                                 rpc_mt_test_destroy_all_doc, 0},
01027         {"mt.mem_test_list", rpc_mt_test_list, rpc_mt_test_list_doc, 0},
01028         {0, 0, 0, 0}
01029 };
01030