2 * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the University of California, Berkeley nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include <ecoli_log.h>
36 #include <ecoli_test.h>
37 #include <ecoli_malloc.h>
39 EC_LOG_TYPE_REGISTER(main);
41 #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / \
42 ((size_t)(!(sizeof(x) % sizeof(0[x])))))
44 static int log_level = EC_LOG_INFO;
45 static int alloc_fail_proba = 0;
47 static size_t alloc_success = 0;
49 static const char ec_short_options[] =
52 "r:" /* random-alloc-fail */
56 #define EC_OPT_HELP "help"
57 #define EC_OPT_LOG_LEVEL "log-level"
58 #define EC_OPT_RANDOM_ALLOC_FAIL "random-alloc-fail"
59 #define EC_OPT_SEED "seed"
61 static const struct option ec_long_options[] = {
62 {EC_OPT_HELP, 1, NULL, 'h'},
63 {EC_OPT_LOG_LEVEL, 1, NULL, 'l'},
64 {EC_OPT_RANDOM_ALLOC_FAIL, 1, NULL, 'r'},
65 {EC_OPT_SEED, 1, NULL, 's'},
69 static void usage(const char *prgname)
71 printf("%s [options] [test1 test2 test3...]\n"
76 " --"EC_OPT_LOG_LEVEL"=<level>\n"
77 " Set log level (0 = no log, 7 = verbose).\n"
79 " --"EC_OPT_RANDOM_ALLOC_FAIL"=<probability>\n"
80 " Cause malloc to fail randomly. This helps to debug\n"
81 " leaks or crashes in error cases. The probability is\n"
82 " between 0 and 100.\n"
85 " Seeds the random number generator. Default is 0.\n"
90 parse_int(const char *s, int min, int max, int *ret, unsigned int base)
95 n = strtoll(s, &end, base);
96 if ((s[0] == '\0') || (end == NULL) || (*end != '\0'))
107 static int parse_args(int argc, char **argv)
111 while ((opt = getopt_long(argc, argv, ec_short_options,
112 ec_long_options, NULL)) != EOF) {
119 case 'l': /* log-level */
120 if (parse_int(optarg, EC_LOG_EMERG,
121 EC_LOG_DEBUG, &log_level, 10) < 0) {
122 printf("Invalid log value\n");
128 case 'r': /* random-alloc-fail */
129 if (parse_int(optarg, 0, 100, &alloc_fail_proba,
131 printf("Invalid probability value\n");
138 if (parse_int(optarg, 0, INT_MAX, &seed, 10) < 0) {
139 printf("Invalid seed value\n");
157 TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr);
158 static struct debug_alloc_hdr_list debug_alloc_hdr_list =
159 TAILQ_HEAD_INITIALIZER(debug_alloc_hdr_list);
162 struct debug_alloc_hdr {
163 TAILQ_ENTRY(debug_alloc_hdr) next;
167 void *stack[STACK_SZ];
172 struct debug_alloc_ftr {
174 } __attribute__((packed));
176 static void *debug_malloc(size_t size, const char *file, unsigned int line)
178 struct debug_alloc_hdr *hdr;
179 struct debug_alloc_ftr *ftr;
180 size_t new_size = size + sizeof(*hdr) + sizeof(*ftr);
184 if (alloc_fail_proba != 0 && (random() % 100) < alloc_fail_proba)
187 hdr = malloc(new_size);
195 hdr->stacklen = backtrace(hdr->stack, COUNT_OF(hdr->stack));
196 hdr->cookie = 0x12345678;
197 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
199 ftr = (struct debug_alloc_ftr *)(
200 (char *)hdr + size + sizeof(*hdr));
201 ftr->cookie = 0x87654321;
204 EC_LOG(EC_LOG_DEBUG, "%s:%d: info: malloc(%zd) -> %p\n",
205 file, line, size, ret);
212 static void debug_free(void *ptr, const char *file, unsigned int line)
214 struct debug_alloc_hdr *hdr, *h;
215 struct debug_alloc_ftr *ftr;
220 EC_LOG(EC_LOG_DEBUG, "%s:%d: info: free(%p)\n", file, line, ptr);
225 hdr = (ptr - sizeof(*hdr));
226 if (hdr->cookie != 0x12345678) {
227 EC_LOG(EC_LOG_ERR, "%s:%d: error: free(%p): bad start cookie\n",
232 ftr = (ptr + hdr->size);
233 if (ftr->cookie != 0x87654321) {
234 EC_LOG(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n",
239 TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
245 EC_LOG(EC_LOG_ERR, "%s:%d: error: free(%p): bad ptr\n",
250 TAILQ_REMOVE(&debug_alloc_hdr_list, hdr, next);
254 static void *debug_realloc(void *ptr, size_t size, const char *file,
257 struct debug_alloc_hdr *hdr, *h;
258 struct debug_alloc_ftr *ftr;
259 size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int);
263 hdr = (ptr - sizeof(*hdr));
264 if (hdr->cookie != 0x12345678) {
266 "%s:%d: error: realloc(%p): bad start cookie\n",
271 ftr = (ptr + hdr->size);
272 if (ftr->cookie != 0x87654321) {
274 "%s:%d: error: realloc(%p): bad end cookie\n",
279 TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
285 EC_LOG(EC_LOG_ERR, "%s:%d: error: realloc(%p): bad ptr\n",
290 TAILQ_REMOVE(&debug_alloc_hdr_list, h, next);
291 hdr = realloc(hdr, new_size);
293 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, h, next);
299 hdr = realloc(NULL, new_size);
310 hdr->stacklen = backtrace(hdr->stack, COUNT_OF(hdr->stack));
311 hdr->cookie = 0x12345678;
312 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
313 ftr = (struct debug_alloc_ftr *)(
314 (char *)hdr + size + sizeof(*hdr));
315 ftr->cookie = 0x87654321;
318 EC_LOG(EC_LOG_DEBUG, "%s:%d: info: realloc(%p, %zd) -> %p\n",
319 file, line, ptr, size, ret);
326 static int debug_alloc_dump_leaks(void)
328 struct debug_alloc_hdr *hdr;
332 EC_LOG(EC_LOG_INFO, "%zd successful allocations\n", alloc_success);
334 if (TAILQ_EMPTY(&debug_alloc_hdr_list))
337 TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) {
339 "%s:%d: error: memory leak size=%zd ptr=%p\n",
340 hdr->file, hdr->line, hdr->size, hdr + 1);
341 buffer = backtrace_symbols(hdr->stack, hdr->stacklen);
342 if (buffer == NULL) {
343 for (i = 0; i < hdr->stacklen; i++)
344 EC_LOG(EC_LOG_ERR, " %p\n", hdr->stack[i]);
346 for (i = 0; i < hdr->stacklen; i++)
347 EC_LOG(EC_LOG_ERR, " %s\n",
348 buffer ? buffer[i] : "unknown");
354 " missing static syms, use: addr2line -f -e <prog> <addr>\n");
359 static int debug_log(int type, unsigned int level, void *opaque,
365 if (level > (unsigned int)log_level)
368 return printf("%s", str);
371 int main(int argc, char **argv)
373 int i, ret = 0, leaks;
375 ret = parse_args(argc, argv);
384 if (0) ec_log_fct_register(debug_log, NULL);
386 /* register a new malloc to track memleaks */
387 TAILQ_INIT(&debug_alloc_hdr_list);
388 if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
389 EC_LOG(EC_LOG_ERR, "cannot register new malloc\n");
397 for (i = 1; i < argc; i++)
398 ret |= ec_test_one(argv[i]);
401 ec_malloc_unregister();
402 leaks = debug_alloc_dump_leaks();
404 if (alloc_fail_proba == 0 && ret != 0) {
405 printf("tests failed\n");
407 } else if (alloc_fail_proba != 0 && leaks != 0) {
408 printf("tests failed (memory leak)\n");
412 printf("\ntests ok\n");