summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@openbsd.org>2022-04-14 16:43:43 +0000
committerIngo Schwarze <schwarze@openbsd.org>2022-04-14 16:43:43 +0000
commitbddcaddd71aa306d9c5a606bc7ece6e2812925c5 (patch)
treecacfd28f1b6b5861bfa32dfeb9551fabcb76bf3c
parent525559c24a4e02693f443d611b8920564a497f85 (diff)
downloadmandoc-bddcaddd71aa306d9c5a606bc7ece6e2812925c5.tar.gz
support for hunting memory leaks;
designed and written last autumn, polished today
-rw-r--r--Makefile7
-rw-r--r--Makefile.depend89
-rwxr-xr-xconfigure8
-rw-r--r--configure.local.example8
-rw-r--r--demandoc.c13
-rw-r--r--main.c8
-rw-r--r--man_macro.c8
-rw-r--r--mandoc_aux.c5
-rw-r--r--mandoc_aux.h8
-rw-r--r--mandoc_dbg.c342
-rw-r--r--mandoc_dbg.h55
-rw-r--r--mandoc_dbg_init.3280
-rw-r--r--mandoc_headers.335
-rw-r--r--mandocd.c15
-rw-r--r--mandocdb.c4
-rw-r--r--mdoc_macro.c12
-rw-r--r--mdoc_state.c5
-rw-r--r--tbl_html.c5
-rw-r--r--tbl_term.c7
19 files changed, 848 insertions, 66 deletions
diff --git a/Makefile b/Makefile
index 6bd65ed0..17e1efb7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
# $Id$
#
-# Copyright (c) 2011, 2013-2021 Ingo Schwarze <schwarze@openbsd.org>
+# Copyright (c) 2011, 2013-2022 Ingo Schwarze <schwarze@openbsd.org>
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
#
# Permission to use, copy, modify, and distribute this software for any
@@ -100,6 +100,7 @@ SRCS = arch.c \
man_validate.c \
mandoc.c \
mandoc_aux.c \
+ mandoc_dbg.c \
mandoc_msg.c \
mandoc_ohash.c \
mandoc_xr.c \
@@ -186,6 +187,8 @@ DISTFILES = INSTALL \
mandoc.h \
mandoc_aux.h \
mandoc_char.7 \
+ mandoc_dbg.h \
+ mandoc_dbg_init.3 \
mandoc_escape.3 \
mandoc_headers.3 \
mandoc_html.3 \
@@ -241,6 +244,7 @@ LIBROFF_OBJS = eqn.o \
LIBMANDOC_OBJS = $(LIBMAN_OBJS) \
$(LIBMDOC_OBJS) \
$(LIBROFF_OBJS) \
+ $(DEBUG_OBJS) \
arch.o \
chars.o \
mandoc.o \
@@ -333,6 +337,7 @@ WWW_MANS = apropos.1.html \
soelim.1.html \
man.cgi.3.html \
mandoc.3.html \
+ mandoc_dbg_init.3.html \
mandoc_escape.3.html \
mandoc_headers.3.html \
mandoc_html.3.html \
diff --git a/Makefile.depend b/Makefile.depend
index d5f6556c..5179e95d 100644
--- a/Makefile.depend
+++ b/Makefile.depend
@@ -1,8 +1,8 @@
arch.o: arch.c config.h roff.h
att.o: att.c config.h roff.h libmdoc.h
catman.o: catman.c config.h compat_fts.h
-cgi.o: cgi.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h main.h manconf.h mansearch.h cgi.h
-chars.o: chars.c config.h mandoc.h mandoc_aux.h mandoc_ohash.h compat_ohash.h libmandoc.h
+cgi.o: cgi.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h main.h manconf.h mansearch.h cgi.h
+chars.o: chars.c config.h mandoc.h mandoc_aux.h mandoc_dbg.h mandoc_ohash.h compat_ohash.h libmandoc.h
compat_err.o: compat_err.c config.h
compat_fts.o: compat_fts.c config.h compat_fts.h
compat_getline.o: compat_getline.c config.h
@@ -22,62 +22,63 @@ compat_strndup.o: compat_strndup.c config.h
compat_strsep.o: compat_strsep.c config.h
compat_strtonum.o: compat_strtonum.c config.h
compat_vasprintf.o: compat_vasprintf.c config.h
-dba.o: dba.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mansearch.h dba_write.h dba_array.h dba.h
-dba_array.o: dba_array.c config.h mandoc_aux.h dba_write.h dba_array.h
-dba_read.o: dba_read.c config.h mandoc_aux.h mansearch.h dba_array.h dba.h dbm.h
+dba.o: dba.c config.h mandoc_aux.h mandoc_dbg.h mandoc_ohash.h compat_ohash.h mansearch.h dba_write.h dba_array.h dba.h
+dba_array.o: dba_array.c config.h mandoc_aux.h mandoc_dbg.h dba_write.h dba_array.h
+dba_read.o: dba_read.c config.h mandoc_aux.h mandoc_dbg.h mansearch.h dba_array.h dba.h dbm.h
dba_write.o: dba_write.c config.h dba_write.h
dbm.o: dbm.c config.h mansearch.h dbm_map.h dbm.h
dbm_map.o: dbm_map.c config.h mansearch.h dbm_map.h dbm.h
-demandoc.o: demandoc.c config.h mandoc.h roff.h man.h mdoc.h mandoc_parse.h
-eqn.o: eqn.c config.h mandoc_aux.h mandoc.h roff.h eqn.h libmandoc.h eqn_parse.h
+demandoc.o: demandoc.c config.h mandoc.h mandoc_dbg.h roff.h man.h mdoc.h mandoc_parse.h
+eqn.o: eqn.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h eqn.h libmandoc.h eqn_parse.h
eqn_html.o: eqn_html.c config.h mandoc.h roff.h eqn.h out.h html.h
eqn_term.o: eqn_term.c config.h eqn.h out.h term.h
-html.o: html.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h out.h html.h manconf.h main.h
+html.o: html.c config.h mandoc_aux.h mandoc_dbg.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h out.h html.h manconf.h main.h
lib.o: lib.c config.h roff.h libmdoc.h lib.in
-main.o: main.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h man.h mandoc_parse.h tag.h term_tag.h main.h manconf.h mansearch.h
-man.o: man.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
-man_html.o: man_html.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h html.h main.h
-man_macro.o: man_macro.c config.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
-man_term.o: man_term.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h term.h term_tag.h main.h
-man_validate.o: man_validate.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h tag.h
-mandoc.o: mandoc.c config.h mandoc_aux.h mandoc.h roff.h libmandoc.h roff_int.h
-mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h
+main.o: main.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h mandoc_xr.h roff.h mdoc.h man.h mandoc_parse.h tag.h term_tag.h main.h manconf.h mansearch.h
+man.o: man.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
+man_html.o: man_html.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h man.h out.h html.h main.h
+man_macro.o: man_macro.c config.h mandoc_dbg.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h
+man_term.o: man_term.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h man.h out.h term.h term_tag.h main.h
+man_validate.o: man_validate.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h tag.h
+mandoc.o: mandoc.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h libmandoc.h roff_int.h
+mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h mandoc_dbg.h
+mandoc_dbg.o: mandoc_dbg.c config.h compat_ohash.h mandoc_aux.h mandoc_dbg.h mandoc.h
mandoc_msg.o: mandoc_msg.c config.h mandoc.h
-mandoc_ohash.o: mandoc_ohash.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h
-mandoc_xr.o: mandoc_xr.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc_xr.h
-mandocd.o: mandocd.c config.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h main.h manconf.h
-mandocdb.o: mandocdb.c config.h compat_fts.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h manconf.h mansearch.h dba_array.h dba.h
-manpath.o: manpath.c config.h mandoc_aux.h mandoc.h manconf.h
-mansearch.o: mansearch.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h manconf.h mansearch.h dbm.h
-mdoc.o: mdoc.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
-mdoc_argv.o: mdoc_argv.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
-mdoc_html.o: mdoc_html.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h out.h html.h main.h
-mdoc_macro.o: mdoc_macro.c config.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
-mdoc_man.o: mdoc_man.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h out.h main.h
-mdoc_markdown.o: mdoc_markdown.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h main.h
-mdoc_state.o: mdoc_state.c config.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
-mdoc_term.o: mdoc_term.c config.h mandoc_aux.h roff.h mdoc.h out.h term.h term_tag.h main.h
-mdoc_validate.o: mdoc_validate.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h tag.h
+mandoc_ohash.o: mandoc_ohash.c config.h mandoc_aux.h mandoc_dbg.h mandoc_ohash.h compat_ohash.h
+mandoc_xr.o: mandoc_xr.c config.h mandoc_aux.h mandoc_dbg.h mandoc_ohash.h compat_ohash.h mandoc_xr.h
+mandocd.o: mandocd.c config.h mandoc.h mandoc_dbg.h roff.h mdoc.h man.h mandoc_parse.h main.h manconf.h
+mandocdb.o: mandocdb.c config.h compat_fts.h mandoc_aux.h mandoc_dbg.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h manconf.h mansearch.h dba_array.h dba.h
+manpath.o: manpath.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h manconf.h
+mansearch.o: mansearch.c config.h mandoc_aux.h mandoc_dbg.h mandoc_ohash.h compat_ohash.h manconf.h mansearch.h dbm.h
+mdoc.o: mdoc.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
+mdoc_argv.o: mdoc_argv.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
+mdoc_html.o: mdoc_html.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h mdoc.h out.h html.h main.h
+mdoc_macro.o: mdoc_macro.c config.h mandoc_dbg.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
+mdoc_man.o: mdoc_man.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h mdoc.h man.h out.h main.h
+mdoc_markdown.o: mdoc_markdown.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h mdoc.h main.h
+mdoc_state.o: mdoc_state.c config.h mandoc_dbg.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
+mdoc_term.o: mdoc_term.c config.h mandoc_aux.h mandoc_dbg.h roff.h mdoc.h out.h term.h term_tag.h main.h
+mdoc_validate.o: mdoc_validate.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h mandoc_xr.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h tag.h
msec.o: msec.c config.h mandoc.h libmandoc.h msec.in
-out.o: out.c config.h mandoc_aux.h mandoc.h tbl.h out.h
+out.o: out.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h tbl.h out.h
preconv.o: preconv.c config.h mandoc.h roff.h mandoc_parse.h libmandoc.h
-read.o: read.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h libmandoc.h roff_int.h tag.h
-roff.o: roff.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mandoc_parse.h libmandoc.h roff_int.h tbl_parse.h eqn_parse.h predefs.in
+read.o: read.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h libmandoc.h roff_int.h tag.h
+roff.o: roff.c config.h mandoc_aux.h mandoc_dbg.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mandoc_parse.h libmandoc.h roff_int.h tbl_parse.h eqn_parse.h predefs.in
roff_html.o: roff_html.c config.h mandoc.h roff.h out.h html.h
roff_term.o: roff_term.c config.h mandoc.h roff.h out.h term.h
roff_validate.o: roff_validate.c config.h mandoc.h roff.h libmandoc.h roff_int.h
soelim.o: soelim.c config.h compat_stringlist.h
st.o: st.c config.h mandoc.h roff.h libmdoc.h
-tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h roff.h mdoc.h roff_int.h tag.h
-tbl.o: tbl.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_parse.h tbl_int.h
-tbl_data.o: tbl_data.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_int.h
-tbl_html.o: tbl_html.c config.h mandoc.h roff.h tbl.h out.h html.h
-tbl_layout.o: tbl_layout.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_int.h
+tag.o: tag.c config.h mandoc_aux.h mandoc_dbg.h mandoc_ohash.h compat_ohash.h roff.h mdoc.h roff_int.h tag.h
+tbl.o: tbl.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h tbl.h libmandoc.h tbl_parse.h tbl_int.h
+tbl_data.o: tbl_data.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h tbl.h libmandoc.h tbl_int.h
+tbl_html.o: tbl_html.c config.h mandoc_dbg.h mandoc.h roff.h tbl.h out.h html.h
+tbl_layout.o: tbl_layout.c config.h mandoc_aux.h mandoc_dbg.h mandoc.h tbl.h libmandoc.h tbl_int.h
tbl_opts.o: tbl_opts.c config.h mandoc.h tbl.h libmandoc.h tbl_int.h
-tbl_term.o: tbl_term.c config.h mandoc.h tbl.h out.h term.h
-term.o: term.c config.h mandoc.h mandoc_aux.h out.h term.h main.h
-term_ascii.o: term_ascii.c config.h mandoc.h mandoc_aux.h out.h term.h manconf.h main.h
-term_ps.o: term_ps.c config.h mandoc_aux.h out.h term.h manconf.h main.h
-term_tab.o: term_tab.c config.h mandoc_aux.h out.h term.h
+tbl_term.o: tbl_term.c config.h mandoc_dbg.h mandoc.h tbl.h out.h term.h
+term.o: term.c config.h mandoc.h mandoc_aux.h mandoc_dbg.h out.h term.h main.h
+term_ascii.o: term_ascii.c config.h mandoc.h mandoc_aux.h mandoc_dbg.h out.h term.h manconf.h main.h
+term_ps.o: term_ps.c config.h mandoc_aux.h mandoc_dbg.h out.h term.h manconf.h main.h
+term_tab.o: term_tab.c config.h mandoc_aux.h mandoc_dbg.h out.h term.h
term_tag.o: term_tag.c config.h mandoc.h roff.h roff_int.h tag.h term_tag.h
tree.o: tree.c config.h mandoc.h roff.h mdoc.h man.h tbl.h eqn.h main.h
diff --git a/configure b/configure
index 6d82bd11..d6e00e2a 100755
--- a/configure
+++ b/configure
@@ -37,6 +37,7 @@ SOURCEDIR=`dirname "${0}"`
MANPATH_BASE="/usr/share/man:/usr/X11R6/man"
MANPATH_DEFAULT="/usr/share/man:/usr/X11R6/man:/usr/local/man"
+DEBUG_MEMORY=0
OSENUM=
OSNAME=
UTF8_LOCALE=
@@ -99,6 +100,7 @@ NEED_GNU_SOURCE=0
NEED_OPENBSD_SOURCE=0
NEED_XPG4_2=0
+DEBUG_OBJS=
MANDOC_COBJS=
SOELIM_COBJS=
@@ -334,6 +336,7 @@ runtest vasprintf VASPRINTF "" -D_GNU_SOURCE || true
# --- fts ---
if [ "${1}" = "-depend" ]; then
+ DEBUG_MEMORY=1
HAVE_FTS=0
HAVE_FTS_COMPARE_CONST=0
echo "tested fts: HAVE_FTS=0 (for make depend)" 1>&2
@@ -461,6 +464,10 @@ echo
echo "#define MAN_CONF_FILE \"/etc/${MANM_MANCONF}\""
echo "#define MANPATH_BASE \"${MANPATH_BASE}\""
echo "#define MANPATH_DEFAULT \"${MANPATH_DEFAULT}\""
+if [ ${DEBUG_MEMORY} -ne 0 ]; then
+ echo "#define DEBUG_MEMORY ${DEBUG_MEMORY}"
+ DEBUG_OBJS=mandoc_dbg.o
+fi
echo "#define OSENUM ${OSENUM}"
[ -n "${OSNAME}" ] && echo "#define OSNAME \"${OSNAME}\""
[ -n "${UTF8_LOCALE}" ] && echo "#define UTF8_LOCALE \"${UTF8_LOCALE}\""
@@ -640,6 +647,7 @@ CC = ${CC}
CFLAGS = ${CFLAGS}
LDADD = ${LDADD}
LDFLAGS = ${LDFLAGS}
+DEBUG_OBJS = ${DEBUG_OBJS}
MANDOC_COBJS = ${MANDOC_COBJS}
SOELIM_COBJS = ${SOELIM_COBJS}
STATIC = ${STATIC}
diff --git a/configure.local.example b/configure.local.example
index 8ca039a4..3fa240c9 100644
--- a/configure.local.example
+++ b/configure.local.example
@@ -1,6 +1,6 @@
# $Id$
#
-# Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org>
+# Copyright (c) 2014-2022 Ingo Schwarze <schwarze@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -312,6 +312,12 @@ BINM_CATMAN=mcatman # default is "catman"
CFLAGS="-g"
+# Hunt for memory leaks.
+# Do not use for production builds.
+# See mandoc_dbg_init(3) for more information.
+
+DEBUG_MEMORY=1
+
# In rare cases, it may be required to skip individual automatic tests.
# Each of the following variables can be set to 0 (test will not be run
# and will be regarded as failed) or 1 (test will not be run and will
diff --git a/demandoc.c b/demandoc.c
index 6ff1c232..e22aa6b5 100644
--- a/demandoc.c
+++ b/demandoc.c
@@ -1,4 +1,4 @@
-/* $Id$ */
+/* $Id$ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@@ -26,6 +26,10 @@
#include <unistd.h>
#include "mandoc.h"
+#if DEBUG_MEMORY
+#define DEBUG_NODEF
+#include "mandoc_dbg.h"
+#endif
#include "roff.h"
#include "man.h"
#include "mdoc.h"
@@ -47,6 +51,10 @@ main(int argc, char *argv[])
int ch, fd, i, list;
extern int optind;
+#if DEBUG_MEMORY
+ mandoc_dbg_init(argc, argv);
+#endif
+
if (argc < 1)
progname = "demandoc";
else if ((progname = strrchr(argv[0], '/')) == NULL)
@@ -97,6 +105,9 @@ main(int argc, char *argv[])
mparse_free(mp);
mchars_free();
+#if DEBUG_MEMORY
+ mandoc_dbg_finish();
+#endif
return (int)MANDOCLEVEL_OK;
}
diff --git a/main.c b/main.c
index c5d15d0c..bd2b3e68 100644
--- a/main.c
+++ b/main.c
@@ -149,11 +149,14 @@ main(int argc, char *argv[])
enum mandoc_os os_e; /* Check base system conventions. */
enum outmode outmode; /* According to command line. */
+#if DEBUG_MEMORY
+ mandoc_dbg_init(argc, argv);
+#endif
#if HAVE_PROGNAME
progname = getprogname();
#else
if (argc < 1)
- progname = mandoc_strdup("mandoc");
+ progname = "mandoc";
else if ((progname = strrchr(argv[0], '/')) == NULL)
progname = argv[0];
else
@@ -671,6 +674,9 @@ out:
} else if (outst.had_output && outst.outtype != OUTT_LINT)
mandoc_msg_summary();
+#if DEBUG_MEMORY
+ mandoc_dbg_finish();
+#endif
return (int)mandoc_msg_getrc();
}
diff --git a/man_macro.c b/man_macro.c
index e16a6a74..9157ee65 100644
--- a/man_macro.c
+++ b/man_macro.c
@@ -26,6 +26,9 @@
#include <stdlib.h>
#include <string.h>
+#if DEBUG_MEMORY
+#include "mandoc_dbg.h"
+#endif
#include "mandoc.h"
#include "roff.h"
#include "man.h"
@@ -394,6 +397,11 @@ in_line_eoln(MACRO_PROT_ARGS)
else if (tok == MAN_EE)
man->flags &= ~ROFF_NOFILL;
+#if DEBUG_MEMORY
+ if (tok == MAN_TH)
+ mandoc_dbg_name(buf);
+#endif
+
for (;;) {
if (buf[*pos] != '\0' && man->last != n && tok == MAN_PD) {
mandoc_msg(MANDOCERR_ARG_EXCESS, line, *pos,
diff --git a/mandoc_aux.c b/mandoc_aux.c
index f1dce2e8..3d751cc8 100644
--- a/mandoc_aux.c
+++ b/mandoc_aux.c
@@ -1,7 +1,7 @@
-/* $Id$ */
+/* $Id$ */
/*
+ * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <string.h>
+#define DEBUG_NODEF 1
#include "mandoc.h"
#include "mandoc_aux.h"
diff --git a/mandoc_aux.h b/mandoc_aux.h
index 765f3c99..7991b30c 100644
--- a/mandoc_aux.h
+++ b/mandoc_aux.h
@@ -1,7 +1,7 @@
-/* $Id$ */
+/* $Id$ */
/*
+ * Copyright (c) 2014, 2017, 2021 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014, 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -25,3 +25,7 @@ void *mandoc_reallocarray(void *, size_t, size_t);
void *mandoc_recallocarray(void *, size_t, size_t, size_t);
char *mandoc_strdup(const char *);
char *mandoc_strndup(const char *, size_t);
+
+#if DEBUG_MEMORY
+#include "mandoc_dbg.h"
+#endif
diff --git a/mandoc_dbg.c b/mandoc_dbg.c
new file mode 100644
index 00000000..647ebf7d
--- /dev/null
+++ b/mandoc_dbg.c
@@ -0,0 +1,342 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2021, 2022 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include "config.h"
+
+#include <sys/types.h>
+
+#if HAVE_ERR
+#include <err.h>
+#endif
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if HAVE_OHASH
+#include <ohash.h>
+#else
+#include "compat_ohash.h"
+#endif
+
+#define DEBUG_NODEF 1
+#include "mandoc_aux.h"
+#include "mandoc.h"
+
+/* Store information about one allocation. */
+struct dhash_entry {
+ const char *file;
+ int line;
+ const char *func;
+ size_t num;
+ size_t size;
+ void *ptr;
+};
+
+/* Store information about all allocations. */
+static struct ohash dhash_table;
+static FILE *dhash_fp;
+static int dhash_aflag;
+static int dhash_fflag;
+static int dhash_lflag;
+static int dhash_nflag;
+static int dhash_sflag;
+
+static void *dhash_alloc(size_t, void *);
+static void *dhash_calloc(size_t, size_t, void *);
+static void dhash_free(void *, void *);
+static unsigned int dhash_slot(void *);
+static void dhash_register(const char *, int, const char *,
+ size_t, size_t, void *, const char *);
+static void dhash_print(struct dhash_entry *);
+static void dhash_purge(const char *, int, const char *, void *);
+
+
+/* *** Debugging wrappers of public API functions. ************************ */
+
+int
+mandoc_dbg_asprintf(const char *file, int line,
+ char **dest, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(dest, fmt, ap);
+ va_end(ap);
+
+ if (ret == -1)
+ err((int)MANDOCLEVEL_SYSERR, NULL);
+
+ dhash_register(file, line, "asprintf", 1, strlen(*dest) + 1,
+ *dest, *dest);
+
+ return ret;
+}
+
+void *
+mandoc_dbg_calloc(size_t num, size_t size, const char *file, int line)
+{
+ void *ptr = mandoc_calloc(num, size);
+ dhash_register(file, line, "calloc", num, size, ptr, NULL);
+ return ptr;
+}
+
+void *
+mandoc_dbg_malloc(size_t size, const char *file, int line)
+{
+ void *ptr = mandoc_malloc(size);
+ dhash_register(file, line, "malloc", 1, size, ptr, NULL);
+ return ptr;
+}
+
+void *
+mandoc_dbg_realloc(void *ptr, size_t size, const char *file, int line)
+{
+ dhash_purge(file, line, "realloc", ptr);
+ ptr = mandoc_realloc(ptr, size);
+ dhash_register(file, line, "realloc", 1, size, ptr, NULL);
+ return ptr;
+}
+
+void *
+mandoc_dbg_reallocarray(void *ptr, size_t num, size_t size,
+ const char *file, int line)
+{
+ dhash_purge(file, line, "reallocarray", ptr);
+ ptr = mandoc_reallocarray(ptr, num, size);
+ dhash_register(file, line, "reallocarray", num, size, ptr, NULL);
+ return ptr;
+}
+
+void *
+mandoc_dbg_recallocarray(void *ptr, size_t oldnum, size_t num, size_t size,
+ const char *file, int line)
+{
+ dhash_purge(file, line, "recallocarray", ptr);
+ ptr = mandoc_recallocarray(ptr, oldnum, num, size);
+ dhash_register(file, line, "recallocarray", num, size, ptr, NULL);
+ return ptr;
+}
+
+char *
+mandoc_dbg_strdup(const char *ptr, const char *file, int line)
+{
+ char *p = mandoc_strdup(ptr);
+ dhash_register(file, line, "strdup", 1, strlen(p) + 1, p, ptr);
+ return p;
+}
+
+char *
+mandoc_dbg_strndup(const char *ptr, size_t sz, const char *file, int line)
+{
+ char *p = mandoc_strndup(ptr, sz);
+ dhash_register(file, line, "strndup", 1, strlen(p) + 1, p, NULL);
+ return p;
+}
+
+void
+mandoc_dbg_free(void *ptr, const char *file, int line)
+{
+ dhash_purge(file, line, "free", ptr);
+ free(ptr);
+}
+
+
+/* *** Memory allocation callbacks for the debugging table. *************** */
+
+static void *
+dhash_alloc(size_t sz, void *arg)
+{
+ return malloc(sz);
+}
+
+static void *
+dhash_calloc(size_t n, size_t sz, void *arg)
+{
+ return calloc(n, sz);
+}
+
+static void
+dhash_free(void *p, void *arg)
+{
+ free(p);
+}
+
+
+/* *** Debugging utility functions. *************************************** */
+
+/* Initialize the debugging table, to be called from the top of main(). */
+void
+mandoc_dbg_init(int argc, char *argv[])
+{
+ struct ohash_info info;
+ char *dhash_fn;
+ int argi;
+
+ info.alloc = dhash_alloc;
+ info.calloc = dhash_calloc;
+ info.free = dhash_free;
+ info.data = NULL;
+ info.key_offset = offsetof(struct dhash_entry, ptr);
+ ohash_init(&dhash_table, 18, &info);
+
+ dhash_fp = stderr;
+ if ((dhash_fn = getenv("DEBUG_MEMORY")) == NULL)
+ return;
+
+ dhash_sflag = 1;
+ for(;; dhash_fn++) {
+ switch (*dhash_fn) {
+ case '\0':
+ break;
+ case 'A':
+ dhash_aflag = 1;
+ continue;
+ case 'F':
+ dhash_fflag = 1;
+ continue;
+ case 'L':
+ dhash_lflag = 1;
+ continue;
+ case 'N':
+ dhash_nflag = 1;
+ continue;
+ case '/':
+ if ((dhash_fp = fopen(dhash_fn, "a+e")) == NULL)
+ err((int)MANDOCLEVEL_SYSERR, "%s", dhash_fn);
+ break;
+ default:
+ errx((int)MANDOCLEVEL_BADARG,
+ "invalid char '%c' in $DEBUG_MEMORY",
+ *dhash_fn);
+ }
+ break;
+ }
+ if (setvbuf(dhash_fp, NULL, _IOLBF, 0) != 0)
+ err((int)MANDOCLEVEL_SYSERR, "setvbuf");
+
+ fprintf(dhash_fp, "P %d", getpid());
+ for (argi = 0; argi < argc; argi++)
+ fprintf(dhash_fp, " [%s]", argv[argi]);
+ fprintf(dhash_fp, "\n");
+}
+
+void
+mandoc_dbg_name(const char *name)
+{
+ if (dhash_nflag)
+ fprintf(dhash_fp, "N %s\n", name);
+}
+
+/* Hash a pointer and return the table slot currently used for it. */
+static unsigned int
+dhash_slot(void *ptr)
+{
+ const char *ks, *ke;
+ uint32_t hv;
+
+ ks = (const char *)&ptr;
+ ke = ks + sizeof(ptr);
+ hv = ohash_interval(ks, &ke);
+ return ohash_lookup_memory(&dhash_table, ks, sizeof(ptr), hv);
+}
+
+/* Record one allocation in the debugging table. */
+static void
+dhash_register(const char *file, int line, const char *func,
+ size_t num, size_t size, void *ptr, const char *str)
+{
+ struct dhash_entry *e;
+ unsigned int slot;
+
+ slot = dhash_slot(ptr);
+ e = ohash_find(&dhash_table, slot);
+ if (dhash_aflag || e != NULL) {
+ fprintf(dhash_fp, "A %s:%d %s(%zu, %zu) = %p",
+ file, line, func, num, size, ptr);
+ if (str != NULL)
+ fprintf(dhash_fp, " \"%s\"", str);
+ fprintf(dhash_fp, "\n");
+ }
+ if (e != NULL) {
+ dhash_print(e);
+ fprintf(dhash_fp, "E duplicate address %p\n", e->ptr);
+ errx((int)MANDOCLEVEL_BADARG, "duplicate address %p", e->ptr);
+ }
+
+ if ((e = malloc(sizeof(*e))) == NULL)
+ err(1, NULL);
+ e->file = file;
+ e->line = line;
+ e->func = func;
+ e->num = num;
+ e->size = size;
+ e->ptr = ptr;
+
+ ohash_insert(&dhash_table, slot, e);
+}
+
+/* Remove one allocation from the debugging table. */
+static void
+dhash_purge(const char *file, int line, const char *func, void *ptr)
+{
+ struct dhash_entry *e;
+ unsigned int slot;
+
+ if (ptr == NULL)
+ return;
+
+ if (dhash_fflag)
+ fprintf(dhash_fp, "F %s:%d %s(%p)\n", file, line, func, ptr);
+
+ slot = dhash_slot(ptr);
+ e = ohash_remove(&dhash_table, slot);
+ free(e);
+}
+
+/* Pretty-print information about one allocation. */
+static void
+dhash_print(struct dhash_entry *e)
+{
+ fprintf(dhash_fp, "L %s:%d %s(%zu, %zu) = %p\n",
+ e->file, e->line, e->func, e->num, e->size, e->ptr);
+}
+
+/* Pretty-print information about all active allocations. */
+void
+mandoc_dbg_finish(void)
+{
+ struct dhash_entry *e;
+ unsigned int errcount, slot;
+
+ errcount = ohash_entries(&dhash_table);
+ e = ohash_first(&dhash_table, &slot);
+ while (e != NULL) {
+ if (dhash_lflag)
+ dhash_print(e);
+ free(e);
+ e = ohash_next(&dhash_table, &slot);
+ }
+ ohash_delete(&dhash_table);
+ if (dhash_sflag)
+ fprintf(dhash_fp, "S %u memory leaks found\n", errcount);
+ if (dhash_fp != stderr)
+ fclose(dhash_fp);
+}
diff --git a/mandoc_dbg.h b/mandoc_dbg.h
new file mode 100644
index 00000000..7801d141
--- /dev/null
+++ b/mandoc_dbg.h
@@ -0,0 +1,55 @@
+/* $Id$ */
+/*
+ * Copyright (c) 2021 Ingo Schwarze <schwarze@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+int mandoc_dbg_asprintf(const char *, int, char **, const char *, ...)
+ __attribute__((__format__ (__printf__, 4, 5)));
+void *mandoc_dbg_calloc(size_t, size_t, const char *, int);
+void *mandoc_dbg_malloc(size_t, const char *, int);
+void *mandoc_dbg_realloc(void *, size_t, const char *, int);
+void *mandoc_dbg_reallocarray(void *, size_t, size_t,
+ const char *, int);
+void *mandoc_dbg_recallocarray(void *, size_t, size_t, size_t,
+ const char *, int);
+char *mandoc_dbg_strdup(const char *, const char *, int);
+char *mandoc_dbg_strndup(const char *, size_t, const char *, int);
+void mandoc_dbg_free(void *, const char *, int);
+
+void mandoc_dbg_init(int argc, char *argv[]);
+void mandoc_dbg_name(const char *);
+void mandoc_dbg_finish(void);
+
+#ifndef DEBUG_NODEF
+#define mandoc_asprintf(dest, fmt, ...) \
+ mandoc_dbg_asprintf(__FILE__, __LINE__, (dest), (fmt), __VA_ARGS__)
+#define mandoc_calloc(num, size) \
+ mandoc_dbg_calloc((num), (size), __FILE__, __LINE__)
+#define mandoc_malloc(size) \
+ mandoc_dbg_malloc((size), __FILE__, __LINE__)
+#define mandoc_realloc(ptr, size) \
+ mandoc_dbg_realloc((ptr), (size), __FILE__, __LINE__)
+#define mandoc_reallocarray(ptr, num, size) \
+ mandoc_dbg_reallocarray((ptr), (num), (size), __FILE__, __LINE__)
+#define mandoc_recallocarray(ptr, old, num, size) \
+ mandoc_dbg_recallocarray((ptr), (old), (num), (size), \
+ __FILE__, __LINE__)
+#define mandoc_strdup(ptr) \
+ mandoc_dbg_strdup((ptr), __FILE__, __LINE__)
+#define mandoc_strndup(ptr, size) \
+ mandoc_dbg_strndup((ptr), (size), __FILE__, __LINE__)
+#define free(ptr) \
+ mandoc_dbg_free((ptr), __FILE__, __LINE__)
+#endif
diff --git a/mandoc_dbg_init.3 b/mandoc_dbg_init.3
new file mode 100644
index 00000000..58f737e0
--- /dev/null
+++ b/mandoc_dbg_init.3
@@ -0,0 +1,280 @@
+.\" $Id$
+.\"
+.\" Copyright (c) 2021, 2022 Ingo Schwarze <schwarze@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate$
+.Dt MANDOC_DBG_INIT 3
+.Os
+.Sh NAME
+.Nm mandoc_dbg_init ,
+.Nm mandoc_dbg_name ,
+.Nm mandoc_dbg_finish
+.Nd search for memory leaks in mandoc
+.Sh SYNOPSIS
+.Ft void
+.Fn mandoc_dbg_init "int argc" "char *argv[]"
+.Ft void
+.Fn mandoc_dbg_name "const char *"
+.Ft void
+.Fn mandoc_dbg_finish void
+.Sh DESCRIPTION
+If the mandoc package is built with the line
+.Ql DEBUG_MEMORY=1
+in the file
+.Pa configure.local ,
+the functions documented in
+.Xr mandoc_malloc 3
+and the function
+.Xr free 3
+are instrumented to record every memory allocation in a dedicated
+hash table and to check that every allocation is freed again.
+This compile time option is only intended for binaries that are
+used exclusively for debugging.
+It is not intended for production binaries because it significantly
+increases run time and memory usage and makes the programs more
+fragile and more error-prone.
+.Pp
+The function
+.Fn mandoc_dbg_init
+initializes the memory debugging subsystem.
+It is called from the top of the
+.Fn main
+programs, passing through the arguments that
+.Fn main
+received.
+The
+.Sx ENVIRONMENT
+section of the present manual page explains how the
+.Ev DEBUG_MEMORY
+environment variable controls the amount and destination of reporting.
+.Pp
+The function
+.Fn mandoc_dbg_name
+is called from the
+.Xr mdoc 7
+and
+.Xr man 7
+parsers whenever a
+.Ic \&Dt
+or
+.Ic \&TH
+macro is parsed, passing the complete macro line as the argument.
+.Pp
+The function
+.Fn mandoc_dbg_finish
+performs cleanup and optionally final reporting.
+It is called from the end of the
+.Fn main
+programs, just before normal termination.
+.Pp
+Getting the
+.Sy #include
+directives right for these functions is slightly tricky.
+If a file already includes
+.Qq Pa mandoc_aux.h ,
+no additional directive is needed because
+.Qq Pa mandoc_aux.h
+already includes
+.Qq Pa mandoc_dgb.h
+if
+.Ql DEBUG_MEMORY=1
+is set in
+.Pa configure.local .
+.Pp
+If a file does not need
+.Qq Pa mandoc_aux.h
+but calls a function documented in the present manual page and also calls
+.Xr free 3
+directly, it needs this code before the other
+.Xr mandoc_headers 3 :
+.Bd -literal -offset indent
+#if DEBUG_MEMORY
+#include "mandoc_dbg.h"
+#endif
+.Ed
+.Pp
+If a file calls a function documented in the present manual page
+but does not directly call
+.Xr free 3 ,
+it can use this less intrusive idiom:
+.Bd -literal -offset indent
+#if DEBUG_MEMORY
+#define DEBUG_NODEF
+#include "mandoc_dbg.h"
+#endif
+.Ed
+.Sh ENVIRONMENT
+The environment variable
+.Ev DEBUG_MEMORY
+controls the amount and destination of reporting.
+.Pp
+If it is unset, diagnostic output is directed to standard error output
+and only fatal errors are reported.
+Even though full memory accounting is always performed
+by any binary that was compiled with
+.Ql DEBUG_MEMORY=1 ,
+resulting in a significant increase in both run time and memory usage,
+memory leaks are
+.Em not
+reported when
+.Ev DEBUG_MEMORY
+is not set at run time.
+.Pp
+If
+.Ev DEBUG_MEMORY
+is set, it is interpreted as a string of flags.
+The flags are as follows:
+.Bl -tag -width 1n
+.It Cm A
+Log every allocation.
+This produces huge amounts of output and is usually not needed
+to find memory leaks.
+Its main purpose is debugging the memory debugging subsystem itself.
+.Pp
+When enabled, allocations are logged in this format:
+.Pp
+.D1 Cm A Ar file Ns .c: Ns Ar line function Ns Po Fa nmemb , size Pc\
+ No = Ar address
+.Pp
+The meaning of the fields is the same as for the
+.Cm L
+option.
+.It Cm F
+Log every
+.Xr free 3
+and every reallocation where the memory to be released or reallocated
+was allocated with one of the functions documented in
+.Xr mandoc_malloc 3 .
+Again, this produces huge amounts of output and is usually not
+needed to find memory leaks, and its main purpose is debugging the
+memory debugging subsystem itself.
+.Pp
+The logging format is:
+.Pp
+.D1 Cm F Ar file Ns .c: Ns Ar line function Ns Pq address
+.Pp
+It provides the name of the
+.Ar file
+and the number of the
+.Ar line
+in that file which called the
+.Xr free 3
+or reallocation
+.Ar function ,
+and the
+.Fa address
+that was given as an argument.
+.Pp
+If both the
+.Cm A
+and the
+.Cm F
+flags are enabled, calls to reallocation functions often log two lines,
+first an
+.Cm F
+line reporting the address passed in as an argument, then an
+.Cm A
+line reporting the adress returned as the function return value.
+.It Cm L
+Log every memory leak.
+For every allocation made after
+.Fn mandoc_dbg_init
+using functions documented in
+.Xr mandoc_malloc 3
+that was not freed before
+.Fn mandoc_dbg_finish ,
+print a line in this format:
+.Pp
+.D1 Cm L Ar file Ns .c: Ns Ar line function Ns Po Fa nmemb , size Pc\
+ No = Ar address
+.Pp
+It provides the name of the
+.Ar file
+and the number of the
+.Ar line
+in that file which called the allocation
+.Ar function
+with the arguments
+.Fa nmemb
+and
+.Fa size
+documented for
+.Xr calloc 3 .
+If the
+.Ar function
+does not take an
+.Fa nmemb
+argument,
+.Fa nmemb
+is reported as 1.
+At the end of the line, the virtual
+.Ar address
+of the memory returned from the allocation function is reported.
+.It Cm N
+Log the names of manual pages processed in the following formats:
+.Bd -unfilled -offset indent
+.Cm N Pf . Ic \&Dt Ar name section Op Ar architecture
+.Cm N Pf . Ic \&TH Ar name section Op Ar additional arguments
+.Ed
+.Pp
+This is particularly useful if a program crashes, runs out of memory,
+or enters an infinite loop.
+The last
+.Cm N
+line logged often indicates the input file triggering the problem.
+.It Cm /
+Interpret the rest of
+.Ev DEBUG_MEMORY
+as an absolute path and redirect debugging output to that file,
+appending to the file if it already exists or creating it otherwise.
+.El
+.Pp
+If
+.Ev DEBUG_MEMORY
+is set, even if it is empty,
+.Fn mandoc_dbg_init
+always writes the line
+.Pp
+.D1 Cm P Ar pid Sy \&[ Ns Ar progname Ns Sy \&]\
+ Sy \&[ Ns Ar argument Ns Sy \&] Ar ...
+.Pp
+enclosing each element of
+.Fa argv
+in square brackets, to avoid that arguments containing whitespace
+appear in the same way as multiple arguments, and
+.Fn mandoc_dbg_finish
+always writes the line:
+.Pp
+.D1 Cm S Ar number No memory leaks found
+.Sh EXAMPLES
+The following is a typical sequence of commands for finding memory
+leaks in the parsers, in the HTML formatter, and in the regression suite:
+.Bd -literal -offset indent
+make distclean
+echo BUILD_CATMAN=1 >> configure.local
+echo DEBUG_MEMORY=1 >> configure.local
+\&./configure
+make
+export DEBUG_MEMORY=NL/tmp/mandoc.debug.txt
+mkdir Out
+export PATH=$PATH:$(pwd)
+\&./catman -T html /usr/share/man Out
+make regress-clean
+make regress
+less /tmp/mandoc.debug.txt
+.Ed
+.Sh SEE ALSO
+.Xr mandoc_malloc 3 ,
+.Xr catman 8
diff --git a/mandoc_headers.3 b/mandoc_headers.3
index d9e9b94f..c00942fc 100644
--- a/mandoc_headers.3
+++ b/mandoc_headers.3
@@ -1,6 +1,6 @@
-.\" $Id$
+.\" $Id$
.\"
-.\" Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2014-2022 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -25,8 +25,8 @@ To support a cleaner coding style, the mandoc header files do not
contain any include directives and do not guard against multiple
inclusion.
The application developer has to make sure that the headers are
-included in a proper order, and that no header is included more
-than once.
+included in the order shown in this manual page,
+and that no header is included more than once.
.Pp
The headers and functions form three major groups:
.Sx Parser interface ,
@@ -83,6 +83,33 @@ for
.Pp
Provides the functions documented in
.Xr mandoc_malloc 3 .
+.Pp
+When this header is included, the same file must not include
+.Qq Pa mandoc_dbg.h
+because
+.Qq Pa mandoc_aux.h
+automatically includes
+.Qq Pa mandoc_dbg.h
+if and only if the preprocessor symbol
+.Dv DEBUG_MEMORY
+is defined.
+.It Qq Pa mandoc_dbg.h
+Debugging utility functions and
+debugging wrappers around memory allocation functions.
+.Pp
+Requires
+.In sys/types.h
+for
+.Vt size_t .
+.Pp
+Provides the functions documented in
+.Xr mandoc_dbg_init 3 .
+.Pp
+This header must not be included unless the preprocessor symbol
+.Dv DEBUG_MEMORY
+is defined.
+When this header is included, the same file must not include
+.Qq Pa mandoc_aux.h .
.It Qq Pa mandoc_ohash.h
Hashing utility functions; can be used everywhere.
.Pp
diff --git a/mandocd.c b/mandocd.c
index 9d07857c..10919929 100644
--- a/mandocd.c
+++ b/mandocd.c
@@ -1,7 +1,7 @@
-/* $Id$ */
+/* $Id$ */
/*
* Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
- * Copyright (c) 2017, 2019 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2017, 2019, 2021 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -35,6 +35,10 @@
#include <unistd.h>
#include "mandoc.h"
+#if DEBUG_MEMORY
+#define DEBUG_NODEF 1
+#include "mandoc_dbg.h"
+#endif
#include "roff.h"
#include "mdoc.h"
#include "man.h"
@@ -129,6 +133,10 @@ main(int argc, char *argv[])
int state, opt;
enum outt outtype;
+#if DEBUG_MEMORY
+ mandoc_dbg_init(argc, argv);
+#endif
+
defos = NULL;
outtype = OUTT_ASCII;
while ((opt = getopt(argc, argv, "I:T:")) != -1) {
@@ -240,6 +248,9 @@ main(int argc, char *argv[])
}
mparse_free(parser);
mchars_free();
+#if DEBUG_MEMORY
+ mandoc_dbg_finish();
+#endif
return state == -1 ? 1 : 0;
}
diff --git a/mandocdb.c b/mandocdb.c
index afb61da6..fb12de71 100644
--- a/mandocdb.c
+++ b/mandocdb.c
@@ -1,6 +1,6 @@
/* $Id$ */
/*
- * Copyright (c) 2011-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011-2021 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2016 Ed Maste <emaste@freebsd.org>
*
@@ -533,7 +533,7 @@ out:
ohash_delete(&mpages);
ohash_delete(&mlinks);
#if DEBUG_MEMORY
- mandoc_d_finish();
+ mandoc_dbg_finish();
#endif
return exitcode;
usage:
diff --git a/mdoc_macro.c b/mdoc_macro.c
index 9294d846..ebc058f6 100644
--- a/mdoc_macro.c
+++ b/mdoc_macro.c
@@ -1,7 +1,7 @@
-/* $Id$ */
+/* $Id$ */
/*
+ * Copyright (c) 2010, 2012-2021 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -26,6 +26,9 @@
#include <string.h>
#include <time.h>
+#if DEBUG_MEMORY
+#include "mandoc_dbg.h"
+#endif
#include "mandoc.h"
#include "roff.h"
#include "mdoc.h"
@@ -1511,6 +1514,11 @@ in_line_eoln(MACRO_PROT_ARGS)
rew_last(mdoc, n->parent);
}
+#if DEBUG_MEMORY
+ if (tok == MDOC_Dt)
+ mandoc_dbg_name(buf);
+#endif
+
if (buf[*pos] == '\0' &&
(tok == MDOC_Fd || *roff_name[tok] == '%')) {
mandoc_msg(MANDOCERR_MACRO_EMPTY,
diff --git a/mdoc_state.c b/mdoc_state.c
index 206d526f..eca7a0d4 100644
--- a/mdoc_state.c
+++ b/mdoc_state.c
@@ -1,6 +1,6 @@
/* $Id$ */
/*
- * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2014, 2015, 2017, 2021 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -23,6 +23,9 @@
#include <stdlib.h>
#include <string.h>
+#if DEBUG_MEMORY
+#include "mandoc_dbg.h"
+#endif
#include "mandoc.h"
#include "roff.h"
#include "mdoc.h"
diff --git a/tbl_html.c b/tbl_html.c
index 007cdcc1..9d5b546d 100644
--- a/tbl_html.c
+++ b/tbl_html.c
@@ -1,7 +1,7 @@
/* $Id$ */
/*
- * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014,2015,2017,2018,2021 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -24,6 +24,9 @@
#include <stdlib.h>
#include <string.h>
+#if DEBUG_MEMORY
+#include "mandoc_dbg.h"
+#endif
#include "mandoc.h"
#include "roff.h"
#include "tbl.h"
diff --git a/tbl_term.c b/tbl_term.c
index ada80041..e424e027 100644
--- a/tbl_term.c
+++ b/tbl_term.c
@@ -1,7 +1,7 @@
-/* $Id$ */
+/* $Id$ */
/*
- * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2011-2021 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -25,6 +25,9 @@
#include <stdlib.h>
#include <string.h>
+#if DEBUG_MEMORY
+#include "mandoc_dbg.h"
+#endif
#include "mandoc.h"
#include "tbl.h"
#include "out.h"