malloc_test: new module for testing/debugging memory problems
[sip-router] / modules / malloc_test / malloc_test.c
1 /*$Id$
2  *
3  * Memory allocators debugging/test sip-router module.
4  *
5  * Copyright (C) 2010 iptelorg GmbH
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 /*
20  * History:
21  * --------
22  *  2010-03-10  initial version (andrei)
23  */
24
25
26 #include "../../sr_module.h"
27 #include "../../mem/mem.h"
28 #include "../../str.h"
29 #include "../../dprint.h"
30 #include "../../locking.h"
31 #include "../../atomic_ops.h"
32 #include "../../cfg/cfg.h"
33 #include "../../rpc.h"
34 #include "../../rand/fastrand.h"
35 #include "../../timer.h"
36 #include "../../mod_fix.h"
37
38 MODULE_VERSION
39
40 static int mt_mem_alloc_f(struct sip_msg*, char*,char*);
41 static int mt_mem_free_f(struct sip_msg*, char*,char*);
42 static int mod_init(void);
43 static void mod_destroy(void);
44
45
46 static cmd_export_t cmds[]={
47         {"mt_mem_alloc", mt_mem_alloc_f, 1, fixup_var_int_1,
48                 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONSEND_ROUTE},
49         {"mt_mem_free", mt_mem_free_f, 1, fixup_var_int_1,
50                 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONSEND_ROUTE},
51         {0, 0, 0, 0, 0}
52 };
53
54
55
56 struct cfg_group_malloc_test {
57         int check_content;
58 };
59
60
61 static struct cfg_group_malloc_test default_mt_cfg = {
62         0 /* check_content, off by default */
63 };
64
65 static void * mt_cfg = &default_mt_cfg;
66
67 static cfg_def_t malloc_test_cfg_def[] = {
68         {"check_content", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
69                 "check if allocated memory was overwritten by filling it with "
70                 "a special pattern and checking it on free"},
71         {0, 0, 0, 0, 0, 0}
72 };
73
74
75
76 static rpc_export_t mt_rpc[];
77
78
79
80 static param_export_t params[]={
81         {"check_content", PARAM_INT, &default_mt_cfg.check_content},
82         {0,0,0}
83 };
84
85
86
87 struct module_exports exports = {
88         "malloc_test",
89         cmds,
90         mt_rpc,        /* RPC methods */
91         params,
92         mod_init, /* module initialization function */
93         0,        /* response function*/
94         mod_destroy, /* destroy function */
95         0,        /* oncancel function */
96         0         /* per-child init function */
97 };
98
99
100
101 #define MC_F_CHECK_CONTENTS 1
102
103 struct mem_chunk{
104         struct mem_chunk* next;
105         void* addr;
106         unsigned long size;
107         unsigned long flags;
108 };
109
110 struct allocated_list {
111         struct mem_chunk* chunks;
112         gen_lock_t lock;
113         volatile long size;
114 };
115
116 struct allocated_list* alloc_lst;
117
118
119 struct rnd_time_test {
120         unsigned long min;
121         unsigned long max;
122         unsigned long total;
123         unsigned long crt;
124         ticks_t min_intvrl;
125         ticks_t max_intvrl;
126         ticks_t stop_time;
127         ticks_t start_time;
128         unsigned long calls;
129         unsigned int errs;
130         unsigned int overfl;
131         struct rnd_time_test* next;
132         struct timer_ln timer;
133         int id;
134 };
135
136 struct rnd_time_test_lst {
137         struct rnd_time_test* tests;
138         gen_lock_t lock;
139         volatile int last_id;
140 };
141
142
143 struct rnd_time_test_lst* rndt_lst;
144
145 static unsigned long mem_unleak(unsigned long size);
146 static void mem_destroy_all_tests();
147
148 static int mod_init(void)
149 {
150         WARN("This is a test/debugging module, don't use it in production\n");
151         /* declare configuration */
152         if (cfg_declare("malloc_test", malloc_test_cfg_def, &default_mt_cfg,
153                                         cfg_sizeof(malloc_test), &mt_cfg)){
154                 ERR("failed to register the configuration\n");
155                 goto error;
156         }
157         
158         alloc_lst = shm_malloc(sizeof(*alloc_lst));
159         if (alloc_lst == 0)
160                 goto error;
161         alloc_lst->chunks = 0;
162         atomic_set_long(&alloc_lst->size, 0);
163         if (lock_init(&alloc_lst->lock) == 0)
164                 goto error;
165         rndt_lst = shm_malloc(sizeof(*rndt_lst));
166         if (rndt_lst == 0)
167                 goto error;
168         rndt_lst->tests = 0;
169         atomic_set_int(&rndt_lst->last_id, 0);
170         if (lock_init(&rndt_lst->lock) == 0)
171                 goto error;
172         return 0;
173 error:
174         return -1;
175 }
176
177
178
179 static void mod_destroy()
180 {
181         if (rndt_lst) {
182                 mem_destroy_all_tests();
183                 lock_destroy(&rndt_lst->lock);
184                 shm_free(rndt_lst);
185                 rndt_lst = 0;
186         }
187         if (alloc_lst) {
188                 mem_unleak(-1);
189                 lock_destroy(&alloc_lst->lock);
190                 shm_free(alloc_lst);
191                 alloc_lst = 0;
192         }
193 }
194
195
196
197 /** record a memory chunk list entry.
198  * @param addr - address of the newly allocated memory
199  * @oaram size - size
200  * @return 0 on success, -1 on error (no more mem).
201  */
202 static int mem_track(void* addr, unsigned long size)
203 {
204         struct mem_chunk* mc;
205         unsigned long* d;
206         unsigned long r,i;
207         
208         mc = shm_malloc(sizeof(*mc));
209         if (mc == 0) goto error;
210         mc->addr = addr;
211         mc->size = size;
212         mc->flags = 0;
213         if (cfg_get(malloc_test, mt_cfg, check_content)){
214                 mc->flags |=  MC_F_CHECK_CONTENTS;
215                 d = addr;
216                 for (r = 0; r < size/sizeof(*d); r++){
217                         d[r]=~(unsigned long)d;
218                 }
219                 for (i=0; i< size % sizeof(*d); i++){
220                         ((char*)&d[r])[i]=~((unsigned long)d >> i*8);
221                 }
222         }
223         lock_get(&alloc_lst->lock);
224                 mc->next = alloc_lst->chunks;
225                 alloc_lst->chunks = mc;
226         lock_release(&alloc_lst->lock);
227         atomic_add_long(&alloc_lst->size, size);
228         return 0;
229 error:
230         return -1;
231 }
232
233
234
235 /** allocate memory.
236  * Allocates memory, but keeps track of it, so that mem_unleak() can
237  * free it.
238  * @param size - how many bytes
239  * @return 0 on success, -1 on error
240  */
241 static int mem_leak(unsigned long size)
242 {
243         void *d;
244         
245         d = shm_malloc(size);
246         if (d) {
247                 if (mem_track(d, size) < 0){
248                         shm_free(d);
249                 }else
250                         return 0;
251         }
252         return -1;
253 }
254
255
256
257 static void mem_chunk_free(struct mem_chunk* c)
258 {
259         unsigned long* d;
260         unsigned long r,i;
261         int err;
262
263         if (cfg_get(malloc_test, mt_cfg, check_content) &&
264                         c->flags & MC_F_CHECK_CONTENTS) {
265                 d = c->addr;
266                 err = 0;
267                 for (r = 0; r < c->size/sizeof(*d); r++){
268                         if (d[r]!=~(unsigned long)d)
269                                 err++;
270                         d[r] = r; /* fill it with something else */
271                 }
272                 for (i=0; i< c->size % sizeof(*d); i++){
273                         if (((unsigned char*)&d[r])[i] !=
274                                         (unsigned char)~((unsigned long)d >> i*8))
275                                 err++;
276                         ((char*)&d[r])[i] = (unsigned char)((unsigned long)d >> i*8);
277                 }
278                 if (err)
279                         ERR("%d errors while checking %ld bytes at %p\n", err, c->size, d);
280         }
281         shm_free(c->addr);
282         c->addr = 0;
283         c->flags = 0;
284 }
285
286
287
288 /** free memory.
289  * Frees previously allocated memory chunks until at least size bytes are 
290  * released. Use -1 to free all,
291  * @param size - at least free size bytes.
292  * @return  bytes_freed (>=0)
293  */
294 static unsigned long mem_unleak(unsigned long size)
295 {
296         struct mem_chunk** mc;
297         struct mem_chunk* t;
298         struct mem_chunk** min_chunk;
299         unsigned long freed;
300         
301         freed = 0;
302         min_chunk = 0;
303         lock_get(&alloc_lst->lock);
304         if (size>=atomic_get_long(&alloc_lst->size)){
305                 /* free all */
306                 for (mc = &alloc_lst->chunks; *mc; ){
307                         t = *mc;
308                         mem_chunk_free(t);
309                         freed += t->size;
310                         *mc = t->next;
311                         shm_free(t);
312                 }
313                 alloc_lst->chunks=0;
314         } else {
315                 /* free at least size bytes, trying smaller chunks first */
316                 for (mc = &alloc_lst->chunks; *mc && (freed < size);) {
317                         if ((*mc)->size <= (size - freed)) {
318                                 t = *mc;
319                                 mem_chunk_free(t);
320                                 freed += t->size;
321                                 *mc = t->next;
322                                 shm_free(t);
323                                 continue;
324                         } else if (min_chunk == 0 || (*min_chunk)->size > (*mc)->size) {
325                                 /* find minimum remaining chunk  */
326                                 min_chunk = mc;
327                         }
328                         mc = &(*mc)->next;
329                 }
330                 if (size > freed && min_chunk) {
331                         mc = min_chunk;
332                         t = *mc;
333                         mem_chunk_free(t);
334                         freed += t->size;
335                         *mc = (*mc)->next;
336                         shm_free(t);
337                 }
338         }
339         lock_release(&alloc_lst->lock);
340         atomic_add_long(&alloc_lst->size, -freed);
341         return freed;
342 }
343
344
345 #define MIN_ulong(a, b) \
346         (unsigned long)((unsigned long)(a)<(unsigned long)(b)?(a):(b))
347
348 /*
349  * Randomly alloc. total_size bytes, in chunks of size between
350  * min & max. max - min should be smaller then 4G.
351  * @return < 0 if there were some alloc errors, 0 on success.
352  */
353 static int mem_rnd_leak(unsigned long min, unsigned long max,
354                                                 unsigned long total_size)
355 {
356         unsigned long size;
357         unsigned long crt_size, crt_min;
358         int err;
359         
360         size = total_size;
361         err = 0;
362         while(size){
363                 crt_min = MIN_ulong(min, size);
364                 crt_size = fastrand_max(MIN_ulong(max, size) - crt_min) + crt_min;
365                 size -= crt_size;
366                 err += mem_leak(crt_size) < 0;
367         }
368         return -err;
369 }
370
371
372
373 /* test timer */
374 static ticks_t tst_timer(ticks_t ticks, struct timer_ln* tl, void* data)
375 {
376         struct rnd_time_test* tst;
377         ticks_t next_int;
378         ticks_t max_int;
379         unsigned long crt_size, crt_min, remaining;
380         
381         tst = data;
382         
383         next_int = 0;
384         max_int = 0;
385         
386         if (tst->total <= tst->crt) {
387                 mem_unleak(tst->crt);
388                 tst->crt = 0;
389                 tst->overfl++;
390         }
391         remaining = tst->total - tst->crt;
392         crt_min = MIN_ulong(tst->min, remaining);
393         crt_size = fastrand_max(MIN_ulong(tst->max, remaining) - crt_min) +
394                                 crt_min;
395         if (mem_leak(crt_size) >= 0)
396                 tst->crt += crt_size;
397         else
398                 tst->errs ++;
399         tst->calls++;
400         
401         if (TICKS_GT(tst->stop_time, ticks)) {
402                 next_int = fastrand_max(tst->max_intvrl - tst->min_intvrl) +
403                                 tst->min_intvrl;
404                 max_int = tst->stop_time - ticks;
405         } else {
406                 /* stop test */
407                 WARN("test %d time expired, stopping"
408                                 " (%d s runtime, %ld calls, %d overfl, %d errors,"
409                                 " crt %ld bytes)\n",
410                                 tst->id, TICKS_TO_S(ticks - tst->start_time),
411                                 tst->calls, tst->overfl, tst->errs, tst->crt);
412                 mem_unleak(tst->crt);
413                 /* tst->crt = 0 */;
414         }
415         
416         /* 0 means stop stop, so if next_int == 0 => stop */
417         return MIN_unsigned(next_int, max_int);
418 }
419
420
421 /*
422  * start a malloc test of a test_time length:
423  *  - randomly between min_intvrl and max_intvrl, alloc.
424  *    a random number of bytes, between min & max.
425  *  - if total_size is reached, free everything.
426  *
427  * @returns 0 on success, -1 on error.
428  */
429 static int mem_leak_time_test(unsigned long min, unsigned long max,
430                                                                 unsigned long total_size,
431                                                                 ticks_t min_intvrl, ticks_t max_intvrl,
432                                                                 ticks_t test_time)
433 {
434         struct rnd_time_test* tst;
435         struct rnd_time_test* l;
436         ticks_t first_int;
437         
438         tst = shm_malloc(sizeof(*tst));
439         if (tst == 0)
440                 goto error;
441         memset(tst, 0, sizeof(*tst));
442         tst->id = atomic_add_int(&rndt_lst->last_id, 1);
443         tst->min = min;
444         tst->max = max;
445         tst-> total = total_size;
446         tst->min_intvrl = min_intvrl;
447         tst->max_intvrl = max_intvrl;
448         tst->start_time = get_ticks_raw();
449         tst->stop_time = get_ticks_raw() + test_time;
450         first_int = fastrand_max(max_intvrl - min_intvrl) + min_intvrl;
451         timer_init(&tst->timer, tst_timer, tst, 0);
452         lock_get(&rndt_lst->lock);
453                 tst->next=rndt_lst->tests;
454                 rndt_lst->tests=tst;
455         lock_release(&rndt_lst->lock);
456         if (timer_add(&tst->timer, MIN_unsigned(first_int, test_time)) < 0 )
457                 goto error;
458         return 0;
459 error:
460         if (tst) {
461                 lock_get(&rndt_lst->lock);
462                         for (l=rndt_lst->tests; l; l=l->next)
463                                 if (l->next == tst) {
464                                         l->next = tst->next;
465                                         break;
466                                 }
467                 lock_release(&rndt_lst->lock);
468                 shm_free(tst);
469         }
470         return -1;
471 }
472
473
474 static int is_mem_test_stopped(struct rnd_time_test* tst)
475 {
476         return TICKS_LE(tst->stop_time, get_ticks_raw());
477 }
478
479 /** stops test tst.
480  * @return 0 on success, -1 on error (test already stopped)
481  */
482 static int mem_test_stop_tst(struct rnd_time_test* tst)
483 {
484         if (!is_mem_test_stopped(tst)) {
485                 if (timer_del(&tst->timer) == 0) {
486                         tst->stop_time=get_ticks_raw();
487                         return 0;
488                 }
489         }
490         return -1;
491 }
492
493
494 /** stops test id.
495  * @return 0 on success, -1 on error (not found).
496  */
497 static int mem_test_stop(int id)
498 {
499         struct rnd_time_test* tst;
500         
501         lock_get(&rndt_lst->lock);
502                 for (tst = rndt_lst->tests; tst; tst = tst->next)
503                         if (tst->id == id) {
504                                 mem_test_stop_tst(tst);
505                                 break;
506                         }
507         lock_release(&rndt_lst->lock);
508         return -(tst == 0);
509 }
510
511
512 static void mem_destroy_all_tests()
513 {
514         struct rnd_time_test* tst;
515         struct rnd_time_test* nxt;
516         
517         lock_get(&rndt_lst->lock);
518                 for (tst = rndt_lst->tests; tst;) {
519                         nxt = tst->next;
520                         mem_test_stop_tst(tst);
521                         shm_free(tst);
522                         tst = nxt;
523                 }
524                 rndt_lst->tests = 0;
525         lock_release(&rndt_lst->lock);
526 }
527
528
529 static int mem_test_destroy(int id)
530 {
531         struct rnd_time_test* tst;
532         struct rnd_time_test** crt_lnk;
533         
534         lock_get(&rndt_lst->lock);
535                 for (tst = 0, crt_lnk = &rndt_lst->tests; *crt_lnk;
536                                 crt_lnk = &(*crt_lnk)->next)
537                         if ((*crt_lnk)->id == id) {
538                                 tst=*crt_lnk;
539                                 mem_test_stop_tst(tst);
540                                 *crt_lnk=tst->next;
541                                 shm_free(tst);
542                                 break;
543                         }
544         lock_release(&rndt_lst->lock);
545         return -(tst == 0);
546 }
547
548 /* script functions: */
549
550
551 static int mt_mem_alloc_f(struct sip_msg* msg, char* sz, char* foo)
552 {
553         int size;
554         
555         if (sz == 0 || get_int_fparam(&size, msg, (fparam_t*)sz) < 0)
556                 return -1;
557         return mem_leak(size)>=0?1:-1;
558 }
559
560
561
562 static int mt_mem_free_f(struct sip_msg* msg, char* sz, char* foo)
563 {
564         int size;
565         unsigned long freed;
566         
567         size=-1;
568         if (sz != 0 && get_int_fparam(&size, msg, (fparam_t*)sz) < 0)
569                 return -1;
570         freed=mem_unleak(size);
571         return (freed==0)?1:freed;
572 }
573
574
575
576 /* RPC exports: */
577
578
579
580 /* helper functions, parses an optional b[ytes]|k|m|g to a numeric shift value
581    (e.g. b -> 0, k -> 10, ...)
582    returns bit shift value on success, -1 on error
583 */
584 static int rpc_get_size_mod(rpc_t* rpc, void* c)
585 {
586         char* m;
587         
588         if (rpc->scan(c, "*s", &m) > 0) {
589                 switch(*m) {
590                         case 'b':
591                         case 'B':
592                                 return 0;
593                         case 'k':
594                         case 'K':
595                                 return 10;
596                         case 'm':
597                         case 'M':
598                                 return 20;
599                         case 'g':
600                         case 'G':
601                                 return 30;
602                         default:
603                                 rpc->fault(c, 500, "bad param use b|k|m|g");
604                                 return -1;
605                 }
606         }
607         return 0;
608 }
609
610
611
612 static const char* rpc_mt_alloc_doc[2] = {
613         "Allocates the specified number of bytes (debugging/test function)."
614         "Use b|k|m|g to specify the desired size unit",
615         0
616 };
617
618 static void rpc_mt_alloc(rpc_t* rpc, void* c)
619 {
620         int size;
621         int rs;
622         
623         if (rpc->scan(c, "d", &size) < 1) {
624                 return;
625         }
626         rs=rpc_get_size_mod(rpc, c);
627         if (rs<0)
628                 /* fault already generated on rpc_get_size_mod() error */
629                 return;
630         if (mem_leak((unsigned long)size << rs) < 0) {
631                 rpc->fault(c, 400, "memory allocation failed");
632         }
633         return;
634 }
635
636
637 static const char* rpc_mt_free_doc[2] = {
638         "Frees the specified number of bytes, previously allocated by one of the"
639         " other malloc_test functions (e.g. mt.mem_alloc or the script "
640         "mt_mem_alloc). Use b|k|m|g to specify the desired size unit."
641         "Returns the number of bytes freed (can be higher or"
642          " smaller then the requested size)",
643         0
644 };
645
646
647 static void rpc_mt_free(rpc_t* rpc, void* c)
648 {
649         int size;
650         int rs;
651         
652         size = -1;
653         rs = 0;
654         if (rpc->scan(c, "*d", &size) > 0) {
655                 /* found size, look if a size modifier is present */
656                 rs=rpc_get_size_mod(rpc, c);
657                 if (rs<0)
658                         /* fault already generated on rpc_get_size_mod() error */
659                         return;
660         }
661         rpc->add(c, "d", (int)(mem_unleak((unsigned long)size << rs) >> rs));
662         return;
663 }
664
665
666
667 static const char* rpc_mt_used_doc[2] = {
668         "Returns how many bytes are currently allocated via the mem_alloc module"
669         " functions. Use b|k|m|g to specify the desired size unit.",
670         0
671 };
672
673
674 static void rpc_mt_used(rpc_t* rpc, void* c)
675 {
676         int rs;
677         
678         rs = 0;
679         rs=rpc_get_size_mod(rpc, c);
680         if (rs<0)
681                 /* fault already generated on rpc_get_size_mod() error */
682                 return;
683         rpc->add(c, "d", (int)(atomic_get_long(&alloc_lst->size) >> rs));
684         return;
685 }
686
687
688 static const char* rpc_mt_rnd_alloc_doc[2] = {
689         "Takes 4 parameters: min, max, total_size and an optional unit (b|k|m|g)."
690         " It will allocate total_size memory, in pieces of random size between"
691         "min .. max (inclusive).",
692         0
693 };
694
695
696 static void rpc_mt_rnd_alloc(rpc_t* rpc, void* c)
697 {
698         int min, max, total_size;
699         int rs;
700         
701         if (rpc->scan(c, "ddd", &min, &max, &total_size) < 3) {
702                 return;
703         }
704         rs=rpc_get_size_mod(rpc, c);
705         if (rs<0)
706                 /* fault already generated on rpc_get_size_mod() error */
707                 return;
708         if (min > max || min < 0 || max > total_size) {
709                 rpc->fault(c, 400, "invalid parameter values");
710                 return;
711         }
712         if (mem_rnd_leak((unsigned long)min << rs,
713                                          (unsigned long)max << rs,
714                                          (unsigned long)total_size <<rs ) < 0) {
715                 rpc->fault(c, 400, "memory allocation failed");
716         }
717         return;
718 }
719
720
721 static const char* rpc_mt_test_start_doc[2] = {
722         "Takes 7 parameters: min, max, total_size, min_interval, max_interval, "
723         "test_time and an optional size unit (b|k|m|g). All the time units are ms."
724         " It will run a memory allocation test for test_time ms. At a random"
725         " interval between min_interval and max_interval ms. it will allocate a"
726         " memory chunk with random size, between min and max. Each time total_size"
727         " is reached, it will free all the memory allocated and start again."
728         "Returns the test id (integer)",
729         0
730 };
731
732
733 static void rpc_mt_test_start(rpc_t* rpc, void* c)
734 {
735         int min, max, total_size;
736         int min_intvrl, max_intvrl, total_time;
737         int rs;
738         
739         if (rpc->scan(c, "dddddd", &min, &max, &total_size,
740                                                                 &min_intvrl, &max_intvrl, &total_time) < 6) {
741                 return;
742         }
743         rs=rpc_get_size_mod(rpc, c);
744         if (rs<0)
745                 /* fault already generated on rpc_get_size_mod() error */
746                 return;
747         if (min > max || min < 0 || max > total_size) {
748                 rpc->fault(c, 400, "invalid size parameters values");
749                 return;
750         }
751         if (min_intvrl > max_intvrl || min_intvrl <= 0 || max_intvrl > total_time){
752                 rpc->fault(c, 400, "invalid time intervals values");
753                 return;
754         }
755         if (mem_leak_time_test((unsigned long)min << rs,
756                                          (unsigned long)max << rs,
757                                          (unsigned long)total_size <<rs,
758                                          MS_TO_TICKS(min_intvrl),
759                                          MS_TO_TICKS(max_intvrl),
760                                          MS_TO_TICKS(total_time)
761                                          ) < 0) {
762                 rpc->fault(c, 400, "memory allocation failed");
763         }
764         return;
765 }
766
767
768 static const char* rpc_mt_test_stop_doc[2] = {
769         "Takes 1 parameter: the test id. It will stop the corresponding test."
770         "Note: the test is stopped, but not destroyed." ,
771         0
772 };
773
774
775 static void rpc_mt_test_stop(rpc_t* rpc, void* c)
776 {
777         int id;
778         
779         if (rpc->scan(c, "d", &id) < 1) {
780                 return;
781         }
782         if (mem_test_stop(id)<0) {
783                 rpc->fault(c, 400, "test %d not found", id);
784         }
785         return;
786 }
787
788
789 static const char* rpc_mt_test_destroy_doc[2] = {
790         "Takes 1 parameter: the test id. It will destroy the corresponding test.",
791         0
792 };
793
794
795 static void rpc_mt_test_destroy(rpc_t* rpc, void* c)
796 {
797         int id;
798         
799         if (rpc->scan(c, "*d", &id) > 0 && id!=-1) {
800                 if (mem_test_destroy(id) < 0 )
801                         rpc->fault(c, 400, "test %d not found", id);
802         } else {
803                 mem_destroy_all_tests();
804         }
805         return;
806 }
807
808
809 static const char* rpc_mt_test_destroy_all_doc[2] = {
810         "It will destroy all the tests (running or stopped).",
811         0
812 };
813
814
815 static void rpc_mt_test_destroy_all(rpc_t* rpc, void* c)
816 {
817         mem_destroy_all_tests();
818         return;
819 }
820
821
822 static const char* rpc_mt_test_list_doc[2] = {
823         "If a test id parameter is provided it will list the corresponding test,"
824         " else it will list all of them",
825         0
826 };
827
828
829 static void rpc_mt_test_list(rpc_t* rpc, void* c)
830 {
831         int id, rs;
832         struct rnd_time_test* tst;
833         void *h;
834         
835         rs = 0;
836         if (rpc->scan(c, "*d", &id) < 1) {
837                 id = -1;
838         } else {
839                 rs=rpc_get_size_mod(rpc, c);
840                 if (rs < 0)
841                         return;
842         }
843         lock_get(&rndt_lst->lock);
844                 for (tst = rndt_lst->tests; tst; tst=tst->next)
845                         if (tst->id == id || id == -1) {
846                                 rpc->add(c, "{", &h);
847                                 rpc->struct_add(h, "dddddddddd",
848                                                 "ID           ",  tst->id,
849                                                 "run time (s) ", (int)TICKS_TO_S((
850                                                                                         TICKS_LE(tst->stop_time,
851                                                                                                         get_ticks_raw()) ?
852                                                                                         tst->stop_time : get_ticks_raw()) -
853                                                                                                         tst->start_time),
854                                                 "remaining (s)", TICKS_LE(tst->stop_time,
855                                                                                                 get_ticks_raw()) ? 0 :
856                                                                                 (int)TICKS_TO_S(tst->stop_time -
857                                                                                                                 get_ticks_raw()),
858                                                 "allocations  ", (int)tst->calls,
859                                                 "errors       ", (int)tst->errs,
860                                                 "overflows    ", (int)tst->overfl,
861                                                 "total alloc  ", (int)((tst->crt +
862                                                                                         tst->overfl * tst->total)>>rs),
863                                                 "min          ", (int)(tst->min>>rs),
864                                                 "max          ", (int)(tst->max>>rs),
865                                                 "total        ", (int)(tst->total>>rs) );
866                                 if (id != -1) break;
867                         }
868         lock_release(&rndt_lst->lock);
869         
870         return;
871 }
872
873
874 static rpc_export_t mt_rpc[] = {
875         {"mt.mem_alloc", rpc_mt_alloc, rpc_mt_alloc_doc, 0},
876         {"mt.mem_free", rpc_mt_free, rpc_mt_free_doc, 0},
877         {"mt.mem_used", rpc_mt_used, rpc_mt_used_doc, 0},
878         {"mt.mem_rnd_alloc", rpc_mt_rnd_alloc, rpc_mt_rnd_alloc_doc, 0},
879         {"mt.mem_test_start", rpc_mt_test_start, rpc_mt_test_start_doc, 0},
880         {"mt.mem_test_stop", rpc_mt_test_stop, rpc_mt_test_stop_doc, 0},
881         {"mt.mem_test_destroy", rpc_mt_test_destroy, rpc_mt_test_destroy_doc, 0},
882         {"mt.mem_test_destroy_all", rpc_mt_test_destroy_all,
883                                                                 rpc_mt_test_destroy_all_doc, 0},
884         {"mt.mem_test_list", rpc_mt_test_list, rpc_mt_test_list_doc, 0},
885         {0, 0, 0, 0}
886 };
887