This is a tool to interpet and show information generated b y my experimental
changes to OpenBSD's malloc
.
Please note that this will only work on a current OpenBSD source tree. At the moment of writing this that means newer than Oct 12 2020.
First step is to apply the diff to src/lib/libc/stdlib/malloc.c
, recompile and install libc:
$ cd /usr/src/lib/libc
$ patch < malloc.diff
$ make obj
$ make
$ doas make install
After this, malloc
will generate utrace(2)
records when instructed to do so.
These ulog records are written to a file on regular exit of your program
The trace file is generated by running your program with
MALLOC_OPTIONS=DT ktrace -tu program
The MALLOC_OPTIONS
instruct malloc
to write the infomration and the ktrace
invocation
takes care of catching the utrace records and writing themn to a file, by
default called ktrace.out
. After that you can use the mdump
program to
display the information.
Remeber to build your program and libraries with debug information.
On OpenBSD system libraries are installed with debug info, so that's
convenient. To get deeper stack trace information on leaks use
more T
's. Note that the mechanism used (__builtin_return_address
)
is not guaranteed to work for more than one T
, some executables
on some platforms will crash when too many T
's are used.
To compile mdump
, you'll need to elftoolchain
package.
# doas pkg_add elftoolchain
$ make obj
$ make
$ doas make install
This wil install the tool and man page into /usr/local
.
Basic usage is
mdump
to show leak info and
mdump -D
to show a dump of malloc
's internal state at program exit.
To produce readable stack traces, the program and its libraries should be
compiled with debug information, typically -g
.
Statically linked programs must be compiled with the
-nopie
option.
High optimization levels can produce debug information that
mdump
cannot interpret.
Setuid or setgid processes can only be traced by the superuser.
Forking programs can be traced by using the -i
flag to ktrace(1)
, but
selecting a single process is needed when running mdump
on the trace file
generated.
The program:
#include <stdio.h>
#include <stdlib.h>
char *p;
void f(void)
{
int i;
void *s[64];
p = realloc(p, 4096);
for (i = 0; i < 64; i++)
s[i] = malloc(4096);
p = realloc(p, 8192);
for (i = 1; i < 64; i++)
free(s[i]);
}
int main()
{
int i;
p = malloc(100);
for (i = 0; i < 10; i++)
f();
printf("done\n");
}
Compile, run and show leaks:
$ cc -g x.c
$ MALLOC_OPTIONS=DTTTT ktrace -tu ./a.out
done
$ mdump
Leak sum=40960 count=10 avg=4096
f at /home/otto/x.c:13
main at /home/otto/x.c:25
?? at ??:0
Leak sum=8192 count=1 avg=8192
f at /home/otto/x.c:14
main at /home/otto/x.c:25
?? at ??:0
Leak sum=65536 count=1 avg=65536
__smakebuf at /usr/src/lib/libc/stdio/makebuf.c:62
__swsetup at /usr/src/lib/libc/stdio/wsetup.c:75
__vfprintf at /usr/src/lib/libc/stdio/vfprintf.c:460
vfprintf at /usr/src/lib/libc/stdio/vfprintf.c:264
$
While running (and if enabled by MALLOC_OPTIONS
) malloc
stores backtrace information for allocations.
Per stackframe the return address is recorded.
Any allocation equal or larger than a page is tracked. For smaller allocations,
only a sample is recorded. This means that not all leaks will be shown for smaller allocations.
At program exit, malloc will check which allocations are not freed,
translate the addresses into library/executable plus offset information
using dladdr(3)
and construct utrace records to send out.
The mdump
program then takes this information and translates the library
plus offset information into function name + file + linenumber information using
the debug information embedded in the program and its libraries.