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