debug malloc to track mem leaks
[protos/libecoli.git] / lib / ecoli_test.c
1 /*
2  * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
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.
15  *
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.
26  */
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #include <ecoli_malloc.h>
33 #include <ecoli_test.h>
34 #include <ecoli_tk.h>
35
36 static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list);
37
38 /* register a driver */
39 void ec_test_register(struct ec_test *test)
40 {
41         TAILQ_INSERT_TAIL(&test_list, test, next);
42 }
43
44 int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input,
45         const char *expected)
46 {
47         struct ec_parsed_tk *p;
48         const char *s;
49         int ret = -1;
50
51         p = ec_tk_parse(tk, input);
52         s = ec_parsed_tk_to_string(p);
53         if (s == NULL && expected == NULL)
54                 ret = 0;
55         else if (s != NULL && expected != NULL &&
56                 !strcmp(s, expected))
57                 ret = 0;
58
59         if (expected == NULL && ret != 0)
60                 printf("tk should not match but matches <%s>\n", s);
61         if (expected != NULL && ret != 0)
62                 printf("tk should match <%s> but matches <%s>\n",
63                         expected, s);
64
65         ec_parsed_tk_free(p);
66
67         return ret;
68 }
69
70 int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
71         const char *expected)
72 {
73         struct ec_completed_tk *p;
74         const char *s;
75         int ret = -1;
76
77         p = ec_tk_complete(tk, input);
78         s = ec_completed_tk_smallest_start(p);
79         if (s == NULL && expected == NULL)
80                 ret = 0;
81         else if (s != NULL && expected != NULL &&
82                 !strcmp(s, expected))
83                 ret = 0;
84
85         if (expected == NULL && ret != 0)
86                 printf("tk should not complete but completes with <%s>\n", s);
87         if (expected != NULL && ret != 0)
88                 printf("tk should complete with <%s> but completes with <%s>\n",
89                         expected, s);
90
91         ec_completed_tk_free(p);
92
93         return ret;
94 }
95
96 TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr);
97 static struct debug_alloc_hdr_list debug_alloc_hdr_list =
98         TAILQ_HEAD_INITIALIZER(debug_alloc_hdr_list);
99
100 struct debug_alloc_hdr {
101         TAILQ_ENTRY(debug_alloc_hdr) next;
102         const char *file;
103         unsigned int line;
104         size_t size;
105         unsigned int cookie;
106 };
107
108 static void *debug_malloc(size_t size, const char *file, unsigned int line)
109 {
110         struct debug_alloc_hdr *hdr;
111         size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int);
112         void *ret;
113
114         hdr = malloc(new_size);
115         if (hdr == NULL) {
116                 ret = NULL;
117         } else {
118                 hdr->file = file;
119                 hdr->line = line;
120                 hdr->size = size;
121                 hdr->cookie = 0x12345678;
122                 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
123                 ret = hdr + 1;
124         }
125
126         printf("%s:%d: info: malloc(%zd) -> %p\n", file, line, size, ret);
127
128         return ret;
129 }
130
131 static void debug_free(void *ptr, const char *file, unsigned int line)
132 {
133         struct debug_alloc_hdr *hdr, *h;
134
135         (void)file;
136         (void)line;
137
138         printf("%s:%d: info: free(%p)\n", file, line, ptr);
139
140         if (ptr == NULL)
141                 return;
142
143         hdr = (ptr - sizeof(*hdr));
144         if (hdr->cookie != 0x12345678) {
145                 printf("%s:%d: error: free(%p): bad start cookie\n",
146                         file, line, ptr);
147                 abort();
148         }
149
150         TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
151                 if (h == hdr)
152                         break;
153         }
154
155         if (h == NULL) {
156                 printf("%s:%d: error: free(%p): bad ptr\n",
157                         file, line, ptr);
158                 abort();
159         }
160
161         TAILQ_REMOVE(&debug_alloc_hdr_list, hdr, next);
162         free(hdr);
163 }
164
165 void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
166 {
167         struct debug_alloc_hdr *hdr = (ptr - sizeof(*hdr));
168         struct debug_alloc_hdr *h;
169         size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int);
170         void *ret;
171
172         if (ptr != NULL) {
173                 if (hdr->cookie != 0x12345678) {
174                         printf("%s:%d: error: realloc(%p): bad start cookie\n",
175                                 file, line, ptr);
176                         abort();
177                 }
178
179                 TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
180                         if (h == hdr)
181                                 break;
182                 }
183
184                 if (h == NULL) {
185                         printf("%s:%d: error: realloc(%p): bad ptr\n",
186                                 file, line, ptr);
187                         abort();
188                 }
189
190                 TAILQ_REMOVE(&debug_alloc_hdr_list, h, next);
191                 hdr = realloc(hdr, new_size);
192                 if (hdr == NULL) {
193                         TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, h, next);
194                         ret = NULL;
195                 } else {
196                         ret = hdr + 1;
197                 }
198         } else {
199                 hdr = realloc(NULL, new_size);
200                 if (hdr == NULL)
201                         ret = NULL;
202                 else
203                         ret= hdr + 1;
204         }
205
206         if (hdr != NULL) {
207                 hdr->file = file;
208                 hdr->line = line;
209                 hdr->size = size;
210                 hdr->cookie = 0x12345678;
211                 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
212         }
213
214         printf("%s:%d: info: realloc(%p, %zd) -> %p\n",
215                 file, line, ptr, size, ret);
216
217         return ret;
218 }
219
220 void debug_alloc_dump(void)
221 {
222         struct debug_alloc_hdr *hdr;
223
224         TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) {
225                 printf("%s:%d: error: memory leak size=%zd ptr=%p\n",
226                         hdr->file, hdr->line, hdr->size, hdr + 1);
227         }
228 }
229
230 /* int ec_test_check_tk_complete_list(const struct ec_tk *tk, */
231 /*      const char *input, ...) */
232
233 int ec_test_all(void)
234 {
235         struct ec_test *test;
236         int ret = 0;
237
238         TAILQ_INIT(&debug_alloc_hdr_list);
239
240         /* register a new malloc to trac memleaks */
241         if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
242                 printf("cannot register new malloc\n");
243                 return -1;
244         }
245
246         TAILQ_FOREACH(test, &test_list, next) {
247                 if (test->test() == 0) {
248                         printf("== test %-20s success\n", test->name);
249                 } else {
250                         printf("== test %-20s failed\n", test->name);
251                         ret = -1;
252                 }
253         }
254
255         ec_malloc_unregister();
256         debug_alloc_dump();
257
258         return ret;
259 }