68f70718fc061e2ecf8cb8452cedf42216fe3400
[kamailio] / src / modules / misctest / misctest_mod.c
1 /**
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
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "../../core/sr_module.h"
27 #include "../../core/mem/mem.h"
28 #include "../../core/str.h"
29 #include "../../core/dprint.h"
30 #include "../../core/locking.h"
31 #include "../../core/atomic_ops.h"
32 #include "../../core/cfg/cfg.h"
33 #include "../../core/rpc.h"
34 #include "../../core/rand/fastrand.h"
35 #include "../../core/timer.h"
36 #include "../../core/mod_fix.h"
37
38 #include "../../core/parser/sdp/sdp.h"
39 #include "../../core/parser/parse_uri.c"
40 #include "../../core/parser/parse_hname2.h"
41 #include "../../core/parser/contact/parse_contact.h"
42 #include "../../core/parser/parse_refer_to.h"
43 #include "../../core/parser/parse_ppi_pai.h"
44 #include "../../core/parser/parse_privacy.h"
45 #include "../../core/parser/parse_diversion.h"
46
47 MODULE_VERSION
48
49 static int mt_mem_alloc_f(struct sip_msg *, char *, char *);
50 static int mt_mem_free_f(struct sip_msg *, char *, char *);
51 static int mod_init(void);
52 static void mod_destroy(void);
53
54 static int misctest_memory_init(void);
55 static int misctest_message_init(void);
56 int misctest_hexprint(void *data, size_t length, int linelen, int split);
57
58 static int misctest_memory = 0;
59 static int misctest_message = 0;
60 static str misctest_message_data = STR_NULL;
61 static str misctest_message_file = STR_NULL;
62
63 /* clang-format off */
64 static cmd_export_t cmds[]={
65         {"mt_mem_alloc", mt_mem_alloc_f, 1, fixup_var_int_1, 0,
66                 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONSEND_ROUTE},
67         {"mt_mem_free", mt_mem_free_f, 1, fixup_var_int_1, 0,
68                 REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE|ONSEND_ROUTE},
69         {0, 0, 0, 0, 0}
70 };
71 /* clang-format on */
72
73
74 /* clang-format off */
75 struct cfg_group_misctest {
76         int mem_check_content;
77         int mem_realloc_p; /* realloc probability */
78 };
79 /* clang-format on */
80
81
82 /* clang-format off */
83 static struct cfg_group_misctest default_mt_cfg = {
84         0, /* mem_check_content, off by default */
85         0  /* mem realloc probability, 0 by default */
86 };
87 /* clang-format on */
88
89 static void *mt_cfg = &default_mt_cfg;
90
91
92 /* clang-format off */
93 static cfg_def_t misctest_cfg_def[] = {
94         {"mem_check_content", CFG_VAR_INT | CFG_ATOMIC, 0, 1, 0, 0,
95                 "check if allocated memory was overwritten by filling it with "
96                 "a special pattern and checking it on free."},
97         {"mem_realloc_p", CFG_VAR_INT | CFG_ATOMIC, 0, 90, 0, 0,
98                 "realloc probability in percents. During tests and mem_rnd_alloc"
99                 " mem_realloc_p percents of the allocations will be made by realloc'ing"
100                 " and existing chunk. The maximum value is limited to 90, to avoid"
101                 " very long mem_rnd_alloc runs (a realloc might also free memory)." },
102         {0, 0, 0, 0, 0, 0}
103 };
104 /* clang-format on */
105
106
107 static rpc_export_t mt_rpc[];
108
109
110 /* clang-format off */
111 static param_export_t params[]={
112         {"memory", PARAM_INT, &misctest_memory},
113         {"message", PARAM_INT, &misctest_message},
114         {"message_data", PARAM_STR, &misctest_message_data},
115         {"message_file", PARAM_STR, &misctest_message_file},
116         {"mem_check_content", PARAM_INT, &default_mt_cfg.mem_check_content},
117         {0,0,0}
118 };
119 /* clang-format on */
120
121
122 /* clang-format off */
123 struct module_exports exports = {
124         "misctest",
125         DEFAULT_DLFLAGS, /* dlopen flags */
126         cmds,
127         params,
128         mt_rpc,         /* RPC methods */
129         0,              /* pseudo-variables exports */
130         0,              /* response function*/
131         mod_init,       /* module initialization function */
132         0,              /* per-child init function */
133         mod_destroy     /* destroy function */
134 };
135 /* clang-format on */
136
137
138 #define MC_F_CHECK_CONTENTS 1
139
140 struct mem_chunk
141 {
142         struct mem_chunk *next;
143         void *addr;
144         unsigned long size;
145         unsigned long flags;
146 };
147
148 struct allocated_list
149 {
150         struct mem_chunk *chunks;
151         gen_lock_t lock;
152         volatile long size;
153         volatile int no;
154 };
155
156 struct allocated_list *alloc_lst = NULL;
157
158
159 struct rnd_time_test
160 {
161         unsigned long min;
162         unsigned long max;
163         unsigned long total;
164         unsigned long crt;
165         ticks_t min_intvrl;
166         ticks_t max_intvrl;
167         ticks_t stop_time;
168         ticks_t start_time;
169         unsigned long calls;
170         unsigned long reallocs;
171         unsigned int errs;
172         unsigned int overfl;
173         struct rnd_time_test *next;
174         struct timer_ln timer;
175         int id;
176 };
177
178 struct rnd_time_test_lst
179 {
180         struct rnd_time_test *tests;
181         gen_lock_t lock;
182         volatile int last_id;
183 };
184
185
186 struct rnd_time_test_lst *rndt_lst = NULL;
187
188 static unsigned long mem_unleak(unsigned long size);
189 static void mem_destroy_all_tests();
190
191 static int mod_init(void)
192 {
193         WARN("This is a test/debugging module, don't use it in production\n");
194         /* declare configuration */
195         if(cfg_declare("misctest", misctest_cfg_def, &default_mt_cfg,
196                            cfg_sizeof(misctest), &mt_cfg)) {
197                 ERR("failed to register the configuration\n");
198                 goto error;
199         }
200
201         if(misctest_memory!=0) {
202                 if(misctest_memory_init()<0) {
203                         goto error;
204                 }
205         }
206
207         if(misctest_message!=0) {
208                 if(misctest_message_init()<0) {
209                         goto error;
210                 }
211                 return -1;
212         }
213
214         return 0;
215 error:
216         return -1;
217 }
218
219
220 static void mod_destroy()
221 {
222         if(misctest_memory!=0) {
223                 if(rndt_lst) {
224                         mem_destroy_all_tests();
225                         lock_destroy(&rndt_lst->lock);
226                         shm_free(rndt_lst);
227                         rndt_lst = 0;
228                 }
229                 if(alloc_lst) {
230                         mem_unleak(-1);
231                         lock_destroy(&alloc_lst->lock);
232                         shm_free(alloc_lst);
233                         alloc_lst = 0;
234                 }
235         }
236 }
237
238 static int misctest_memory_init(void)
239 {
240         alloc_lst = shm_malloc(sizeof(*alloc_lst));
241         if(alloc_lst == 0)
242                 goto error;
243         alloc_lst->chunks = 0;
244         atomic_set_long(&alloc_lst->size, 0);
245         atomic_set_int(&alloc_lst->no, 0);
246         if(lock_init(&alloc_lst->lock) == 0)
247                 goto error;
248         rndt_lst = shm_malloc(sizeof(*rndt_lst));
249         if(rndt_lst == 0)
250                 goto error;
251         rndt_lst->tests = 0;
252         atomic_set_int(&rndt_lst->last_id, 0);
253         if(lock_init(&rndt_lst->lock) == 0)
254                 goto error;
255
256         return 0;
257 error:
258         return -1;
259 }
260
261 static int misctest_message_init(void)
262 {
263         char tbuf[BUF_SIZE];
264         FILE *f;
265         long fsize;
266         sip_msg_t tmsg = { };
267
268         if(misctest_message_data.s!=0 && misctest_message_data.len>0) {
269                 if(misctest_message_data.len>=BUF_SIZE-2) {
270                         LM_ERR("the data is too big\n");
271                         return -1;
272                 }
273                 memcpy(tbuf, misctest_message_data.s, misctest_message_data.len);
274                 tbuf[misctest_message_data.len] = '\0';
275                 tmsg.len = misctest_message_data.len;
276         } else if(misctest_message_file.s!=0 && misctest_message_file.len>0) {
277                 LM_DBG("reading data from file: %.*s\n", misctest_message_file.len,
278                                 misctest_message_file.s);
279                 f = fopen(misctest_message_file.s, "r");
280                 if(f==NULL) {
281                         LM_ERR("cannot open file: %.*s\n", misctest_message_file.len,
282                                         misctest_message_file.s);
283                         return -1;
284                 }
285                 fseek(f, 0, SEEK_END);
286                 fsize = ftell(f);
287                 if(fsize<0) {
288                         LM_ERR("ftell failed on file: %.*s\n", misctest_message_file.len,
289                                         misctest_message_file.s);
290                         fclose(f);
291                         return -1;
292                 }
293                 fseek(f, 0, SEEK_SET);
294
295                 if(fsize>=BUF_SIZE-2) {
296                         LM_ERR("the file data is too big\n");
297                         fclose(f);
298                         return -1;
299
300                 }
301                 if(fread(tbuf, fsize, 1, f) != fsize) {
302                         if(ferror(f)) {
303                                 LM_ERR("error reading from file: %.*s\n",
304                                         misctest_message_file.len, misctest_message_file.s);
305                         }
306                 }
307                 fclose(f);
308
309                 tbuf[fsize] = 0;
310                 tmsg.len = (int)fsize;
311         } else {
312                 LM_ERR("no input data\n");
313                 return -1;
314         }
315
316         tmsg.buf = tbuf;
317
318         LM_INFO("data - start: %p - end: %p - len: %d\n", tmsg.buf,
319                         tmsg.buf + tmsg.len, tmsg.len);
320         LM_INFO("data - content: [[%.*s]]\n", tmsg.len, tmsg.buf);
321
322         misctest_hexprint(tmsg.buf, tmsg.len, 20, 10);
323
324         if (parse_msg(tmsg.buf, tmsg.len, &tmsg) < 0) {
325                 goto cleanup;
326         }
327
328         parse_sdp(&tmsg);
329
330         parse_headers(&tmsg, HDR_TO_F, 0);
331
332         parse_contact_header(&tmsg);
333
334         parse_refer_to_header(&tmsg);
335
336         parse_to_header(&tmsg);
337
338         parse_pai_header(&tmsg);
339
340         parse_diversion_header(&tmsg);
341
342         parse_privacy(&tmsg);
343
344 cleanup:
345         free_sip_msg(&tmsg);
346
347         return 0;
348 }
349
350 /**
351  *      misctest_hexprint - output a hex dump of a buffer
352  *
353  *      data - pointer to the buffer
354  *      length - length of buffer to write
355  *      linelen - number of chars to output per line
356  *      split - number of chars in each chunk on a line
357  */
358
359 int misctest_hexprint(void *data, size_t length, int linelen, int split)
360 {
361         char buffer[512];
362         char *ptr;
363         const void *inptr;
364         int pos;
365         int remaining = length;
366
367         inptr = data;
368
369         if(sizeof(buffer) <= (3 + (4 * (linelen / split)) + (linelen * 4))) {
370                 LM_ERR("buffer size is too small\n");
371                 return -1;
372         }
373
374         while (remaining > 0) {
375                 int lrem;
376                 int splitcount;
377                 ptr = buffer;
378
379                 lrem = remaining;
380                 splitcount = 0;
381                 for (pos = 0; pos < linelen; pos++) {
382
383                         if (split == splitcount++) {
384                                 sprintf(ptr, "  ");
385                                 ptr += 2;
386                                 splitcount = 1;
387                         }
388
389                         if (lrem) {
390                                 sprintf(ptr, "%02x ", *((unsigned char *) inptr + pos));
391                                 lrem--;
392                         } else {
393                                 sprintf(ptr, "   ");
394                         }
395                         ptr += 3;
396                 }
397
398                 *ptr++ = ' ';
399                 *ptr++ = ' ';
400
401                 lrem = remaining;
402                 splitcount = 0;
403                 for (pos = 0; pos < linelen; pos++) {
404                         unsigned char c;
405
406                         if (split == splitcount++) {
407                                 sprintf(ptr, "  ");
408                                 ptr += 2;
409                                 splitcount = 1;
410                         }
411
412                         if (lrem) {
413                                 c = *((unsigned char *) inptr + pos);
414                                 if (c > 31 && c < 127) {
415                                         sprintf(ptr, "%c", c);
416                                 } else {
417                                         sprintf(ptr, ".");
418                                 }
419                                 lrem--;
420                         }
421                         ptr++;
422                 }
423
424                 *ptr = '\0';
425                 LM_INFO("%s\n", buffer);
426
427                 inptr += linelen;
428                 remaining -= linelen;
429         }
430
431         return 0;
432 }
433
434 /** record a memory chunk list entry.
435  * @param addr - address of the newly allocated memory
436  * @oaram size - size
437  * @return 0 on success, -1 on error (no more mem).
438  */
439 static int mem_track(void *addr, unsigned long size)
440 {
441         struct mem_chunk *mc;
442         unsigned long *d;
443         unsigned long r, i;
444
445         mc = shm_malloc(sizeof(*mc));
446         if(mc == 0)
447                 goto error;
448         mc->addr = addr;
449         mc->size = size;
450         mc->flags = 0;
451         if(cfg_get(misctest, mt_cfg, mem_check_content)) {
452                 mc->flags |= MC_F_CHECK_CONTENTS;
453                 d = addr;
454                 for(r = 0; r < size / sizeof(*d); r++) {
455                         d[r] = ~(unsigned long)&d[r];
456                 }
457                 for(i = 0; i < size % sizeof(*d); i++) {
458                         ((char *)&d[r])[i] = ~((unsigned long)&d[r] >> i * 8);
459                 }
460         }
461         lock_get(&alloc_lst->lock);
462         mc->next = alloc_lst->chunks;
463         alloc_lst->chunks = mc;
464         lock_release(&alloc_lst->lock);
465         atomic_add_long(&alloc_lst->size, size);
466         atomic_inc_int(&alloc_lst->no);
467         return 0;
468 error:
469         return -1;
470 }
471
472
473 /** allocate memory.
474  * Allocates memory, but keeps track of it, so that mem_unleak() can
475  * free it.
476  * @param size - how many bytes
477  * @return 0 on success, -1 on error
478  */
479 static int mem_leak(unsigned long size)
480 {
481         void *d;
482
483         d = shm_malloc(size);
484         if(d) {
485                 if(mem_track(d, size) < 0) {
486                         shm_free(d);
487                 } else
488                         return 0;
489         }
490         return -1;
491 }
492
493
494 /* realloc a chunk, unsafe (requires external locking) version.
495  * @return 0 on success, -1 on error
496  */
497 static int _mem_chunk_realloc_unsafe(struct mem_chunk *c, unsigned long size)
498 {
499         unsigned long *d;
500         int r, i;
501
502         d = shm_realloc(c->addr, size);
503         if(d) {
504                 if(cfg_get(misctest, mt_cfg, mem_check_content)
505                                 && c->flags & MC_F_CHECK_CONTENTS) {
506                         /* re-fill the test patterns (the address might have changed
507                            and they depend on it) */
508                         for(r = 0; r < size / sizeof(*d); r++) {
509                                 d[r] = ~(unsigned long)&d[r];
510                         }
511                         for(i = 0; i < size % sizeof(*d); i++) {
512                                 ((char *)&d[r])[i] = ~((unsigned long)&d[r] >> i * 8);
513                         }
514                 }
515                 c->addr = d;
516                 c->size = size;
517                 return 0;
518         }
519         return -1;
520 }
521
522
523 static void mem_chunk_free(struct mem_chunk *c)
524 {
525         unsigned long *d;
526         unsigned long r, i;
527         int err;
528
529         if(cfg_get(misctest, mt_cfg, mem_check_content)
530                         && c->flags & MC_F_CHECK_CONTENTS) {
531                 d = c->addr;
532                 err = 0;
533                 for(r = 0; r < c->size / sizeof(*d); r++) {
534                         if(d[r] != ~(unsigned long)&d[r])
535                                 err++;
536                         d[r] = (unsigned long)&d[r]; /* fill it with something else */
537                 }
538                 for(i = 0; i < c->size % sizeof(*d); i++) {
539                         if(((unsigned char *)&d[r])[i]
540                                         != (unsigned char)~((unsigned long)&d[r] >> i * 8))
541                                 err++;
542                         ((char *)&d[r])[i] = (unsigned char)((unsigned long)&d[r] >> i * 8);
543                 }
544                 if(err)
545                         ERR("%d errors while checking %ld bytes at %p\n", err, c->size, d);
546         }
547         shm_free(c->addr);
548         c->addr = 0;
549         c->flags = 0;
550 }
551
552
553 /** free memory.
554  * Frees previously allocated memory chunks until at least size bytes are
555  * released. Use -1 to free all,
556  * @param size - at least free size bytes.
557  * @return  bytes_freed (>=0)
558  */
559 static unsigned long mem_unleak(unsigned long size)
560 {
561         struct mem_chunk **mc;
562         struct mem_chunk *t;
563         struct mem_chunk **min_chunk;
564         unsigned long freed;
565         unsigned int no;
566
567         freed = 0;
568         no = 0;
569         min_chunk = 0;
570         lock_get(&alloc_lst->lock);
571         if(size >= atomic_get_long(&alloc_lst->size)) {
572                 /* free all */
573                 for(mc = &alloc_lst->chunks; *mc;) {
574                         t = *mc;
575                         mem_chunk_free(t);
576                         freed += t->size;
577                         no++;
578                         *mc = t->next;
579                         shm_free(t);
580                 }
581                 alloc_lst->chunks = 0;
582         } else {
583                 /* free at least size bytes, trying smaller chunks first */
584                 for(mc = &alloc_lst->chunks; *mc && (freed < size);) {
585                         if((*mc)->size <= (size - freed)) {
586                                 t = *mc;
587                                 mem_chunk_free(t);
588                                 freed += t->size;
589                                 no++;
590                                 *mc = t->next;
591                                 shm_free(t);
592                                 continue;
593                         } else if(min_chunk == 0 || (*min_chunk)->size > (*mc)->size) {
594                                 /* find minimum remaining chunk  */
595                                 min_chunk = mc;
596                         }
597                         mc = &(*mc)->next;
598                 }
599                 if(size > freed && min_chunk) {
600                         mc = min_chunk;
601                         t = *mc;
602                         mem_chunk_free(t);
603                         freed += t->size;
604                         no++;
605                         *mc = (*mc)->next;
606                         shm_free(t);
607                 }
608         }
609         lock_release(&alloc_lst->lock);
610         atomic_add_long(&alloc_lst->size, -freed);
611         atomic_add_int(&alloc_lst->no, -no);
612         return freed;
613 }
614
615
616 /** realloc randomly size bytes.
617  * Chooses randomly a previously allocated chunk and realloc')s it.
618  * @param size - size.
619  * @param diff - filled with difference, >= 0 means more bytes were alloc.,
620  *               < 0 means bytes were freed.
621  * @return  >= 0 on success, -1 on error/ not found
622  * (empty list is a valid error reason)
623  */
624 static int mem_rnd_realloc(unsigned long size, long *diff)
625 {
626         struct mem_chunk *t;
627         int ret;
628         int target, i;
629
630         *diff = 0;
631         ret = -1;
632         lock_get(&alloc_lst->lock);
633         target = fastrand_max(atomic_get_int(&alloc_lst->no));
634         for(t = alloc_lst->chunks, i = 0; t; t = t->next, i++) {
635                 if(target == i) {
636                         *diff = (long)size - (long)t->size;
637                         if((ret = _mem_chunk_realloc_unsafe(t, size)) < 0)
638                                 *diff = 0;
639                         break;
640                 }
641         }
642         lock_release(&alloc_lst->lock);
643         atomic_add_long(&alloc_lst->size, *diff);
644         return ret;
645 }
646
647
648 #define MIN_ulong(a, b) \
649         (unsigned long)((unsigned long)(a) < (unsigned long)(b) ? (a) : (b))
650
651 /*
652  * Randomly alloc. total_size bytes, in chunks of size between
653  * min & max. max - min should be smaller then 4G.
654  * @return < 0 if there were some alloc errors, 0 on success.
655  */
656 static int mem_rnd_leak(
657                 unsigned long min, unsigned long max, unsigned long total_size)
658 {
659         unsigned long size;
660         unsigned long crt_size, crt_min;
661         long diff;
662         int err, p;
663
664         size = total_size;
665         err = 0;
666         while(size) {
667                 crt_min = MIN_ulong(min, size);
668                 crt_size = fastrand_max(MIN_ulong(max, size) - crt_min) + crt_min;
669                 p = cfg_get(misctest, mt_cfg, mem_realloc_p);
670                 if(p && ((fastrand_max(99) + 1) <= p)) {
671                         if(mem_rnd_realloc(crt_size, &diff) == 0) {
672                                 size -= diff;
673                                 continue;
674                         } /* else fallback to normal alloc. */
675                 }
676                 size -= crt_size;
677                 err += mem_leak(crt_size) < 0;
678         }
679         return -err;
680 }
681
682
683 /* test timer */
684 static ticks_t tst_timer(ticks_t ticks, struct timer_ln *tl, void *data)
685 {
686         struct rnd_time_test *tst;
687         ticks_t next_int;
688         ticks_t max_int;
689         unsigned long crt_size, crt_min, remaining;
690         long diff;
691         int p;
692
693         tst = data;
694
695         next_int = 0;
696         max_int = 0;
697
698         if(tst->total <= tst->crt) {
699                 mem_unleak(tst->crt);
700                 tst->crt = 0;
701                 tst->overfl++;
702         }
703         remaining = tst->total - tst->crt;
704         crt_min = MIN_ulong(tst->min, remaining);
705         crt_size = fastrand_max(MIN_ulong(tst->max, remaining) - crt_min) + crt_min;
706         p = cfg_get(misctest, mt_cfg, mem_realloc_p);
707         if(p && ((fastrand_max(99) + 1) <= p)) {
708                 if(mem_rnd_realloc(crt_size, &diff) == 0) {
709                         tst->crt -= diff;
710                         tst->reallocs++;
711                         goto skip_alloc;
712                 }
713         }
714         if(mem_leak(crt_size) >= 0)
715                 tst->crt += crt_size;
716         else
717                 tst->errs++;
718 skip_alloc:
719         tst->calls++;
720
721         if(TICKS_GT(tst->stop_time, ticks)) {
722                 next_int = fastrand_max(tst->max_intvrl - tst->min_intvrl)
723                                    + tst->min_intvrl;
724                 max_int = tst->stop_time - ticks;
725         } else {
726                 /* stop test */
727                 WARN("test %d time expired, stopping"
728                          " (%d s runtime, %ld calls, %d overfl, %d errors,"
729                          " crt %ld bytes)\n",
730                                 tst->id, TICKS_TO_S(ticks - tst->start_time), tst->calls,
731                                 tst->overfl, tst->errs, tst->crt);
732                 mem_unleak(tst->crt);
733                 /* tst->crt = 0 */;
734         }
735
736         /* 0 means stop stop, so if next_int == 0 => stop */
737         return MIN_unsigned(next_int, max_int);
738 }
739
740
741 /*
742  * start a malloc test of a test_time length:
743  *  - randomly between min_intvrl and max_intvrl, alloc.
744  *    a random number of bytes, between min & max.
745  *  - if total_size is reached, free everything.
746  *
747  * @returns test id (>=0) on success, -1 on error.
748  */
749 static int mem_leak_time_test(unsigned long min, unsigned long max,
750                 unsigned long total_size, ticks_t min_intvrl, ticks_t max_intvrl,
751                 ticks_t test_time)
752 {
753         struct rnd_time_test *tst;
754         struct rnd_time_test *l;
755         ticks_t first_int;
756         int id;
757
758         tst = shm_malloc(sizeof(*tst));
759         if(tst == 0)
760                 goto error;
761         memset(tst, 0, sizeof(*tst));
762         id = tst->id = atomic_add_int(&rndt_lst->last_id, 1);
763         tst->min = min;
764         tst->max = max;
765         tst->total = total_size;
766         tst->min_intvrl = min_intvrl;
767         tst->max_intvrl = max_intvrl;
768         tst->start_time = get_ticks_raw();
769         tst->stop_time = get_ticks_raw() + test_time;
770         first_int = fastrand_max(max_intvrl - min_intvrl) + min_intvrl;
771         timer_init(&tst->timer, tst_timer, tst, 0);
772         lock_get(&rndt_lst->lock);
773         tst->next = rndt_lst->tests;
774         rndt_lst->tests = tst;
775         lock_release(&rndt_lst->lock);
776         if(timer_add(&tst->timer, MIN_unsigned(first_int, test_time)) < 0)
777                 goto error;
778         return id;
779 error:
780         if(tst) {
781                 lock_get(&rndt_lst->lock);
782                 for(l = rndt_lst->tests; l; l = l->next)
783                         if(l->next == tst) {
784                                 l->next = tst->next;
785                                 break;
786                         }
787                 lock_release(&rndt_lst->lock);
788                 shm_free(tst);
789         }
790         return -1;
791 }
792
793
794 static int is_mem_test_stopped(struct rnd_time_test *tst)
795 {
796         return TICKS_LE(tst->stop_time, get_ticks_raw());
797 }
798
799 /** stops test tst.
800  * @return 0 on success, -1 on error (test already stopped)
801  */
802 static int mem_test_stop_tst(struct rnd_time_test *tst)
803 {
804         if(!is_mem_test_stopped(tst)) {
805                 if(timer_del(&tst->timer) == 0) {
806                         tst->stop_time = get_ticks_raw();
807                         return 0;
808                 }
809         }
810         return -1;
811 }
812
813
814 /** stops test id.
815  * @return 0 on success, -1 on error (not found).
816  */
817 static int mem_test_stop(int id)
818 {
819         struct rnd_time_test *tst;
820
821         lock_get(&rndt_lst->lock);
822         for(tst = rndt_lst->tests; tst; tst = tst->next)
823                 if(tst->id == id) {
824                         mem_test_stop_tst(tst);
825                         break;
826                 }
827         lock_release(&rndt_lst->lock);
828         return -(tst == 0);
829 }
830
831
832 static void mem_destroy_all_tests()
833 {
834         struct rnd_time_test *tst;
835         struct rnd_time_test *nxt;
836
837         lock_get(&rndt_lst->lock);
838         for(tst = rndt_lst->tests; tst;) {
839                 nxt = tst->next;
840                 mem_test_stop_tst(tst);
841                 shm_free(tst);
842                 tst = nxt;
843         }
844         rndt_lst->tests = 0;
845         lock_release(&rndt_lst->lock);
846 }
847
848
849 static int mem_test_destroy(int id)
850 {
851         struct rnd_time_test *tst;
852         struct rnd_time_test **crt_lnk;
853
854         lock_get(&rndt_lst->lock);
855         for(tst = 0, crt_lnk = &rndt_lst->tests; *crt_lnk;
856                         crt_lnk = &(*crt_lnk)->next)
857                 if((*crt_lnk)->id == id) {
858                         tst = *crt_lnk;
859                         mem_test_stop_tst(tst);
860                         *crt_lnk = tst->next;
861                         shm_free(tst);
862                         break;
863                 }
864         lock_release(&rndt_lst->lock);
865         return -(tst == 0);
866 }
867
868 /* script functions: */
869
870
871 static int mt_mem_alloc_f(struct sip_msg *msg, char *sz, char *foo)
872 {
873         int size;
874
875         if(sz == 0 || get_int_fparam(&size, msg, (fparam_t *)sz) < 0)
876                 return -1;
877         return mem_leak(size) >= 0 ? 1 : -1;
878 }
879
880
881 static int mt_mem_free_f(struct sip_msg *msg, char *sz, char *foo)
882 {
883         int size;
884         unsigned long freed;
885
886         size = -1;
887         if(sz != 0 && get_int_fparam(&size, msg, (fparam_t *)sz) < 0)
888                 return -1;
889         freed = mem_unleak(size);
890         return (freed == 0) ? 1 : freed;
891 }
892
893
894 /* RPC exports: */
895
896
897 /* helper functions, parses an optional b[ytes]|k|m|g to a numeric shift value
898    (e.g. b -> 0, k -> 10, ...)
899    returns bit shift value on success, -1 on error
900 */
901 static int rpc_get_size_mod(rpc_t *rpc, void *c)
902 {
903         char *m;
904
905         if(rpc->scan(c, "*s", &m) > 0) {
906                 switch(*m) {
907                         case 'b':
908                         case 'B':
909                                 return 0;
910                         case 'k':
911                         case 'K':
912                                 return 10;
913                         case 'm':
914                         case 'M':
915                                 return 20;
916                         case 'g':
917                         case 'G':
918                                 return 30;
919                         default:
920                                 rpc->fault(c, 500, "bad param use b|k|m|g");
921                                 return -1;
922                 }
923         }
924         return 0;
925 }
926
927
928 static const char *rpc_mt_alloc_doc[2] = {
929                 "Allocates the specified number of bytes (debugging/test function)."
930                 "Use b|k|m|g to specify the desired size unit",
931                 0};
932
933 static void rpc_mt_alloc(rpc_t *rpc, void *c)
934 {
935         int size;
936         int rs;
937
938         if(rpc->scan(c, "d", &size) < 1) {
939                 return;
940         }
941         rs = rpc_get_size_mod(rpc, c);
942         if(rs < 0)
943                 /* fault already generated on rpc_get_size_mod() error */
944                 return;
945         if(mem_leak((unsigned long)size << rs) < 0) {
946                 rpc->fault(c, 400, "memory allocation failed");
947         }
948         return;
949 }
950
951
952 static const char *rpc_mt_realloc_doc[2] = {
953                 "Reallocates the specified number of bytes from a pre-allocated"
954                 " randomly selected memory chunk. If no pre-allocated memory"
955                 " chunks exists, it will fail."
956                 " Make sure mt.mem_used is non 0 or call mt.mem_alloc prior to calling"
957                 " this function."
958                 " Returns the difference in bytes (<0 if bytes were freed, >0 if more"
959                 " bytes were allocated)."
960                 "Use b|k|m|g to specify the desired size unit",
961                 0};
962
963 static void rpc_mt_realloc(rpc_t *rpc, void *c)
964 {
965         int size;
966         int rs;
967         long diff;
968
969         if(rpc->scan(c, "d", &size) < 1) {
970                 return;
971         }
972         rs = rpc_get_size_mod(rpc, c);
973         if(rs < 0)
974                 /* fault already generated on rpc_get_size_mod() error */
975                 return;
976         if(mem_rnd_realloc((unsigned long)size << rs, &diff) < 0) {
977                 rpc->fault(c, 400, "memory allocation failed");
978         }
979         rpc->add(c, "d", diff >> rs);
980         return;
981 }
982
983
984 static const char *rpc_mt_free_doc[2] = {
985                 "Frees the specified number of bytes, previously allocated by one of "
986                 "the"
987                 " other misctest functions (e.g. mt.mem_alloc or the script "
988                 "mt_mem_alloc). Use b|k|m|g to specify the desired size unit."
989                 "Returns the number of bytes freed (can be higher or"
990                 " smaller then the requested size)",
991                 0};
992
993
994 static void rpc_mt_free(rpc_t *rpc, void *c)
995 {
996         int size;
997         int rs;
998
999         size = -1;
1000         rs = 0;
1001         if(rpc->scan(c, "*d", &size) > 0) {
1002                 /* found size, look if a size modifier is present */
1003                 rs = rpc_get_size_mod(rpc, c);
1004                 if(rs < 0)
1005                         /* fault already generated on rpc_get_size_mod() error */
1006                         return;
1007         }
1008         rpc->add(c, "d", (int)(mem_unleak((unsigned long)size << rs) >> rs));
1009         return;
1010 }
1011
1012
1013 static const char *rpc_mt_used_doc[2] = {
1014                 "Returns how many memory chunks and how many bytes are currently"
1015                 " allocated via the mem_alloc module functions."
1016                 " Use b|k|m|g to specify the desired size unit.",
1017                 0};
1018
1019
1020 static void rpc_mt_used(rpc_t *rpc, void *c)
1021 {
1022         int rs;
1023
1024         rs = 0;
1025         rs = rpc_get_size_mod(rpc, c);
1026         if(rs < 0)
1027                 /* fault already generated on rpc_get_size_mod() error */
1028                 return;
1029         rpc->add(c, "d", atomic_get_int(&alloc_lst->no));
1030         rpc->add(c, "d", (int)(atomic_get_long(&alloc_lst->size) >> rs));
1031         return;
1032 }
1033
1034
1035 static const char *rpc_mt_rnd_alloc_doc[2] = {
1036                 "Takes 4 parameters: min, max, total_size and an optional unit "
1037                 "(b|k|m|g)."
1038                 " It will allocate total_size memory, in pieces of random size between"
1039                 "min .. max (inclusive).",
1040                 0};
1041
1042
1043 static void rpc_mt_rnd_alloc(rpc_t *rpc, void *c)
1044 {
1045         int min, max, total_size;
1046         int rs;
1047         int err;
1048
1049         if(rpc->scan(c, "ddd", &min, &max, &total_size) < 3) {
1050                 return;
1051         }
1052         rs = rpc_get_size_mod(rpc, c);
1053         if(rs < 0)
1054                 /* fault already generated on rpc_get_size_mod() error */
1055                 return;
1056         if(min > max || min < 0 || max > total_size) {
1057                 rpc->fault(c, 400, "invalid parameter values");
1058                 return;
1059         }
1060         if((err = mem_rnd_leak((unsigned long)min << rs, (unsigned long)max << rs,
1061                                 (unsigned long)total_size << rs))
1062                         < 0) {
1063                 rpc->fault(c, 400, "memory allocation failed (%d errors)", -err);
1064         }
1065         return;
1066 }
1067
1068
1069 static const char *rpc_mt_test_start_doc[2] = {
1070                 "Takes 7 parameters: min, max, total_size, min_interval, max_interval, "
1071                 "test_time and an optional size unit (b|k|m|g). All the time units are "
1072                 "ms."
1073                 " It will run a memory allocation test for test_time ms. At a random"
1074                 " interval between min_interval and max_interval ms. it will allocate a"
1075                 " memory chunk with random size, between min and max. Each time "
1076                 "total_size"
1077                 " is reached, it will free all the memory allocated and start again."
1078                 "Returns the test id (integer)",
1079                 0};
1080
1081
1082 static void rpc_mt_test_start(rpc_t *rpc, void *c)
1083 {
1084         int min, max, total_size;
1085         int min_intvrl, max_intvrl, total_time;
1086         int rs;
1087         int id;
1088
1089         if(rpc->scan(c, "dddddd", &min, &max, &total_size, &min_intvrl, &max_intvrl,
1090                            &total_time)
1091                         < 6) {
1092                 return;
1093         }
1094         rs = rpc_get_size_mod(rpc, c);
1095         if(rs < 0)
1096                 /* fault already generated on rpc_get_size_mod() error */
1097                 return;
1098         if(min > max || min < 0 || max > total_size) {
1099                 rpc->fault(c, 400, "invalid size parameters values");
1100                 return;
1101         }
1102         if(min_intvrl > max_intvrl || min_intvrl <= 0 || max_intvrl > total_time) {
1103                 rpc->fault(c, 400, "invalid time intervals values");
1104                 return;
1105         }
1106         if((id = mem_leak_time_test((unsigned long)min << rs,
1107                                 (unsigned long)max << rs, (unsigned long)total_size << rs,
1108                                 MS_TO_TICKS(min_intvrl), MS_TO_TICKS(max_intvrl),
1109                                 MS_TO_TICKS(total_time)))
1110                         < 0) {
1111                 rpc->fault(c, 400, "memory allocation failed");
1112         } else {
1113                 rpc->add(c, "d", id);
1114         }
1115         return;
1116 }
1117
1118
1119 static const char *rpc_mt_test_stop_doc[2] = {
1120                 "Takes 1 parameter: the test id. It will stop the corresponding test."
1121                 "Note: the test is stopped, but not destroyed.",
1122                 0};
1123
1124
1125 static void rpc_mt_test_stop(rpc_t *rpc, void *c)
1126 {
1127         int id;
1128
1129         if(rpc->scan(c, "d", &id) < 1) {
1130                 return;
1131         }
1132         if(mem_test_stop(id) < 0) {
1133                 rpc->fault(c, 400, "test %d not found", id);
1134         }
1135         return;
1136 }
1137
1138
1139 static const char *rpc_mt_test_destroy_doc[2] = {
1140                 "Takes 1 parameter: the test id. It will destroy the corresponding "
1141                 "test.",
1142                 0};
1143
1144
1145 static void rpc_mt_test_destroy(rpc_t *rpc, void *c)
1146 {
1147         int id;
1148
1149         if(rpc->scan(c, "*d", &id) > 0 && id != -1) {
1150                 if(mem_test_destroy(id) < 0)
1151                         rpc->fault(c, 400, "test %d not found", id);
1152         } else {
1153                 mem_destroy_all_tests();
1154         }
1155         return;
1156 }
1157
1158
1159 static const char *rpc_mt_test_destroy_all_doc[2] = {
1160                 "It will destroy all the tests (running or stopped).", 0};
1161
1162
1163 static void rpc_mt_test_destroy_all(rpc_t *rpc, void *c)
1164 {
1165         mem_destroy_all_tests();
1166         return;
1167 }
1168
1169
1170 static const char *rpc_mt_test_list_doc[2] = {
1171                 "If a test id parameter is provided it will list the corresponding "
1172                 "test,"
1173                 " else it will list all of them. Use b |k | m | g as a second parameter"
1174                 " for the size units (default bytes)",
1175                 0};
1176
1177
1178 static void rpc_mt_test_list(rpc_t *rpc, void *c)
1179 {
1180         int id, rs;
1181         struct rnd_time_test *tst;
1182         void *h;
1183
1184         rs = 0;
1185         if(rpc->scan(c, "*d", &id) < 1) {
1186                 id = -1;
1187         } else {
1188                 rs = rpc_get_size_mod(rpc, c);
1189                 if(rs < 0)
1190                         return;
1191         }
1192         lock_get(&rndt_lst->lock);
1193         for(tst = rndt_lst->tests; tst; tst = tst->next)
1194                 if(tst->id == id || id == -1) {
1195                         rpc->add(c, "{", &h);
1196                         rpc->struct_add(h, "ddddddddddd", "ID           ", tst->id,
1197                                         "run time (s) ",
1198                                         (int)TICKS_TO_S((TICKS_LE(tst->stop_time, get_ticks_raw())
1199                                                                                                         ? tst->stop_time
1200                                                                                                         : get_ticks_raw())
1201                                                                         - tst->start_time),
1202                                         "remaining (s)",
1203                                         TICKS_LE(tst->stop_time, get_ticks_raw())
1204                                                         ? 0
1205                                                         : (int)TICKS_TO_S(tst->stop_time - get_ticks_raw()),
1206                                         "total calls  ", (int)tst->calls, "reallocs     ",
1207                                         (int)tst->reallocs, "errors       ", (int)tst->errs,
1208                                         "overflows    ", (int)tst->overfl, "total alloc  ",
1209                                         (int)((tst->crt + tst->overfl * tst->total) >> rs),
1210                                         "min          ", (int)(tst->min >> rs), "max          ",
1211                                         (int)(tst->max >> rs), "total        ",
1212                                         (int)(tst->total >> rs));
1213                         if(id != -1)
1214                                 break;
1215                 }
1216         lock_release(&rndt_lst->lock);
1217
1218         return;
1219 }
1220
1221
1222 /* clang-format off */
1223 static rpc_export_t mt_rpc[] = {
1224         {"mt.mem_alloc", rpc_mt_alloc, rpc_mt_alloc_doc, 0},
1225         {"mt.mem_free", rpc_mt_free, rpc_mt_free_doc, 0},
1226         {"mt.mem_realloc", rpc_mt_realloc, rpc_mt_realloc_doc, 0},
1227         {"mt.mem_used", rpc_mt_used, rpc_mt_used_doc, 0},
1228         {"mt.mem_rnd_alloc", rpc_mt_rnd_alloc, rpc_mt_rnd_alloc_doc, 0},
1229         {"mt.mem_test_start", rpc_mt_test_start, rpc_mt_test_start_doc, 0},
1230         {"mt.mem_test_stop", rpc_mt_test_stop, rpc_mt_test_stop_doc, 0},
1231         {"mt.mem_test_destroy", rpc_mt_test_destroy, rpc_mt_test_destroy_doc, 0},
1232         {"mt.mem_test_destroy_all", rpc_mt_test_destroy_all,
1233                                                                 rpc_mt_test_destroy_all_doc, 0},
1234         {"mt.mem_test_list", rpc_mt_test_list, rpc_mt_test_list_doc, 0},
1235         {0, 0, 0, 0}
1236 };
1237 /* clang-format on */