source@mandoc.bsd.lv
 help / color / mirror / Atom feed
* mandoc: support for hunting memory leaks; designed and written last
@ 2022-04-14 16:44 schwarze
  0 siblings, 0 replies; only message in thread
From: schwarze @ 2022-04-14 16:44 UTC (permalink / raw)
  To: source

Log Message:
-----------
support for hunting memory leaks;
designed and written last autumn, polished today

Modified Files:
--------------
    mandoc:
        Makefile
        Makefile.depend
        configure
        configure.local.example
        demandoc.c
        main.c
        man_macro.c
        mandoc_aux.c
        mandoc_aux.h
        mandoc_headers.3
        mandocd.c
        mandocdb.c
        mdoc_macro.c
        mdoc_state.c
        tbl_html.c
        tbl_term.c

Added Files:
-----------
    mandoc:
        mandoc_dbg.c
        mandoc_dbg.h
        mandoc_dbg_init.3

Revision Data
-------------
Index: Makefile.depend
===================================================================
RCS file: /home/cvs/mandoc/mandoc/Makefile.depend,v
retrieving revision 1.51
retrieving revision 1.52
diff -LMakefile.depend -LMakefile.depend -u -p -r1.51 -r1.52
--- Makefile.depend
+++ 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 confi
 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
--- /dev/null
+++ mandoc_dbg_init.3
@@ -0,0 +1,280 @@
+.\" $Id: mandoc_dbg_init.3,v 1.1 2022/04/14 16:43:44 schwarze Exp $
+.\"
+.\" 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: April 14 2022 $
+.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
Index: mdoc_state.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/mdoc_state.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -Lmdoc_state.c -Lmdoc_state.c -u -p -r1.17 -r1.18
--- mdoc_state.c
+++ 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"
--- /dev/null
+++ mandoc_dbg.c
@@ -0,0 +1,342 @@
+/* $Id: mandoc_dbg.c,v 1.1 2022/04/14 16:43:44 schwarze Exp $ */
+/*
+ * 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);
+}
Index: configure
===================================================================
RCS file: /home/cvs/mandoc/mandoc/configure,v
retrieving revision 1.81
retrieving revision 1.82
diff -Lconfigure -Lconfigure -u -p -r1.81 -r1.82
--- configure
+++ 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_SO
 
 # --- 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}
Index: main.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/main.c,v
retrieving revision 1.360
retrieving revision 1.361
diff -Lmain.c -Lmain.c -u -p -r1.360 -r1.361
--- main.c
+++ 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();
 }
 
Index: tbl_html.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/tbl_html.c,v
retrieving revision 1.39
retrieving revision 1.40
diff -Ltbl_html.c -Ltbl_html.c -u -p -r1.39 -r1.40
--- tbl_html.c
+++ 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"
Index: configure.local.example
===================================================================
RCS file: /home/cvs/mandoc/mandoc/configure.local.example,v
retrieving revision 1.43
retrieving revision 1.44
diff -Lconfigure.local.example -Lconfigure.local.example -u -p -r1.43 -r1.44
--- configure.local.example
+++ 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
@@ -311,6 +311,12 @@ BINM_CATMAN=mcatman		# default is "catma
 # unchanged, and nothing will be added.
 
 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
Index: mandoc_aux.h
===================================================================
RCS file: /home/cvs/mandoc/mandoc/mandoc_aux.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -Lmandoc_aux.h -Lmandoc_aux.h -u -p -r1.7 -r1.8
--- mandoc_aux.h
+++ 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
 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
Index: mandoc_aux.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/mandoc_aux.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -Lmandoc_aux.c -Lmandoc_aux.c -u -p -r1.11 -r1.12
--- mandoc_aux.c
+++ 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"
 
Index: mdoc_macro.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/mdoc_macro.c,v
retrieving revision 1.234
retrieving revision 1.235
diff -Lmdoc_macro.c -Lmdoc_macro.c -u -p -r1.234 -r1.235
--- mdoc_macro.c
+++ 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"
@@ -1510,6 +1513,11 @@ in_line_eoln(MACRO_PROT_ARGS)
 		if (n->tok == MDOC_Nm)
 			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] == '%')) {
--- /dev/null
+++ mandoc_dbg.h
@@ -0,0 +1,55 @@
+/* $Id: mandoc_dbg.h,v 1.1 2022/04/14 16:43:44 schwarze Exp $ */
+/*
+ * 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
Index: mandocd.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/mandocd.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -Lmandocd.c -Lmandocd.c -u -p -r1.12 -r1.13
--- mandocd.c
+++ 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;
 }
 
Index: tbl_term.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/tbl_term.c,v
retrieving revision 1.76
retrieving revision 1.77
diff -Ltbl_term.c -Ltbl_term.c -u -p -r1.76 -r1.77
--- tbl_term.c
+++ 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"
Index: demandoc.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/demandoc.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -Ldemandoc.c -Ldemandoc.c -u -p -r1.33 -r1.34
--- demandoc.c
+++ 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;
 }
 
Index: man_macro.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/man_macro.c,v
retrieving revision 1.146
retrieving revision 1.147
diff -Lman_macro.c -Lman_macro.c -u -p -r1.146 -r1.147
--- man_macro.c
+++ 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"
@@ -393,6 +396,11 @@ in_line_eoln(MACRO_PROT_ARGS)
 		man->flags |= ROFF_NOFILL;
 	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) {
Index: mandocdb.c
===================================================================
RCS file: /home/cvs/mandoc/mandoc/mandocdb.c,v
retrieving revision 1.270
retrieving revision 1.271
diff -Lmandocdb.c -Lmandocdb.c -u -p -r1.270 -r1.271
--- mandocdb.c
+++ 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:
Index: Makefile
===================================================================
RCS file: /home/cvs/mandoc/mandoc/Makefile,v
retrieving revision 1.540
retrieving revision 1.541
diff -LMakefile -LMakefile -u -p -r1.540 -r1.541
--- Makefile
+++ 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 \
Index: mandoc_headers.3
===================================================================
RCS file: /home/cvs/mandoc/mandoc/mandoc_headers.3,v
retrieving revision 1.34
retrieving revision 1.35
diff -Lmandoc_headers.3 -Lmandoc_headers.3 -u -p -r1.34 -r1.35
--- mandoc_headers.3
+++ 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 m
 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
--
 To unsubscribe send an email to source+unsubscribe@mandoc.bsd.lv


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-04-14 16:44 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-14 16:44 mandoc: support for hunting memory leaks; designed and written last schwarze

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).