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