170 lines
6.3 KiB
Plaintext
170 lines
6.3 KiB
Plaintext
Using memwatch
|
|
==============
|
|
|
|
What is it?
|
|
|
|
Memwatch is primarily a memory leak detector for C. Besides
|
|
detecting leaks, it can do a bunch of other stuff, but lets
|
|
stay to the basics. If you _really_ want to know all the
|
|
gory details, you should check out the header file,
|
|
memwatch.h, and the source code. It's actually got some
|
|
comments! (Whoa, what a concept!)
|
|
|
|
How do I get the latest version?
|
|
|
|
http://www.link-data.com/sourcecode.html
|
|
ftp://ftp.link-data.com/pub/memwatch/
|
|
|
|
How does it work?
|
|
|
|
Using the C preprocessor, memwatch replaces all your
|
|
programs calls to ANSI C memory allocation functions with
|
|
calls to it's own functions, which keeps a record of all
|
|
allocations.
|
|
|
|
Memwatch is very unobtrusive; unless the define MEMWATCH is
|
|
defined, memwatch removes all traces of itself from the
|
|
code (using the preprocessor).
|
|
|
|
Memwatch normally writes it's data to the file
|
|
memwatch.log, but this can be overridden; see the section
|
|
on I/O, later.
|
|
|
|
Initialization and cleanup
|
|
|
|
In order to do it's work in a timely fashion, memwatch
|
|
needs to do some startup and cleanup work. mwInit()
|
|
initializes memwatch and mwTerm() terminates it. Memwatch
|
|
can auto-initialize, and will do so if you don't call
|
|
mwInit() yourself. If this is the case, memwatch will use
|
|
atexit() to register mwTerm() to the atexit-queue.
|
|
|
|
The auto-init technique has a caveat; if you are using
|
|
atexit() yourself to do cleanup work, memwatch may
|
|
terminate before your program is done. To be on the safe
|
|
side, use mwInit() and mwTerm().
|
|
|
|
mwInit() and mwTerm() is nestable, so you can call mwInit()
|
|
several times, requiring mwTerm() to be called an equal
|
|
number of times to terminate memwatch.
|
|
|
|
In case of the program aborting in a controlled way, you
|
|
may want to call mwAbort() instead of mwTerm(). mwAbort()
|
|
will terminate memwatch even if there are outstanding calls
|
|
to mwTerm().
|
|
|
|
I/O operations
|
|
|
|
During normal operations, memwatch creates a file named
|
|
memwatch.log. Sometimes, memwatch.log can't be created;
|
|
then memwatch tries to create files name memwatNN.log,
|
|
where NN is between 01 and 99. If that fails, no log will
|
|
be produced.
|
|
|
|
If you can't use a file log, or don't want to, no worry.
|
|
Just call mwSetOutFunc() with the address of a "void
|
|
func(int c)" function, and all output will be directed
|
|
there, character by character.
|
|
|
|
Memwatch also has an Abort/Retry/Ignore handler that is
|
|
used when an ASSERT or VERIFY fails. The default handler
|
|
does no I/O, but automatically aborts the program. You can
|
|
use any handler you want; just send the address of a "int
|
|
func(const char*)" to mwSetAriFunc(). For more details on
|
|
that, see memwatch.h.
|
|
|
|
TRACE/ASSERT/VERIFY macros
|
|
|
|
Memwatch defines (if not already defined) the macros TRACE,
|
|
ASSERT and VERIFY. If you are already using macros with
|
|
these names, memwatch 2.61 and later will not override
|
|
them. Memwatch 2.61 and later will also always define the
|
|
macros mwTRACE, mwASSERT and mwVERIFY, so you can use these
|
|
to make sure you're talking to memwatch. Versions previous
|
|
to 2.61 will OVERRIDE TRACE, ASSERT and VERIFY.
|
|
|
|
To make sure that existing TRACE, ASSERT and VERIFY macros
|
|
are preserved, you can define MW_NOTRACE, MW_NOASSERT and
|
|
MW_NOVERIFY. All versions of memwatch will abide by these.
|
|
|
|
Stress-testing the application
|
|
|
|
You can simulate low-memory conditions using mwLimit().
|
|
mwLimit() takes the maximum number of bytes to be
|
|
allocated; when the limit is hit, allocation requests will
|
|
fail, and a "limit" message will be logged.
|
|
|
|
If you hit a real low-memory situation, memwatch logs that
|
|
too. Memwatch itself has some reserve memory tucked away so
|
|
it should continue running even in the worst conditions.
|
|
|
|
Hunting down wild writes and other Nasty Things
|
|
|
|
Wild writes are usually caused by using pointers that arent
|
|
initialized, or that were initialized, but then the memory
|
|
they points to is moved or freed. The best way to avoid
|
|
these kind of problems is to ALWAYS initialize pointers to
|
|
NULL, and after freeing a memory buffer, setting all
|
|
pointers that pointed to it to NULL.
|
|
|
|
To aid in tracking down uninitialized pointers memwatch
|
|
zaps all memory with certain values. Recently allocated
|
|
memory (unless calloc'd, of course), contains 0xFE.
|
|
Recently freed memory contains 0xFD. So if your program
|
|
crashes when using memwatch and not without memwatch, it's
|
|
most likely because you are not initializing your allocated
|
|
buffers, or using the buffers after they've been freed.
|
|
|
|
In the event that a wild pointer should damage memwatch's
|
|
internal data structures, memwatch employs checksums,
|
|
multiple copies of some values, and can also repair it's
|
|
own data structures.
|
|
|
|
If you are a paranoid person, and as programmer you should
|
|
be, you can use memwatch's mwIsReadAddr() and
|
|
mwIsSafeAddr() functions to check the accessibility of
|
|
memory. These are implemented for both ANSI C systems and
|
|
Win32 systems. Just put an mwASSERT() around the check and
|
|
forget about it.
|
|
|
|
Can I help?
|
|
|
|
Well, sure. For instance, I like memwatch to compile
|
|
without any warnings or errors. If you are using an ANSI C
|
|
compliant compiler, and are getting warnings or errors,
|
|
please mail me the details and instructions on how to fix
|
|
them, if you can.
|
|
|
|
Another thing you can do if you decide to use memwatch is
|
|
to mail me the name of the project(s) (and URL, if any),
|
|
hardware and operating system, compiler and what user
|
|
(organization). I will then post this info on the list of
|
|
memwatch users.
|
|
(http://www.link-data.com/memwatchusers.html)
|
|
|
|
Top five problems using memwatch
|
|
|
|
5. Passed a non-memwatch allocated pointer to memwatch's
|
|
free(). Symtom: Causes an erroneous "WILD free" log
|
|
entry to appear. Cure: Either include memwatch.h for
|
|
the file that allocates, or use mwFree_() to free it.
|
|
|
|
4. Relied on auto-initialization when using atexit().
|
|
Symptom: Causes incorrect "unfreed" and "WILD free"
|
|
messages. Cure: Use mwInit() and mwTerm().
|
|
|
|
3. Forgot to include memwatch.h in all files. Symptom:
|
|
Tends to generate "WILD free" and "unfreed" messages.
|
|
Cure: Make sure to include memwatch.h!
|
|
|
|
2. No write permissions in currect directory. Symptom:
|
|
Seems like memwatch 'just aint working'. Cure: Use
|
|
mwSetOutFunc() to redirect output.
|
|
|
|
...and the number one problem is...
|
|
|
|
1. Didn't define MEMWATCH when compiling. Symptom:
|
|
Memwatch dutifully disables itself. Cure: Try adding
|
|
-DMEMWATCH to the command line.
|
|
|