A preliminary attempt at some documentation for mpr. It's quite haphazard at
the moment, but should be better than nothing.

Also see subdirectory "example" for an example application.

--

MPRPC

        MPRPC is an environment variable that must be set prior to calling
        function mpr(). It is set using the program mprpc. MPRPC is used
        internally to traverse and display the call chain during each
        alloc/free. If MPRPC is unset, the call to function mpr() will fail
        with return code -1.

        Note that in order for mpr to be able to traverse the call chain,
        you must compile your application with frame-pointer support. This is
        usually true, unless you compile your application with a special flag
        (e.g. using -fomit-frame-pointer with GCC), or your compiler optimizes
        away assignments to the frame-pointer register (e.g. using -O3 with
        sco5's default C compiler).

        Here is how to set MPRPC:

                % MPRPC=`mprpc a.out` MPRFI="cat >log" a.out ...

        If you're working with a single application, it may be more convenient
        to export MPRPC once, instead of having to set it each time you
        run your application:

                % export MPRPC=`mprpc a.out`
                % MPRFI="cat >log" a.out ...

        However, remember to reset MPRPC if you re-compile your application.

--

MPRFI

        MPRFI is a shell pipeline used to filter the alloc/free log
        messages. The pipeline is invoked by function mpr() using popen(3).

        MPRFI is most often used to save the messages, untouched, to a
        log file.

        e.g.
                % MPRFI="cat >log" a.out ...

        If your application does a lot of allocs/frees, it may be worthwhile
        to filter the log messages through a compression utility - this can
        significantly reduce the size of the log file.

        e.g.
                % MPRFI="gzip >log.gz" a.out

        As a special case, if MPRFI is unset, or is set to the empty string,
        mpr will not log anything. This is a convenient way to disable
        logging without having to re-compile your executable.

        e.g.
                % MPRFI= a.out ...

                or

                % unset MPRFI
                % a.out ...

--

extern int mpr()

        mpr() is an initialization function that needs to be called once to
        initialize mpr. It will be called automatically the first time your
        application calls malloc(), unless you compiled the library libmpr.a
        with the flag -UAUTOMPR (see README).

        If you compiled libmpr.a with the -UAUTOMPR flag, then you will need
        to add a call to mpr() yourself, most probably in your application's
        main() function.

        mpr() returns 0 for success, -1 if MPRPC is not set correctly, and
        -2 if it could not successfully create the MPRFI pipeline.

        e.g.
                extern int mpr();

                main()
                {
                        if (mpr() < 0)
                                exit(1);
                        ...
                }

        There is currently no provision to arbitrarily enable and disable
        logging by calling mpr() multiple times within your application. The
        added complexity doesn't seem worth it.

--

mpr [[-f | -F foo.c,bar.c] [-l]] [-Ix] [-i foo,bar,...] [-p] a.out

        mpr maps program counters of an executable (that were logged as
        part of the call chain) into corresponding function names and,
        optionally, file names and line numbers.

        Its stdin should be the the contents of the log file
        (pre-filtered through mprlk if you wish to examine memory leaks).

        e.g.
                To see memory allocation requests:

                        % mpr a.out <log

                To see memory leaks:

                        % mprlk <log | mpr a.out

        Option -i ignores functions that you do not wish to have displayed
        as part of the call chain. This is useful if you have "wrapper"
        functions around calls to malloc/realloc/free that are cluttering
        your display. The argument to option -i should be a list of
        comma-separated function names that you wish to ignore

        e.g.
                void *
                xmalloc(size_t sz)
                {
                        void *p = malloc(sz);
                        if (!p && sz)
                                abort();
                        return p;
                }

                void *
                xrealloc(void *ptr, size_t sz)
                {
                        void *p = realloc(ptr, sz);
                        if (!p && sz)
                                abort();
                        return p;
                }

                The functions xmalloc() and xrealloc() are wrappers around
                calls to malloc() and realloc() that check for allocation
                failures; however, you may not wish to see them in the call
                chain since they were not "really" responsible for the
                allocations - you'd much rather see only their callers.
                Invoking mpr with the option "-i xmalloc,xrealloc" will do
                this.

        As mentioned above, "," (comma) is the default separator for the list
        of function names to be ignored with option -i. However, if you are
        using mpr with a C++ executable, you may need to use a different
        separator since "," may appear in a C++ function's argument list. In
        such cases, option -I can be used to specify a different separator.

        e.g.
                % mpr -I"#" -i 'foo(int &, void *)#bar(int)' a.out <log

        Option -f displays file names in addition to function names. This
        only works for those parts of your application that were compiled
        with the "-g" option of the C compiler. Parts of the program that
        were not compiled with "-g" will show empty file names. (As mentioned
        in the README file, you must have the GNU debugger GDB installed in
        order to use the "-f -l" options of mpr).

        Option -l displays line numbers in addition to file names and
        function names. It should be used with option -f. Again, parts of
        the application that were not compiled with "-g" will not show line
        number information.

        e.g.
                % mpr -f -l -i xmalloc,xrealloc a.out <log

        If you use the "-f" option of mpr, then the function names
        displayed by mpr will be as you expect. However, without option
        "-f", there is a problem with duplicate function names - functions
        with identical names defined in different object files of your
        application. In this case, mpr will append additional "+" characters
        to duplicate named functions to distinguish between them (since "+"
        cannot be part of a legal C function name). The particular function
        should be obvious from the rest of the call chain displayed by mpr.

        e.g.
                bar.c:

                static void baz()
                {
                        ...
                        p = malloc(...);
                        ...
                }

                bar()
                {
                        baz();
                }

                foo.c:

                static void baz()
                {
                        ...
                        p = malloc(...);
                        ...
                }

                foo()
                {
                        baz();
                }

                Here, both files bar.c and foo.c define a function named
                baz(). If baz() happens to appear in the call chain of any
                alloc/free request, mpr will append a "+" to either foo.c's
                baz() or bar.c's baz() to distinguish between them. Exactly
                which one should be obvious from the caller of baz() which
                will be the next function in the call chain: it will be
                either foo() or bar().

                If, however, you invoke mpr with option "-f" (and foo.c
                and bar.c were compiled with "-g"), then there is no need
                to add "+" to distinguish between the two baz()'s since
                the file names (foo.c and bar.c) will do this.

        Option -F is similar to option -f, but it restricts the set of source
        files for which file names are displayed (compared to option -f which
        tries to list file names for all source files of the executable).
	(With the old version of mpr (in old/mpr), option -F is also useful
	to reduce the runtime of 'mpr -f -l').

        e.g.
                % mpr -F foo.c,bar.c -l a.out <log

                This will display file names and line numbers only for files
                foo.c and bar.c.

                % mpr -F "`cd src/dir1; echo *.c`" -l a.out <log

                This will display file names and line numbers only for the
                source files in directory src/dir1.

        Option -p may be useful if you are using mph with a C++ program.
        By default, mph will print the parameter list of each C++ function -
        this can result in unwieldy looking output. Option -p omits the
        parameter list.

--

mprcc [-c N] [-w N]

        mprcc groups memory allocation requests by call chains.
        Its stdin should be connected to the stdout of mpr.

        mprcc displays the call chain in column 1, the number of allocations
        in column 2 and the total amount allocated by the call chain (in
        bytes) in column 3.

        e.g.
                % mpr -ixmalloc,xrealloc a.out <log | mprcc

        If you wish to see memory leaks grouped by call chains, use
        the mprlk filter prior to invoking mpr. This will then show the
        call chains responsible for leaks in column 1, the number of
        allocations that were leaked in column 2 and the total amount that
        was leaked by each call chain in column 3.

        e.g.
                % mprlk <log | mpr -ixmalloc,xrealloc a.out | mprcc

        Option -c sets the length of the call chains. By default it is set
        to 1, so you get allocation requests grouped only by the last
        function to call the memory allocation routines. Setting it to a
        higher value may make it easier for you to track down functions
        responsible for allocations by showing longer call chains leading
        up to the allocation requests.

        e.g. To see memory allocation requests grouped by call chains of
             length 9:

                % mpr -f -l -ixmalloc,xrealloc a.out <log | mprcc -c9

        Note that the "granularity" of the call chains that mprcc uses to
        group allocation requests is determined by how you invoke mpr.
        If you invoke mpr without options "-f -l", then the granularity
        is per function; if you invoke mpr with options "-f -l" then the
        granularity is per function and per line.

        e.g.
                foo()
                {
                        p1 = malloc(...);
                        p2 = malloc(...);
                        ...
                }

                The calls to malloc() in function foo() will be lumped
                together in one call chain if invoked as

                        mpr a.out <log | mprcc

                The calls to malloc() in function foo() will appear in
                different call chains if invoked as

                        mpr -f -l a.out <log | mprcc

        Option -w sets the width of the display. By default, it is set
        to 80. If you wish to see more of the call chain, but doing so
        results in funny looking output (since mprcc tries to line up the
        last 2 columns), then use option -w to increase the width.

        e.g. To see the entire call chain on a 132 column wide output:

                % mpr -f -l a.out <log | mprcc -c99 -w132

--

mprlk

        mprlk identifies those allocation requests that lead to memory leaks.
        Its stdin should be the contents of the log file.

        It is used most often as a pre-filter for mpr or mprsz.

        e.g.
                To examine memory leaks:

                        % mprlk <log | mpr -ixmalloc,xrealloc a.out

                To examine memory leaks grouped by function call chains of
                length 3:

                        % mprlk <log | mpr a.out | mprcc -c3

                To examine memory leaks grouped by size:

                        % mprlk <log | mprsz

--

mprhi [-c N] [-w N] [-b N]

        mprhi displays a histogram of the total amount of memory allocated
        by your application. Its stdin should be the contents of the log
        file.

        mprhi uses '*' to represent a block of allocated memory with a
        default block size of 1024 bytes. Option -b can be used to change
        the default block size.

        Option -c sets the length of the call chains to display alongside
        the histogram. It is set to 0 by default, so you don't see the
        call chains.

        e.g. To see a histogram with a block size of 2048:

                % mprhi -b2048 <log

        If you wish to see call chains alongside the histogram, then use
        option -c to set the length of the call chains to display. In
        this case you should pre-filter the log file through program mpr.

        e.g. To see a histogram with a block size of 2048 and function
             name, file name and line number call chains of length 5:

                % mpr -f -l a.out <log | mprhi -b2048 -c5

        Note that without option -c, mprhi will not show adjacent histogram
        entries with the same length, i.e. the output is "compressed" to show
        only changes in the amount of allocated memory in "granularities" of
        the block size. This, coupled with option -b for showing a bigger
        block size, can give you a quick idea of your application's memory
        alloc/free history, without having to wade through an enormous amount
        of output, especially if your application does thousands of
        allocs/frees.

        However, with option -c, mprhi is much more verbose and shows one
        line for *every* alloc/free. This can help you pin down the call
        chains (or sequence of call chains) responsible for the biggest
        allocs and frees, and also precisely when they did so.

        Option -w sets the width of the display. By default, it is set to
        80. It is only useful when you use option -c to show call chains
        alongside the histogram. In this case, mprhi tries to right justify
        the call chains to line up in column 80 (the histogram is easier to
        visualize by separating it from the call chain). However, if the
        output scrolls off the end or if the call chains are too close to
        the histogram, then you can either (1) use option -b to increase the
        block size or (2) decrease the length of the call chains or (3) use
        option -w to increase the width of the display.

--

mprsz

        mprsz groups memory allocation requests by size.

        Its stdin should be the the contents of the log file
        (pre-filtered through mprlk if you wish to examine memory leaks).
        
        The output consists of 2 columns: column 1 is the size and column 2
        is the number of allocations of that size.

        e.g.
                To examine memory allocation requests grouped by size:

                        % mprsz <log

                To examine memory allocation leaks grouped by size:

                        % mprlk <log | mprsz

--

extern int mcheck(void (*abortfunc)())

        Function mcheck() is not part of mpr - it's actually part of the
        GNU malloc library that mpr uses. However, it can be used to catch a
        class of memory violation errors that mpr does not address so it is
        described here. For convenience, a copy of mcheck() is included in
        library libmpr.a.

        Calling mcheck() enables checks for the following common memory
        violations:
                - memory clobbered before allocated block
                - memory clobbered past end of allocated block
                - block freed twice

        By default, mcheck() is called automatically the first time your
        application calls malloc(), unless you compiled the library libmpr.a
        with the flag -UAUTOMCHECK (see README).

        If you compiled libmpr.a with the -UAUTOMCHECK flag, but you wish to
        have the malloc library perform the above memory checks, then you
        will need to add a call to mcheck() yourself, most probably in your
        application's main() function.

        mcheck() returns 0 for success and -1 for failure. It will fail if
        called after malloc() or realloc() have already been called. Hence,
        it should be one of the first functions your application calls on
        startup.

        e.g.
                main()
                {
                        if (mcheck(0) < 0)
                                exit(1);
                        ...
                }

        The argument to mcheck() is a function to be called when the malloc
        library detects a memory violation. Setting it to 0 uses a default
        function which prints a message on stderr and then calls abort(3).

--

Syntax of the log file

        You do not need to understand the syntax of the log file described
        here to use mpr. This information is provided for the curious and
        for those who would like to write new tools to process the log file.

        The log file consists of one line for each memory alloc/free request.
        The first field of each line identifies the type of request
        (malloc/free/realloc); the last few fields indicate the parameters to
        the particular request; the fields in between are the program counters
        at the time of the request (the "call chain") - the program counters
        are used to precisely track the source of each request. This is much
        more useful than conventional methods of tracking only the immediate
        caller of each request (e.g. using __FILE__ and __LINE__).

        ()  -> grouping
        *   -> repeat 0 or more times
        +   -> repeat 1 or more times
        "x" -> literal x

        <file>       ::= <line>*
        <line>       ::= <mline> | <fline> | <rline>
        <mline>      ::= "m:"<callchain><sz>":"<addr>            // malloc
        <fline>      ::= "f:"<callchain><addr>                   // free
        <rline>      ::= "r:"<callchain><sz>":"<oaddr>":"<naddr> // realloc
        <callchain>  ::= (<pc>":")+
        <pc>         ::= <num>
        <sz>         ::= <num>
        <addr>       ::= <num>
        <oaddr>      ::= <num>
        <naddr>      ::= <num>
        <num>        ::= [0-9][0-9]*                             // decimal

        Note that for malloc, <addr> may be 0 if the allocation fails and
        for realloc, <newaddr> may be 0 if the re-allocation fails.

        All numbers are stored in decimal format, rather than hexadecimal,
        to make it easier to process them using awk(1).

--
