]> git.cameronkatri.com Git - mandoc.git/blob - mandoc_dbg.c
At the end of every tbl(7) cell, clear the \z state.
[mandoc.git] / mandoc_dbg.c
1 /* $Id: mandoc_dbg.c,v 1.1 2022/04/14 16:43:44 schwarze Exp $ */
2 /*
3 * Copyright (c) 2021, 2022 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include "config.h"
18
19 #include <sys/types.h>
20
21 #if HAVE_ERR
22 #include <err.h>
23 #endif
24 #include <stdarg.h>
25 #include <stddef.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #if HAVE_OHASH
33 #include <ohash.h>
34 #else
35 #include "compat_ohash.h"
36 #endif
37
38 #define DEBUG_NODEF 1
39 #include "mandoc_aux.h"
40 #include "mandoc.h"
41
42 /* Store information about one allocation. */
43 struct dhash_entry {
44 const char *file;
45 int line;
46 const char *func;
47 size_t num;
48 size_t size;
49 void *ptr;
50 };
51
52 /* Store information about all allocations. */
53 static struct ohash dhash_table;
54 static FILE *dhash_fp;
55 static int dhash_aflag;
56 static int dhash_fflag;
57 static int dhash_lflag;
58 static int dhash_nflag;
59 static int dhash_sflag;
60
61 static void *dhash_alloc(size_t, void *);
62 static void *dhash_calloc(size_t, size_t, void *);
63 static void dhash_free(void *, void *);
64 static unsigned int dhash_slot(void *);
65 static void dhash_register(const char *, int, const char *,
66 size_t, size_t, void *, const char *);
67 static void dhash_print(struct dhash_entry *);
68 static void dhash_purge(const char *, int, const char *, void *);
69
70
71 /* *** Debugging wrappers of public API functions. ************************ */
72
73 int
74 mandoc_dbg_asprintf(const char *file, int line,
75 char **dest, const char *fmt, ...)
76 {
77 va_list ap;
78 int ret;
79
80 va_start(ap, fmt);
81 ret = vasprintf(dest, fmt, ap);
82 va_end(ap);
83
84 if (ret == -1)
85 err((int)MANDOCLEVEL_SYSERR, NULL);
86
87 dhash_register(file, line, "asprintf", 1, strlen(*dest) + 1,
88 *dest, *dest);
89
90 return ret;
91 }
92
93 void *
94 mandoc_dbg_calloc(size_t num, size_t size, const char *file, int line)
95 {
96 void *ptr = mandoc_calloc(num, size);
97 dhash_register(file, line, "calloc", num, size, ptr, NULL);
98 return ptr;
99 }
100
101 void *
102 mandoc_dbg_malloc(size_t size, const char *file, int line)
103 {
104 void *ptr = mandoc_malloc(size);
105 dhash_register(file, line, "malloc", 1, size, ptr, NULL);
106 return ptr;
107 }
108
109 void *
110 mandoc_dbg_realloc(void *ptr, size_t size, const char *file, int line)
111 {
112 dhash_purge(file, line, "realloc", ptr);
113 ptr = mandoc_realloc(ptr, size);
114 dhash_register(file, line, "realloc", 1, size, ptr, NULL);
115 return ptr;
116 }
117
118 void *
119 mandoc_dbg_reallocarray(void *ptr, size_t num, size_t size,
120 const char *file, int line)
121 {
122 dhash_purge(file, line, "reallocarray", ptr);
123 ptr = mandoc_reallocarray(ptr, num, size);
124 dhash_register(file, line, "reallocarray", num, size, ptr, NULL);
125 return ptr;
126 }
127
128 void *
129 mandoc_dbg_recallocarray(void *ptr, size_t oldnum, size_t num, size_t size,
130 const char *file, int line)
131 {
132 dhash_purge(file, line, "recallocarray", ptr);
133 ptr = mandoc_recallocarray(ptr, oldnum, num, size);
134 dhash_register(file, line, "recallocarray", num, size, ptr, NULL);
135 return ptr;
136 }
137
138 char *
139 mandoc_dbg_strdup(const char *ptr, const char *file, int line)
140 {
141 char *p = mandoc_strdup(ptr);
142 dhash_register(file, line, "strdup", 1, strlen(p) + 1, p, ptr);
143 return p;
144 }
145
146 char *
147 mandoc_dbg_strndup(const char *ptr, size_t sz, const char *file, int line)
148 {
149 char *p = mandoc_strndup(ptr, sz);
150 dhash_register(file, line, "strndup", 1, strlen(p) + 1, p, NULL);
151 return p;
152 }
153
154 void
155 mandoc_dbg_free(void *ptr, const char *file, int line)
156 {
157 dhash_purge(file, line, "free", ptr);
158 free(ptr);
159 }
160
161
162 /* *** Memory allocation callbacks for the debugging table. *************** */
163
164 static void *
165 dhash_alloc(size_t sz, void *arg)
166 {
167 return malloc(sz);
168 }
169
170 static void *
171 dhash_calloc(size_t n, size_t sz, void *arg)
172 {
173 return calloc(n, sz);
174 }
175
176 static void
177 dhash_free(void *p, void *arg)
178 {
179 free(p);
180 }
181
182
183 /* *** Debugging utility functions. *************************************** */
184
185 /* Initialize the debugging table, to be called from the top of main(). */
186 void
187 mandoc_dbg_init(int argc, char *argv[])
188 {
189 struct ohash_info info;
190 char *dhash_fn;
191 int argi;
192
193 info.alloc = dhash_alloc;
194 info.calloc = dhash_calloc;
195 info.free = dhash_free;
196 info.data = NULL;
197 info.key_offset = offsetof(struct dhash_entry, ptr);
198 ohash_init(&dhash_table, 18, &info);
199
200 dhash_fp = stderr;
201 if ((dhash_fn = getenv("DEBUG_MEMORY")) == NULL)
202 return;
203
204 dhash_sflag = 1;
205 for(;; dhash_fn++) {
206 switch (*dhash_fn) {
207 case '\0':
208 break;
209 case 'A':
210 dhash_aflag = 1;
211 continue;
212 case 'F':
213 dhash_fflag = 1;
214 continue;
215 case 'L':
216 dhash_lflag = 1;
217 continue;
218 case 'N':
219 dhash_nflag = 1;
220 continue;
221 case '/':
222 if ((dhash_fp = fopen(dhash_fn, "a+e")) == NULL)
223 err((int)MANDOCLEVEL_SYSERR, "%s", dhash_fn);
224 break;
225 default:
226 errx((int)MANDOCLEVEL_BADARG,
227 "invalid char '%c' in $DEBUG_MEMORY",
228 *dhash_fn);
229 }
230 break;
231 }
232 if (setvbuf(dhash_fp, NULL, _IOLBF, 0) != 0)
233 err((int)MANDOCLEVEL_SYSERR, "setvbuf");
234
235 fprintf(dhash_fp, "P %d", getpid());
236 for (argi = 0; argi < argc; argi++)
237 fprintf(dhash_fp, " [%s]", argv[argi]);
238 fprintf(dhash_fp, "\n");
239 }
240
241 void
242 mandoc_dbg_name(const char *name)
243 {
244 if (dhash_nflag)
245 fprintf(dhash_fp, "N %s\n", name);
246 }
247
248 /* Hash a pointer and return the table slot currently used for it. */
249 static unsigned int
250 dhash_slot(void *ptr)
251 {
252 const char *ks, *ke;
253 uint32_t hv;
254
255 ks = (const char *)&ptr;
256 ke = ks + sizeof(ptr);
257 hv = ohash_interval(ks, &ke);
258 return ohash_lookup_memory(&dhash_table, ks, sizeof(ptr), hv);
259 }
260
261 /* Record one allocation in the debugging table. */
262 static void
263 dhash_register(const char *file, int line, const char *func,
264 size_t num, size_t size, void *ptr, const char *str)
265 {
266 struct dhash_entry *e;
267 unsigned int slot;
268
269 slot = dhash_slot(ptr);
270 e = ohash_find(&dhash_table, slot);
271 if (dhash_aflag || e != NULL) {
272 fprintf(dhash_fp, "A %s:%d %s(%zu, %zu) = %p",
273 file, line, func, num, size, ptr);
274 if (str != NULL)
275 fprintf(dhash_fp, " \"%s\"", str);
276 fprintf(dhash_fp, "\n");
277 }
278 if (e != NULL) {
279 dhash_print(e);
280 fprintf(dhash_fp, "E duplicate address %p\n", e->ptr);
281 errx((int)MANDOCLEVEL_BADARG, "duplicate address %p", e->ptr);
282 }
283
284 if ((e = malloc(sizeof(*e))) == NULL)
285 err(1, NULL);
286 e->file = file;
287 e->line = line;
288 e->func = func;
289 e->num = num;
290 e->size = size;
291 e->ptr = ptr;
292
293 ohash_insert(&dhash_table, slot, e);
294 }
295
296 /* Remove one allocation from the debugging table. */
297 static void
298 dhash_purge(const char *file, int line, const char *func, void *ptr)
299 {
300 struct dhash_entry *e;
301 unsigned int slot;
302
303 if (ptr == NULL)
304 return;
305
306 if (dhash_fflag)
307 fprintf(dhash_fp, "F %s:%d %s(%p)\n", file, line, func, ptr);
308
309 slot = dhash_slot(ptr);
310 e = ohash_remove(&dhash_table, slot);
311 free(e);
312 }
313
314 /* Pretty-print information about one allocation. */
315 static void
316 dhash_print(struct dhash_entry *e)
317 {
318 fprintf(dhash_fp, "L %s:%d %s(%zu, %zu) = %p\n",
319 e->file, e->line, e->func, e->num, e->size, e->ptr);
320 }
321
322 /* Pretty-print information about all active allocations. */
323 void
324 mandoc_dbg_finish(void)
325 {
326 struct dhash_entry *e;
327 unsigned int errcount, slot;
328
329 errcount = ohash_entries(&dhash_table);
330 e = ohash_first(&dhash_table, &slot);
331 while (e != NULL) {
332 if (dhash_lflag)
333 dhash_print(e);
334 free(e);
335 e = ohash_next(&dhash_table, &slot);
336 }
337 ohash_delete(&dhash_table);
338 if (dhash_sflag)
339 fprintf(dhash_fp, "S %u memory leaks found\n", errcount);
340 if (dhash_fp != stderr)
341 fclose(dhash_fp);
342 }