89172f18a469b7126a532466e7a4313daf305429
[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 test id (>=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         int id;
438         
439         tst = shm_malloc(sizeof(*tst));
440         if (tst == 0)
441                 goto error;
442         memset(tst, 0, sizeof(*tst));
443         id = tst->id = atomic_add_int(&rndt_lst->last_id, 1);
444         tst->min = min;
445         tst->max = max;
446         tst-> total = total_size;
447         tst->min_intvrl = min_intvrl;
448         tst->max_intvrl = max_intvrl;
449         tst->start_time = get_ticks_raw();
450         tst->stop_time = get_ticks_raw() + test_time;
451         first_int = fastrand_max(max_intvrl - min_intvrl) + min_intvrl;
452         timer_init(&tst->timer, tst_timer, tst, 0);
453         lock_get(&rndt_lst->lock);
454                 tst->next=rndt_lst->tests;
455                 rndt_lst->tests=tst;
456         lock_release(&rndt_lst->lock);
457         if (timer_add(&tst->timer, MIN_unsigned(first_int, test_time)) < 0 )
458                 goto error;
459         return id;
460 error:
461         if (tst) {
462                 lock_get(&rndt_lst->lock);
463                         for (l=rndt_lst->tests; l; l=l->next)
464                                 if (l->next == tst) {
465                                         l->next = tst->next;
466                                         break;
467                                 }
468                 lock_release(&rndt_lst->lock);
469                 shm_free(tst);
470         }
471         return -1;
472 }
473
474
475 static int is_mem_test_stopped(struct rnd_time_test* tst)
476 {
477         return TICKS_LE(tst->stop_time, get_ticks_raw());
478 }
479
480 /** stops test tst.
481  * @return 0 on success, -1 on error (test already stopped)
482  */
483 static int mem_test_stop_tst(struct rnd_time_test* tst)
484 {
485         if (!is_mem_test_stopped(tst)) {
486                 if (timer_del(&tst->timer) == 0) {
487                         tst->stop_time=get_ticks_raw();
488                         return 0;
489                 }
490         }
491         return -1;
492 }
493
494
495 /** stops test id.
496  * @return 0 on success, -1 on error (not found).
497  */
498 static int mem_test_stop(int id)
499 {
500         struct rnd_time_test* tst;
501         
502         lock_get(&rndt_lst->lock);
503                 for (tst = rndt_lst->tests; tst; tst = tst->next)
504                         if (tst->id == id) {
505                                 mem_test_stop_tst(tst);
506                                 break;
507                         }
508         lock_release(&rndt_lst->lock);
509         return -(tst == 0);
510 }
511
512
513 static void mem_destroy_all_tests()
514 {
515         struct rnd_time_test* tst;
516         struct rnd_time_test* nxt;
517         
518         lock_get(&rndt_lst->lock);
519                 for (tst = rndt_lst->tests; tst;) {
520                         nxt = tst->next;
521                         mem_test_stop_tst(tst);
522                         shm_free(tst);
523                         tst = nxt;
524                 }
525                 rndt_lst->tests = 0;
526         lock_release(&rndt_lst->lock);
527 }
528
529
530 static int mem_test_destroy(int id)
531 {
532         struct rnd_time_test* tst;
533         struct rnd_time_test** crt_lnk;
534         
535         lock_get(&rndt_lst->lock);
536                 for (tst = 0, crt_lnk = &rndt_lst->tests; *crt_lnk;
537                                 crt_lnk = &(*crt_lnk)->next)
538                         if ((*crt_lnk)->id == id) {
539                                 tst=*crt_lnk;
540                                 mem_test_stop_tst(tst);
541                                 *crt_lnk=tst->next;
542                                 shm_free(tst);
543                                 break;
544                         }
545         lock_release(&rndt_lst->lock);
546         return -(tst == 0);
547 }
548
549 /* script functions: */
550
551
552 static int mt_mem_alloc_f(struct sip_msg* msg, char* sz, char* foo)
553 {
554         int size;
555         
556         if (sz == 0 || get_int_fparam(&size, msg, (fparam_t*)sz) < 0)
557                 return -1;
558         return mem_leak(size)>=0?1:-1;
559 }
560
561
562
563 static int mt_mem_free_f(struct sip_msg* msg, char* sz, char* foo)
564 {
565         int size;
566         unsigned long freed;
567         
568         size=-1;
569         if (sz != 0 && get_int_fparam(&size, msg, (fparam_t*)sz) < 0)
570                 return -1;
571         freed=mem_unleak(size);
572         return (freed==0)?1:freed;
573 }
574
575
576
577 /* RPC exports: */
578
579
580
581 /* helper functions, parses an optional b[ytes]|k|m|g to a numeric shift value
582    (e.g. b -> 0, k -> 10, ...)
583    returns bit shift value on success, -1 on error
584 */
585 static int rpc_get_size_mod(rpc_t* rpc, void* c)
586 {
587         char* m;
588         
589         if (rpc->scan(c, "*s", &m) > 0) {
590                 switch(*m) {
591                         case 'b':
592                         case 'B':
593                                 return 0;
594                         case 'k':
595                         case 'K':
596                                 return 10;
597                         case 'm':
598                         case 'M':
599                                 return 20;
600                         case 'g':
601                         case 'G':
602                                 return 30;
603                         default:
604                                 rpc->fault(c, 500, "bad param use b|k|m|g");
605                                 return -1;
606                 }
607         }
608         return 0;
609 }
610
611
612
613 static const char* rpc_mt_alloc_doc[2] = {
614         "Allocates the specified number of bytes (debugging/test function)."
615         "Use b|k|m|g to specify the desired size unit",
616         0
617 };
618
619 static void rpc_mt_alloc(rpc_t* rpc, void* c)
620 {
621         int size;
622         int rs;
623         
624         if (rpc->scan(c, "d", &size) < 1) {
625                 return;
626         }
627         rs=rpc_get_size_mod(rpc, c);
628         if (rs<0)
629                 /* fault already generated on rpc_get_size_mod() error */
630                 return;
631         if (mem_leak((unsigned long)size << rs) < 0) {
632                 rpc->fault(c, 400, "memory allocation failed");
633         }
634         return;
635 }
636
637
638 static const char* rpc_mt_free_doc[2] = {
639         "Frees the specified number of bytes, previously allocated by one of the"
640         " other malloc_test functions (e.g. mt.mem_alloc or the script "
641         "mt_mem_alloc). Use b|k|m|g to specify the desired size unit."
642         "Returns the number of bytes freed (can be higher or"
643          " smaller then the requested size)",
644         0
645 };
646
647
648 static void rpc_mt_free(rpc_t* rpc, void* c)
649 {
650         int size;
651         int rs;
652         
653         size = -1;
654         rs = 0;
655         if (rpc->scan(c, "*d", &size) > 0) {
656                 /* found size, look if a size modifier is present */
657                 rs=rpc_get_size_mod(rpc, c);
658                 if (rs<0)
659                         /* fault already generated on rpc_get_size_mod() error */
660                         return;
661         }
662         rpc->add(c, "d", (int)(mem_unleak((unsigned long)size << rs) >> rs));
663         return;
664 }
665
666
667
668 static const char* rpc_mt_used_doc[2] = {
669         "Returns how many bytes are currently allocated via the mem_alloc module"
670         " functions. Use b|k|m|g to specify the desired size unit.",
671         0
672 };
673
674
675 static void rpc_mt_used(rpc_t* rpc, void* c)
676 {
677         int rs;
678         
679         rs = 0;
680         rs=rpc_get_size_mod(rpc, c);
681         if (rs<0)
682                 /* fault already generated on rpc_get_size_mod() error */
683                 return;
684         rpc->add(c, "d", (int)(atomic_get_long(&alloc_lst->size) >> rs));
685         return;
686 }
687
688
689 static const char* rpc_mt_rnd_alloc_doc[2] = {
690         "Takes 4 parameters: min, max, total_size and an optional unit (b|k|m|g)."
691         " It will allocate total_size memory, in pieces of random size between"
692         "min .. max (inclusive).",
693         0
694 };
695
696
697 static void rpc_mt_rnd_alloc(rpc_t* rpc, void* c)
698 {
699         int min, max, total_size;
700         int rs;
701         
702         if (rpc->scan(c, "ddd", &min, &max, &total_size) < 3) {
703                 return;
704         }
705         rs=rpc_get_size_mod(rpc, c);
706         if (rs<0)
707                 /* fault already generated on rpc_get_size_mod() error */
708                 return;
709         if (min > max || min < 0 || max > total_size) {
710                 rpc->fault(c, 400, "invalid parameter values");
711                 return;
712         }
713         if (mem_rnd_leak((unsigned long)min << rs,
714                                          (unsigned long)max << rs,
715                                          (unsigned long)total_size <<rs ) < 0) {
716                 rpc->fault(c, 400, "memory allocation failed");
717         }
718         return;
719 }
720
721
722 static const char* rpc_mt_test_start_doc[2] = {
723         "Takes 7 parameters: min, max, total_size, min_interval, max_interval, "
724         "test_time and an optional size unit (b|k|m|g). All the time units are ms."
725         " It will run a memory allocation test for test_time ms. At a random"
726         " interval between min_interval and max_interval ms. it will allocate a"
727         " memory chunk with random size, between min and max. Each time total_size"
728         " is reached, it will free all the memory allocated and start again."
729         "Returns the test id (integer)",
730         0
731 };
732
733
734 static void rpc_mt_test_start(rpc_t* rpc, void* c)
735 {
736         int min, max, total_size;
737         int min_intvrl, max_intvrl, total_time;
738         int rs;
739         int id;
740         
741         if (rpc->scan(c, "dddddd", &min, &max, &total_size,
742                                                                 &min_intvrl, &max_intvrl, &total_time) < 6) {
743                 return;
744         }
745         rs=rpc_get_size_mod(rpc, c);
746         if (rs<0)
747                 /* fault already generated on rpc_get_size_mod() error */
748                 return;
749         if (min > max || min < 0 || max > total_size) {
750                 rpc->fault(c, 400, "invalid size parameters values");
751                 return;
752         }
753         if (min_intvrl > max_intvrl || min_intvrl <= 0 || max_intvrl > total_time){
754                 rpc->fault(c, 400, "invalid time intervals values");
755                 return;
756         }
757         if ((id=mem_leak_time_test((unsigned long)min << rs,
758                                          (unsigned long)max << rs,
759                                          (unsigned long)total_size <<rs,
760                                          MS_TO_TICKS(min_intvrl),
761                                          MS_TO_TICKS(max_intvrl),
762                                          MS_TO_TICKS(total_time)
763                                          )) < 0) {
764                 rpc->fault(c, 400, "memory allocation failed");
765         } else {
766                 rpc->add(c, "d", id);
767         }
768         return;
769 }
770
771
772 static const char* rpc_mt_test_stop_doc[2] = {
773         "Takes 1 parameter: the test id. It will stop the corresponding test."
774         "Note: the test is stopped, but not destroyed." ,
775         0
776 };
777
778
779 static void rpc_mt_test_stop(rpc_t* rpc, void* c)
780 {
781         int id;
782         
783         if (rpc->scan(c, "d", &id) < 1) {
784                 return;
785         }
786         if (mem_test_stop(id)<0) {
787                 rpc->fault(c, 400, "test %d not found", id);
788         }
789         return;
790 }
791
792
793 static const char* rpc_mt_test_destroy_doc[2] = {
794         "Takes 1 parameter: the test id. It will destroy the corresponding test.",
795         0
796 };
797
798
799 static void rpc_mt_test_destroy(rpc_t* rpc, void* c)
800 {
801         int id;
802         
803         if (rpc->scan(c, "*d", &id) > 0 && id!=-1) {
804                 if (mem_test_destroy(id) < 0 )
805                         rpc->fault(c, 400, "test %d not found", id);
806         } else {
807                 mem_destroy_all_tests();
808         }
809         return;
810 }
811
812
813 static const char* rpc_mt_test_destroy_all_doc[2] = {
814         "It will destroy all the tests (running or stopped).",
815         0
816 };
817
818
819 static void rpc_mt_test_destroy_all(rpc_t* rpc, void* c)
820 {
821         mem_destroy_all_tests();
822         return;
823 }
824
825
826 static const char* rpc_mt_test_list_doc[2] = {
827         "If a test id parameter is provided it will list the corresponding test,"
828         " else it will list all of them. Use b |k | m | g as a second parameter"
829         " for the size units (default bytes)",
830         0
831 };
832
833
834 static void rpc_mt_test_list(rpc_t* rpc, void* c)
835 {
836         int id, rs;
837         struct rnd_time_test* tst;
838         void *h;
839         
840         rs = 0;
841         if (rpc->scan(c, "*d", &id) < 1) {
842                 id = -1;
843         } else {
844                 rs=rpc_get_size_mod(rpc, c);
845                 if (rs < 0)
846                         return;
847         }
848         lock_get(&rndt_lst->lock);
849                 for (tst = rndt_lst->tests; tst; tst=tst->next)
850                         if (tst->id == id || id == -1) {
851                                 rpc->add(c, "{", &h);
852                                 rpc->struct_add(h, "dddddddddd",
853                                                 "ID           ",  tst->id,
854                                                 "run time (s) ", (int)TICKS_TO_S((
855                                                                                         TICKS_LE(tst->stop_time,
856                                                                                                         get_ticks_raw()) ?
857                                                                                         tst->stop_time : get_ticks_raw()) -
858                                                                                                         tst->start_time),
859                                                 "remaining (s)", TICKS_LE(tst->stop_time,
860                                                                                                 get_ticks_raw()) ? 0 :
861                                                                                 (int)TICKS_TO_S(tst->stop_time -
862                                                                                                                 get_ticks_raw()),
863                                                 "allocations  ", (int)tst->calls,
864                                                 "errors       ", (int)tst->errs,
865                                                 "overflows    ", (int)tst->overfl,
866                                                 "total alloc  ", (int)((tst->crt +
867                                                                                         tst->overfl * tst->total)>>rs),
868                                                 "min          ", (int)(tst->min>>rs),
869                                                 "max          ", (int)(tst->max>>rs),
870                                                 "total        ", (int)(tst->total>>rs) );
871                                 if (id != -1) break;
872                         }
873         lock_release(&rndt_lst->lock);
874         
875         return;
876 }
877
878
879 static rpc_export_t mt_rpc[] = {
880         {"mt.mem_alloc", rpc_mt_alloc, rpc_mt_alloc_doc, 0},
881         {"mt.mem_free", rpc_mt_free, rpc_mt_free_doc, 0},
882         {"mt.mem_used", rpc_mt_used, rpc_mt_used_doc, 0},
883         {"mt.mem_rnd_alloc", rpc_mt_rnd_alloc, rpc_mt_rnd_alloc_doc, 0},
884         {"mt.mem_test_start", rpc_mt_test_start, rpc_mt_test_start_doc, 0},
885         {"mt.mem_test_stop", rpc_mt_test_stop, rpc_mt_test_stop_doc, 0},
886         {"mt.mem_test_destroy", rpc_mt_test_destroy, rpc_mt_test_destroy_doc, 0},
887         {"mt.mem_test_destroy_all", rpc_mt_test_destroy_all,
888                                                                 rpc_mt_test_destroy_all_doc, 0},
889         {"mt.mem_test_list", rpc_mt_test_list, rpc_mt_test_list_doc, 0},
890         {0, 0, 0, 0}
891 };
892