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.
36 #include <ecoli_init.h>
37 #include <ecoli_log.h>
38 #include <ecoli_test.h>
39 #include <ecoli_malloc.h>
41 EC_LOG_TYPE_REGISTER(main);
43 #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / \
44 ((size_t)(!(sizeof(x) % sizeof(0[x])))))
46 static int log_level = EC_LOG_INFO;
47 static int alloc_fail_proba = 0;
49 static size_t alloc_success = 0;
51 static const char ec_short_options[] =
54 "r:" /* random-alloc-fail */
58 #define EC_OPT_HELP "help"
59 #define EC_OPT_LOG_LEVEL "log-level"
60 #define EC_OPT_RANDOM_ALLOC_FAIL "random-alloc-fail"
61 #define EC_OPT_SEED "seed"
63 static const struct option ec_long_options[] = {
64 {EC_OPT_HELP, 1, NULL, 'h'},
65 {EC_OPT_LOG_LEVEL, 1, NULL, 'l'},
66 {EC_OPT_RANDOM_ALLOC_FAIL, 1, NULL, 'r'},
67 {EC_OPT_SEED, 1, NULL, 's'},
71 static void usage(const char *prgname)
73 printf("%s [options] [test1 test2 test3...]\n"
78 " --"EC_OPT_LOG_LEVEL"=<level>\n"
79 " Set log level (0 = no log, 7 = verbose).\n"
81 " --"EC_OPT_RANDOM_ALLOC_FAIL"=<probability>\n"
82 " Cause malloc to fail randomly. This helps to debug\n"
83 " leaks or crashes in error cases. The probability is\n"
84 " between 0 and 100.\n"
87 " Seeds the random number generator. Default is 0.\n"
92 parse_int(const char *s, int min, int max, int *ret, unsigned int base)
97 n = strtoll(s, &end, base);
98 if ((s[0] == '\0') || (end == NULL) || (*end != '\0'))
109 static int parse_args(int argc, char **argv)
113 while ((opt = getopt_long(argc, argv, ec_short_options,
114 ec_long_options, NULL)) != EOF) {
121 case 'l': /* log-level */
122 if (parse_int(optarg, EC_LOG_EMERG,
123 EC_LOG_DEBUG, &log_level, 10) < 0) {
124 printf("Invalid log value\n");
130 case 'r': /* random-alloc-fail */
131 if (parse_int(optarg, 0, 100, &alloc_fail_proba,
133 printf("Invalid probability value\n");
140 if (parse_int(optarg, 0, INT_MAX, &seed, 10) < 0) {
141 printf("Invalid seed value\n");
159 TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr);
160 static struct debug_alloc_hdr_list debug_alloc_hdr_list =
161 TAILQ_HEAD_INITIALIZER(debug_alloc_hdr_list);
164 struct debug_alloc_hdr {
165 TAILQ_ENTRY(debug_alloc_hdr) next;
169 void *stack[STACK_SZ];
174 struct debug_alloc_ftr {
176 } __attribute__((packed));
178 static void *debug_malloc(size_t size, const char *file, unsigned int line)
180 struct debug_alloc_hdr *hdr;
181 struct debug_alloc_ftr *ftr;
182 size_t new_size = size + sizeof(*hdr) + sizeof(*ftr);
186 if (alloc_fail_proba != 0 && (r % 100) < alloc_fail_proba)
189 hdr = malloc(new_size);
197 hdr->stacklen = backtrace(hdr->stack, COUNT_OF(hdr->stack));
198 hdr->cookie = 0x12345678;
199 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
201 ftr = (struct debug_alloc_ftr *)(
202 (char *)hdr + size + sizeof(*hdr));
203 ftr->cookie = 0x87654321;
206 EC_LOG(EC_LOG_DEBUG, "%s:%d: info: malloc(%zd) -> %p [rand=%d]\n",
207 file, line, size, ret, r);
216 static void debug_free(void *ptr, const char *file, unsigned int line)
218 struct debug_alloc_hdr *hdr, *h;
219 struct debug_alloc_ftr *ftr;
224 EC_LOG(EC_LOG_DEBUG, "%s:%d: info: free(%p)\n", file, line, ptr);
229 hdr = (ptr - sizeof(*hdr));
230 if (hdr->cookie != 0x12345678) {
231 EC_LOG(EC_LOG_ERR, "%s:%d: error: free(%p): bad start cookie\n",
236 ftr = (ptr + hdr->size);
237 if (ftr->cookie != 0x87654321) {
238 EC_LOG(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n",
243 TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
249 EC_LOG(EC_LOG_ERR, "%s:%d: error: free(%p): bad ptr\n",
254 TAILQ_REMOVE(&debug_alloc_hdr_list, hdr, next);
258 static void *debug_realloc(void *ptr, size_t size, const char *file,
261 struct debug_alloc_hdr *hdr, *h;
262 struct debug_alloc_ftr *ftr;
263 size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int);
267 hdr = (ptr - sizeof(*hdr));
268 if (hdr->cookie != 0x12345678) {
270 "%s:%d: error: realloc(%p): bad start cookie\n",
275 ftr = (ptr + hdr->size);
276 if (ftr->cookie != 0x87654321) {
278 "%s:%d: error: realloc(%p): bad end cookie\n",
283 TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
289 EC_LOG(EC_LOG_ERR, "%s:%d: error: realloc(%p): bad ptr\n",
294 TAILQ_REMOVE(&debug_alloc_hdr_list, h, next);
295 hdr = realloc(hdr, new_size);
297 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, h, next);
303 hdr = realloc(NULL, new_size);
314 hdr->stacklen = backtrace(hdr->stack, COUNT_OF(hdr->stack));
315 hdr->cookie = 0x12345678;
316 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
317 ftr = (struct debug_alloc_ftr *)(
318 (char *)hdr + size + sizeof(*hdr));
319 ftr->cookie = 0x87654321;
322 EC_LOG(EC_LOG_DEBUG, "%s:%d: info: realloc(%p, %zd) -> %p\n",
323 file, line, ptr, size, ret);
330 static int debug_alloc_dump_leaks(void)
332 struct debug_alloc_hdr *hdr;
336 EC_LOG(EC_LOG_INFO, "%zd successful allocations\n", alloc_success);
338 if (TAILQ_EMPTY(&debug_alloc_hdr_list))
341 TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) {
343 "%s:%d: error: memory leak size=%zd ptr=%p\n",
344 hdr->file, hdr->line, hdr->size, hdr + 1);
345 buffer = backtrace_symbols(hdr->stack, hdr->stacklen);
346 if (buffer == NULL) {
347 for (i = 0; i < hdr->stacklen; i++)
348 EC_LOG(EC_LOG_ERR, " %p\n", hdr->stack[i]);
350 for (i = 0; i < hdr->stacklen; i++)
351 EC_LOG(EC_LOG_ERR, " %s\n",
352 buffer ? buffer[i] : "unknown");
358 " missing static syms, use: addr2line -f -e <prog> <addr>\n");
363 static int debug_log(int type, unsigned int level, void *opaque,
369 if (level > (unsigned int)log_level)
372 if (printf("%s", str) < 0)
378 int main(int argc, char **argv)
380 int i, ret = 0, leaks;
382 ret = parse_args(argc, argv);
391 /* register a new malloc to track memleaks */
392 TAILQ_INIT(&debug_alloc_hdr_list);
393 if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
394 EC_LOG(EC_LOG_ERR, "cannot register new malloc\n");
399 fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno));
402 ec_log_fct_register(debug_log, NULL);
408 for (i = 1; i < argc; i++)
409 ret |= ec_test_one(argv[i]);
412 leaks = debug_alloc_dump_leaks();
414 if (alloc_fail_proba == 0 && ret != 0) {
415 printf("tests failed\n");
417 } else if (alloc_fail_proba != 0 && leaks != 0) {
418 printf("tests failed (memory leak)\n");
422 printf("\ntests ok\n");