Initial source commit

This commit is contained in:
Tony Bark 2025-10-03 02:19:59 -04:00
commit f1384c11ee
335 changed files with 52715 additions and 0 deletions

View file

@ -0,0 +1,746 @@
Fortify - A fortified memory allocation shell for C and C++
-----------------------------------------------------------
written by Simon P. Bullen
Version 2.2, 1 November 1995
This software is not public domain. All material in
this archive is (C) Copyright 1995 Simon P. Bullen. The
software is freely distributable, with the condition that
no more than a nominal fee is charged for media.
Everything in this distribution must be kept together, in
original, unmodified form.
The software may be modified for your own personal use,
but modified files may not be distributed.
The material is provided "as is" without warranty of
any kind. The author accepts no responsibility for damage
caused by this software.
This software may not be used in any way by Microsoft
Corporation or its subsidiaries, or current employees of
Microsoft Corporation or its subsidiaries.
This software may not be used for the construction,
development, production, or testing of weapon systems of
any kind.
This software may not be used for the construction,
development, production, or use of plants/installations
which include the processing of radioactive/fissionable
material.
If you use this software at all, I'd love to hear from you. All
questions, criticisms, suggestions, praise and postcards are most welcome.
email: sbullen@cybergraphic.com.au
snail: Simon P. Bullen
PO BOX 12138
A'Beckett St.
Melbourne 3000
Australia
CONTENTS
--------
Your archive should have the following files:
fortify.doc - This file. Read it first
fortify.cxx - Actual code. Rename to fortify.c for C only development
fortify.h - Each file in your program should #include this
ufortify.h - User configuration file. Here is where you set your options
test.c - C tester
test2.cxx - C++ tester
makefile - Makefile for GCC to build the testers.
OVERVIEW
--------
Fortify is a (fortified) shell for memory allocations. It supports
malloc/calloc/realloc/strdup/free and new/delete. It traps memory leaks,
writes beyond and before memory blocks, writes to freed memory, free twice,
freeing memory never allocated, and removes all randomness from reading
uninitialized or free memory.
It is a descendant of a library I wrote way back in 1990 called SafeMem.
It can be adapted to most memory management functions; the original version
of SafeMem worked only with the Amiga's AllocMem/FreeMem.
Fortify works by allocating extra space on each block. This includes a
private header (which is used to keep track of Fortify's list of allocated
memory), and two "fortification zones" or "sentinels" (which are used to
detect writing outside the bound of the user's memory).
PORTABILITY
-----------
Fortify is intended to be a highly portable tool. It should work quite
happily in any vaguely ANSI C/C++ environment, on any operating system or
processor architecture without modification.
It does not, however, support bizarre memory models. A pointer is a
pointer is a pointer. I have no intention of ever supporting any of the
non-flat memory models used by some pretend operating systems.
Fortify INSTALLATION (C++)
--------------------------
To install Fortify, each source file will need to #include "fortify.h".
To enable Fortify, define the symbol FORTIFY. The makefile is the best
place to do this. The symbol FORTIFY will need to be defined for every
module, this includes "fortify.c" or "fortify.cxx".
If FORTIFY is not defined, it will compile away to nothing - it will be
as if Fortify was never installed at all (you should recompile all
sourcecode with Fortify disabled for a release of your software).
If you do not have stdout available, you may wish to set an alternative
output function. See Fortify_SetOutputFunc() and
FORTIFY_AUTOMATIC_LOGFILE, below.
Your program will need to link in "fortify.cxx" (after it's been
compiled, of course).
You may also need to adjust some options in "ufortify.h" before Fortify
will work correctly. See "Compile Time Customizations", below.
Fortify INSTALLATION (C)
------------------------
To install Fortify into a C program, simply rename "fortify.cxx" to
"fortify.c", and follow the C++ instructions above. "fortify.cxx" will
compile quite happily as C, as all the core Fortify code is C, and the C++
specific code is surrounded by #ifdef __cplusplus/#endif.
You may also need to adjust some options in "ufortify.h" before Fortify
will work correctly. See "Compile Time Customizations", below.
COMPILE TIME CUSTOMIZATIONS
---------------------------
The file "ufortify.h" contains many #defines that you can use to
customize Fortify's behavior. Fortify will do it's best to provide
sensible defaults for most of these options, if for some reason they are
not present in "ufortify.h".
Most of the options are merely user preferences that you may wish to
change or turn on only when you are trying to track down a particularly
nasty bug. There are a couple of configuration items, however, that will
need to be set correctly for Fortify to compile on your particular system.
When it can, Fortify will detect a compiler and set the appropriate
options.
Ideally, Fortify would be able to be built under all compilers without
modification, but due to differences in compilers and the devious nature of
some of the stuff Fortify has to acheive, this is sadly not possible. It
is my aim to make Fortify automatically configure itself for all major
compilers out there, so if you need to adjust some of these
compiler-specific options to get Fortify to work under your system, I would
greatly appreciate the code fragment that detects your compiler and sets up
the correct options (these live in "fortify.h" - search for __GNUG__ for an
example).
#define FORTIFY_PROVIDE_ARRAY_NEW
#define FORTIFY_PROVIDE_ARRAY_DELETE
Some C++ compilers have separate operators for newing and deleting
arrays. If your compiler does, you will need to define this symbol. If
you are unsure, turn them both on. Your program won't compile or link if
they should be off (providing your test code actually news and deletes an
array). GCC 2.6.3 and Borland C++ 4.5 both need these symbols. Microsoft
C++ 1.5 and SAS 6.5 C++ both don't. Fortify will automatically turn on
this option for GCC and Borland..
If both array new and array delete are enabled, Fortify will be able to
ensure that you always use the correct form of delete, and will issue an
error message if you use the wrong one.
Fortify: Incorrect deallocator "delete[]" detected at test2.cxx.17
(0x7438ac2,1,test2.cxx.13) was allocated with "new"
#define FORTIFY_STRDUP
strdup() is a non-ANSI function that is essentially a malloc() and a
strcpy(). If you use this function, you will need to enable
FORTIFY_STRDUP, so that the memory you get back from strdup() will be
allocated by Fortify.
If you don't do this, Fortify will generate an error message when you
attempt to free the memory.
#define FORTIFY_NO_PERCENT_P
Some non-ANSI versions of sprintf() do not recognize "%p" as a valid
conversion specification. Fortify uses "%p" to output all of it's
pointers. If your sprintf() does not support "%p", define
FORTIFY_NO_PERCENT_P, and Fortify will use "%lx" instead.
#define FORTIFY_STORAGE
You can use this to apply a storage type to all Fortify's exportable
functions. If you are putting Fortify in an export library for example,
you may need to put __export here, for example.
#define FORTIFY_BEFORE_SIZE 32
#define FORTIFY_BEFORE_VALUE 0xA3
#define FORTIFY_AFTER_SIZE 32
#define FORTIFY_AFTER_VALUE 0xA5
These values define how much "fortification" is placed around each
memory block you allocate. Fortify will place FORTIFY_BEFORE_SIZE bytes
worth of memory right before your memory block, and FORTIFY_AFTER_SIZE
bytes after your memory block, and these will be initialized to
FORTIFY_BEFORE_VALUE and FORTIFY_AFTER_VALUE respectively. If your program
then accidentally writes into either of these regions, Fortify will detect
this, and issue an error message.
If you don't want these fortifications to be allocated, specify a size
of 0. Note that the value parameters are 8 bits.
Fortify: Underwrite detected before block (0x743b462,123,test.c.14) at
test.c.16
Memory integrity was last verified at test.c.14
Address Offset Data (a3)
...64 bytes skipped...
0x743b45a 64 a3a3a3a3 a3a3a341 "£££££££A"
#define FORTIFY_ALIGNMENT sizeof(double)
Many processors require data objects to have a specific alignment in
memory. The memory allocators on these machines will only return memory
blocks aligned correctly for the largest of these requirements. Fortify,
however, adds some magic cookies to the start of it's memory blocks.
Fortify will guarantee that the amount of memory it adds to the front of a
memory block will be a multiple of FORTIFY_ALIGNMENT (It does this by
increasing the FORTIFY_BEFORE_SIZE, after taking into account the size of
it's private header). The default of sizeof(double) should work on most
systems.
#define FORTIFY_FILL_ON_ALLOCATE
#define FORTIFY_FILL_ON_ALLOCATE_VALUE 0xA7
Programs often rely on uninitialized memory being certain values
(usually 0). If you define FORTIFY_FILL_ON_ALLOCATE, all memory that you
allocate will be initialized to FORTIFY_FILL_ON_ALLOCATE_VALUE, which you
should define to be some horrid value (definitely NOT 0). This will
encourage all code that relies on uninitialized memory to behave rather
differently when Fortify is running. If you ever see a pointer that is
0xA7A7A7A7, you can be certain that it is uninitialized.
#define FORTIFY_FILL_ON_DEALLOCATE
#define FORTIFY_FILL_ON_DEALLOCATE_VALUE 0xA9
Programmers often try to use memory after they've freed it, which can
sometimes work (so long as nobody else has modified the memory before you
look at it), but is incredibly dangerous and definitely bad practice. If
FORTIFY_FILL_ON_DEALLOCATE is defined, all memory you free will be stomped
out with FORTIFY_FILL_ON_DEALLOCATE_VALUE, which ensures that any attempt
to read freed memory will give incorrect results. If you ever see a
pointer that is 0xA9A9A9A9, it definately came from free'd memory.
#define FORTIFY_FILL_ON_CORRUPTION
Fortify will never free memory that has had it's fortifications
overwritten. This is so that you have ample opportunity to examine this
memory in a debugger. If the memory remains corrupted, Fortify will report
this each time it does a check of memory, which can be annoying (but
useful, if the program continues to modify the memory). If you define
FORTIFY_FILL_ON_CORRUPTION, Fortify will re-initialize a corrupted
fortification after it issues it's message. Fortify will then no longer
complain about this memory, until it is corrupted again.
This has the extra advantage that software that tries to read this
memory will probably break.
#define FORTIFY_CHECK_ALL_MEMORY_ON_ALLOCATE
#define FORTIFY_CHECK_ALL_MEMORY_ON_DEALLOCATE
CHECK_ALL_MEMORY_ON... means that for every single memory allocation
or deallocation, every single block of memory will be checked. This can
slow down programs considerably if you have a large number of blocks
allocated. You would normally only need to turn this on if you are trying
to pinpoint where a corruption was occurring.
A block of memory is always checked when it is freed, so if
CHECK_ALL... isn't turned on, corruptions will still be detected,
eventually.
You can also force Fortify to check all memory with a call to
Fortify_CheckAllMemory(). If you have a memory corruption you can't find,
sprinkling these through the suspect code will help narrow it down. When
you get a message regarding a corruption, part of the output will be a line
that says "Memory integrity was last verified at file.line". This
corresponds to when the "CheckAllMemory" operation was last successfully
performed, either automatically, or by an explicit call to
Fortify_CheckAllMemory().
#define FORTIFY_PARANOID_DEALLOCATE
FORTIFY_PARANOID_DEALLOCATE causes Fortify to traverse the memory list
to ensure the memory you are about to free was really allocated. This
option is recommended in a protected memory operating system. If a free()
of a garbage pointer is attempted, Fortify will generate a segmentation
fault when it attempts to checksum the memory pointed to by the garbage
pointer. If, however, FORTIFY_PARANOID_DELETE is on, Fortify will be able
to determine that the pointer is garbage without crashing the system, and
will be able to generate an error message.
Note that in C++, however, if you try to "delete" a garbage pointer,
you will most likely crash before Fortify gets a look in. When you do a
"delete" in C++, the first thing that happens is the object's destructor
gets called. Depending on what the destructor actually does, it may crash
due to having a bogus "this". The memory isn't freed until AFTER the
destructor, which is when Fortify would be able to detect the invalid
pointer (too late). If you are having problems with a weird crash in a
destructor, you may wish to add Fortify_CheckPointer(this) to the start of
the destructor, but be warned that Fortify will generate an error message
if you destruct an object that was created on the stack.
FORTIFY_PARANOID_DEALLOCATE adds a small amount of overhead to freeing
memory (though not nearly as much as
FORTIFY_CHECK_ALL_MEMORY_ON_DEALLOCATE). Paranoid mode would be quicker if
Fortify didn't use a naive linked list to store it's memory on (see TO DO
LIST, below).
#define FORTIFY_WARN_ON_ZERO_MALLOC
#define FORTIFY_FAIL_ON_ZERO_MALLOC
In some implementations, a malloc(0) will always fail (This is
non-ANSI). If Fortify is turned on, malloc(0) won't actually reach malloc
as a malloc(0) - Fortify adds fortifications, which means it won't be a
zero byte allocation anymore, and will probably succeed.
Thus it is possible to write code that would work if Fortify was turned
on, but fail when Fortify is turned off. If the version of malloc() you
are using does not allow malloc(0) to succeed, you must enable
FORTIFY_FAIL_ON_ZERO_MALLOC to enable your program to function properly
with Fortify turned on.
#define FORTIFY_WARN_ON_ALLOCATE_FAIL
This causes a debug to be issued whenever an allocation fails. This
can be very useful when identifying the cause for a crash.
#define FORTIFY_WARN_ON_FALSE_FAIL
A debug will be issued when an allocation is "false failed". See
Fortify_SetAllocateFailRate() and Fortify_SetAllocationLimit() for more
information.
Fortify: A "malloc()" of 128 bytes "false failed" at test.c.27
#define FORTIFY_WARN_ON_SIZE_T_OVERFLOW
This causes Fortify to check for breaking the size_t limit. This is a
problem in 16-bit applications where breaking the 16 bit limit is
reasonably likely.
The problem is that Fortify adds a small amount of overhead to a memory
block; so in a 16-bit size_t environment, if you tried to allocate 64K,
Fortify would make that block bigger than 64K and your allocation would
fail due to the presence of Fortify. With size_t being 32 bits for all
environments worth programming in, this problem is extremely unlikely
(Unless you plan to allocate 4 gigabytes).
#define FORTIFY_AUTOMATIC_LOG_FILE
#define FORTIFY_LOG_FILENAME "fortify.log"
#define FORTIFY_FIRST_ERROR_FUNCTION cout << "\a\a\aOh Dear!\n"
If FORTIFY_AUTOMATIC_LOG_FILE is defined (C++ only), then Fortify will
output to a log file. It will also automatically call Fortify_EnterScope()
at program initialization, and Fortify_LeaveScope() at program termination.
If Fortify generated no output, the log file will not be altered.
FORTIFY_FIRST_ERROR_FUNCTION will be called upon generation of the
first Fortify message, so that the user can tell if a Fortify report has
been generated. Otherwise, Fortify would quietly write all this useful
stuff out to the log file, and no-one would know to look there!
You don't need to use Fortify_SetOutputFunc() at all if you're using
the automatic log file.
#define FORTIFY_LOCK()
#define FORTIFY_UNLOCK()
In a multi-threaded environment, we need to arbitrate access to the
fortify memory list. This is what FORTIFY_LOCK() and FORTIFY_UNLOCK() are
used for. The calls to FORTIFY_LOCK() and FORTIFY_UNLOCK() must nest. If
no two threads/tasks/processes will be using the same Fortify at the same
time, then FORTIFY_LOCK() and FORTIFY_UNLOCK() can safely be #defined away
to nothing.
#define FORTIFY_TRACK_DEALLOCATED_MEMORY
#define FORTIFY_DEALLOCATED_MEMORY_LIMIT 1048576
#define FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY
#define FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY
If FORTIFY_TRACK_DEALLOCATED_MEMORY is enabled, Fortify won't actually
free memory when you deallocate it. Freed memory goes onto the
"deallocated" list, where Fortify will keep an eye on it. If you write to
the memory after it's been freed, Fortify will be able to tell. If you
attempt to free memory twice, Fortify will be able to tell you for certain,
rather than just suspect.
Deallocated memory tracking is only active if you've entered at least
one level of Fortify scope. This is so that Fortify will be able to
actually free the deallocated memory when the scope is left.
Fortify: Write to deallocated block (0x75c4502,126,test.c.21)
detected at test.c.40
Memory block was deallocated by "realloc()" at test.c.34
Memory integrity was last verified at test.c.35
Address Offset Data (a9)
0x75c4502 0 58a9a9a9 a9a9a9a9 a9a9a9a9 a9a9a9a9 "X"
...110 bytes skipped...
FORTIFY_DEALLOCATED_MEMORY_LIMIT, if defined, is the maximum amount of
memory that Fortify should keep on it's deallocated list. If you
deallocate some memory, and the total amount of deallocated memory being
tracked is greater than this number, then Fortify will throw away (actually
free) the oldest chunks of memory, until it's under this threshold. You
don't have to worry about running out of memory if this value is set high,
Fortify will discard deallocated memory in preference to letting an
allocation fail. The main reason for this option is to restrict the amount
of time spent checking freed memory for corruption.
FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY will cause Fortify to
issue a warning whenever it has to free some deallocated memory when it
didn't really want to. If an allocation was going to fail, or if it was
over the FORTIFY_DEALLOCATED_MEMORY_LIMIT, and it had to free some memory,
then a warning would be issued. Freeing deallocated memory when leaving a
Fortify scope does not cause a warning.
FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY is useful when
you are trying to track down a particular write to some deallocated memory.
Fortify will list the details of all blocks being discarded, so that you
can tell if the block you are interested in is still being watched by
Fortify.
Note that Fortify will always perform one last check on the memory
before it discards it, so you can be sure that all memory being discarded
is corruption free.
Fortify: Warning - Discarding deallocated memory at test.c.46
(0x743e8b2,126,test.c.22,test.c.35)
(0x743f0b2,456,test.c.32,test.c.46)
#define FORTIFY_DELETE_STACK_SIZE 256
Due to the way that delete works, Fortify has to maintain it's own
stack of delete source-code information. For simplicity, this has been
implemented in a static array. The size of this stack reflects the number
of levels of recursion that Fortify will be able to track during deletes
(ie deletes being called from destructors called from a delete call). If
this recursion limit is exceeded, Fortify will simply output the deepest
sourcecode level known. I think 256 levels should be ample.
typedef void (*Fortify_NewHandlerFunc)(void);
#define FORTIFY_NEW_HANDLER_FUNC Fortify_NewHandlerFunc
By default, Fortify will use the standard new handler function
prototype. However, some non-standard compiler implementations (such as
Microsoft Visual C++ 2.0) use non-standard prototypes for their new
handlers. FORTIFY_NEW_HANDLER_FUNC is the type that Fortify uses for it's
new handler function pointer. Note that Fortify won't pass any parameters
to the new handler, so changing this is probably quite useless at this
point in time.
RUN TIME CONTROL
----------------
Fortify can also be controlled at run time with a few special
functions, which compile away to nothing if FORTIFY isn't defined.
Fortify_Disable() - Fortify can be disabled at run-time. Previously
this would only work if no memory was allocated at the time
Fortify_Disable() was called, but in C++ environments, this is almost never
possible. After Fortify_Disable() has been called, all new allocations
will not be Fortified. Deallocations will be treated slightly differently.
Fortify will check to see if it allocated the memory, and if so, free it.
If not, it will pass it on to free(). Fortify_Disable() is useful to turn
off Fortify if the overhead or bugs being induced by Fortify are getting in
the way. To correctly remove Fortify for software release, you must
compile it out.
Note that once you've called Fortify_Disable(), there is no way to turn
it back on again.
Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output) - Sets the function
used to output all error and diagnostic messages by Fortify. The output
function takes a single (const char *) argument, and must be able to handle
newlines. The default output function is a printf() to stdout, unless you
are using FORTIFY_AUTOMATIC_LOG_FILE, where the default is to output to the
log file. The function returns the old pointer.
Fortify_SetAllocateFailRate(int Percent) - Fortify will make an
allocation attempt "fail" this "Percent" of the time, even if the memory IS
available. Useful to "stress-test" an application. Returns the old value.
The fail rate defaults to 0.
Fortify_SetAllocationLimit(unsigned long AllocationLimit) - Impose a
limit on the amount of memory the application can allocate. This limit may
be changed at any time. If you set this to 0, memory allocations are
guaranteed to fail. The default is 0xffffffff, which is essentially no
limit. Returns the old value. Fortify_GetCurrentAllocation() can be used
to find out how much memory is currently allocated (see STATISTICAL
FUNCTIONS, below).
DIAGNOSTIC FUNCTIONS
--------------------
Fortify also provides some additional diagnostic functions which can be
used to track down memory corruption and memory leaks. If Fortify is
disabled, these functions do nothing. If calling these functions directly
from a debugger, remember to add the "const char *file" and "unsigned long
line" parameters to each of the calls (these are normally added by the
preprocessor macros).
Fortify_CheckPointer(void *uptr) - Returns true if the uptr points to a
valid block of allocated memory. The memory must be on Fortify's list, and
it's sentinels must be in tact. If anything is wrong, an error message is
issued (if Fortify is disabled, this function always returns true).
Fortify_LabelPointer(void *uptr, const char *label) - Labels the memory
block with a string provided by the user. This function takes a copy of
the passed in string. The pointer MUST be one returned by a Fortify
allocation function.
This function can be very useful to track a memory block that was
allocated on the same line as a bunch of other memory blocks. Whenever
Fortify outputs the information about this memory block, the label string
will also be output.
Fortify_CheckAllMemory() - Checks the sentinels of all memory that
Fortify knows about. This includes allocated memory, and if
FORTIFY_TRACK_DEALLOCATED_MEMORY is turned on, any deallocated memory that
Fortify is still tracking. Returns the number of blocks that failed (if
Fortify is disabled, this function always returns 0).
Fortify_ListAllMemory() - Outputs the entire list of currently
allocated memory. For each block is output it's Address, Size, and the
SourceFile and Line that allocated it. If there is no memory on the list,
this function outputs nothing. It returns the number of blocks on the list
(if Fortify has been disabled, this function always returns 0).
Fortify_DumpAllMemory() - Outputs a hex dump of all currently allocated
memory.
Fortify_EnterScope() - enters a level of fortify scope. Returns the
new scope level.
Fortify_LeaveScope() - leaves a level of fortify scope, it also prints
a dump of all unfreed memory allocated within the scope being left. This
can be very useful in tracking down memory leaks in a part of a program.
If you place a EnterScope/LeaveScope pair around a set of functions that
should have no memory allocated when it's done, Fortify will let you know
if this isn't the case. Fortify will also discard any deallocated memory
from the scope being left, if it is tracking deallocated memory.
STATISTICAL FUNCTIONS
---------------------
Fortify gathers some basic statistics about your programs memory usage.
It will keep track of the total number of memory allocation operations
performed, and also the maximum amount of memory allocated at any one time.
Fortify_OutputStatistics() - display the current statistics.
Fortify: Statistics at test.c.70
Maximum memory allocated at one time: 13633 bytes in 7 blocks
There have been 9 allocations and 4 deallocations
There was a total of 14343 bytes allocated
The average allocation was 1593 bytes
Fortify_GetCurrentAllocation() - returns the total amount of memory
currently allocated by the application (if Fortify is disabled, this
function will always return 0).
PROBLEMS WITH THE new AND delete MACROS
---------------------------------------
Due to limitations of the C preprocessor, getting caller source-code
information to new and delete isn't as easy as it is for malloc() and
free(). The macro for "new" which adds this information onto the new call
causes syntax errors if you try to declare a custom new operator. The
actual Fortifying works fine, it's just the macro expansion which causes
problems.
If this happens, the easiest thing to do is #ifdef out the custom
new/delete operators if FORTIFY is defined. Alternatively, you can not
#include "fortify.h" for the offending file (and any files that use these
custom operators, but remember that they won't have source-code
information).
eg.
#ifndef FORTIFY /* can't easily use custom new's with FORTIFY enabled */
void *X::operator new(size_t size) { return malloc(size); }
#endif
Due to a limitation with delete (it is illegal to declare a version of
delete that takes different parameters), Fortify has limited information
about where delete is being called from, and so the the line and source
information will often say "delete.0". If a delete is occurring from
within another delete, Fortify is now able to maintain a 'delete stack',
and will output this stack after the main body of the Fortify hit.
WHEN TO USE FORTIFY
-------------------
You should never be without Fortify when you're developing software.
It will detect your bugs _as_you_write_them_, which makes them a lot easier
to find.
Leave Fortify enabled until the final test and release of your
software. You probably won't want some of the slower options, such as
CHECK_ALL_MEMORY_ON_DEALLOCATE. With the exception of those options,
Fortify doesn't have a great deal of overhead. If posing a great problem,
this overhead can be reduced by cutting down on the size of the
fortifications, and turning off the pre/post fills, but each thing you turn
off gives Fortify less information to work with in tracking your bugs.
(And besides, the slower your program runs while you're developing it,
the more efficient your algorithms are likely to be!).
Dynamic Link Libraries
----------------------
Dynamic Linking of the standard libraries will not work with Fortify.
The standard library DLL has it's own copy of memory management functions,
so any memory allocated by the DLL will be unknown to Fortify, and you will
get erroneous error messages.
If you are writing your own DLL's, or using a 3rd party one, it may be
possible to write a callback module to get the DLL's to call the Fortify
routines in the parent application. If anyone does write a module to do
this, please send it to me so I can include it in the next version.
If the DLL doesn't allocate any memory, or your application doesn't
call free/delete/realloc on any memory allocated by the DLL, then Fortify
should work fine.
Another alternative to the DLL problem is to use normal link libraries
while you are developing with Fortify, and then rebuild them as DLL's for
release. This of course may not be appropriate in all situations.
REVISION HISTORY
----------------
Changes from V2.1 to V2.2
Added FORTIFY_WARN_ON_ZERO_MALLOC, and FORTIFY_FAIL_ON_ZERO_MALLOC, as
some compilers do not allow a malloc(0) to succeed.
Fixed a couple of incorrect #ifdef's.
Improved some of the documentation.
Fortify_LabelPointer() misbehaved when Fortify had been run-time
disabled.
Changes from V2.0 to V2.1
Fixed Fortify_LabelPointer(), which was broken.
Changes from V2.0B to V2.0
Moved the checksum to the start of the Header, to avoid alignment
problems (thanks to Ron Flory and Tim Taylor for finding this)
Fixed a miscalcultation with FORTIFY_ALIGNED_BEFORE_SIZE (thanks to Tim
Taylor)
Implemented the delete stack - Fortify is now aware of deletes inside
deletes, and outputs the full delete stack with all delete related hits.
Implemented the new and improved Fortify_Disable() - works even if
memory has already been allocated.
Fixed a few problems with FORTIFY_TRACK_DEALLOCATED_MEMORY being turned
off.
Fixed a couple of incorrect sprintfs.
Added Fortify_LabelPointer (thanks to Steve Toal for this idea)
FORTIFY_WARN_ON_ZERO_ALLOCATE removed as "new char[0]" is allowed, as
is malloc(0).
free(0) is now accepted as harmless.
Changes from V1.0 to V2.0 B
Added FORTIFY_ALIGNMENT, so that machines requiring special alignment
wouldn't break.
Added deallocated memory tracking, and loads of diagnostics made
possible by this.
Added statistics.
Merged Fortify and ZFortify.
Each memory block now knows which allocator function was used to
allocate it, and uses this information to ensure the correct function is
being used to free it.
Added support for strdup()
Added support for versions of sprintf that don't support "%p".
Fortify will now supply default values for configuration parameters
missing from "ufortify.h"
A few minor portability bug fixes
Fortify will now automatically configure itself for GCC and Borland C++
4.5.
All error messages and warnings tidied up so they are easier to
understand.
The new operators will now call the new handler if the allocation fails
(if there is a new handler installed, of course).
Changes from SafeMem to Fortify V1.0
Completely rewrote it.
Fortifies ANSI memory allocation functions rather than Amiga OS ones.
Supports C++'s new and delete
TO DO
-----
Make Fortify automatically configure itself for major compilers.
Add support for non-standard new-handlers, such as Microsoft Visual C++
2.0, via FORTIFY_NEW_HANDLER_FUNC.
Implement a stack for scope source-code info and scoped statistics.
Better documentation.
FAQ - installation problems, linking problems, "spurious output",
understanding the results, how to track down hits.
Better test programs (with examples of obscure bugs).
Memory allocation list could be implemented as a "skip list", so that
searching would be quicker. "Paranoid" mode would be quite fast.
CHECK_ALL_MEMORY_ON_ could take a percentage of the time to do it - it
could be set to 50%, so that memory is only checked 1/2 of the time, or we
only check 1/2 of the memory, or something like that, so that the checking
still goes on, but less frequently, so there is less overhead. Probably
overkill.
Write a DLL callback module so that user DLL's can share memory with
their parent applicatoins.
Add a "pass-count" to source-code information.

View file

@ -0,0 +1,73 @@
#
# Modification History
#
# 2002-March-29 Jason Rohrer
# Added include path to make minorGems reachable.
# Added pthread linking and debug compile flag.
#
#
# GCC makefile for Fortify's test applications
# Disable -DFORTIFY to compile without Fortify
#
# To build both of the testers, it should simply be
# a matter of
#
# make all
#
#
.SUFFIXES: .cxx .cpp
CC = g++
CCFLAGS = -g -Wall -DFORTIFY -Wformat -pedantic -I../../../..
LN = g++
LNFLAGS = -lpthread #-lm #-liostream
.c.o:
@echo ---- $*.c ----
@$(CC) -c $(CCFLAGS) $*.c
.cpp.o:
@echo ---- $*.cpp ----
@$(CC) -c $(CCFLAGS) $*.cpp
#.cpp.o:
# @echo ---- $*.cpp ----
# @$(CC) -c $(CCFLAGS) $*.cpp
all: test test2 test3
OBJS= test.o fortify.o
test: $(OBJS)
@echo Linking test
@$(LN) -o test $(OBJS) $(CCFLAGS) $(LNFLAGS)
OBJS2 = test2.o fortify.o
test2: $(OBJS2)
@echo Linking test2
@$(LN) -o test2 $(OBJS2) $(CCFLAGS) $(LNFLAGS)
OBJS3 = test3.o fortify.o
test3: $(OBJS3)
@echo Linking test3
@$(LN) -o test3 $(OBJS3) $(CCFLAGS) $(LNFLAGS)
clean:
rm -f test test2 test3 $(OBJS) $(OBJS2) $(OBJS3)
fortify.o: fortify.cpp fortify.h ufortify.h
test.o: test.c fortify.h ufortify.h
test2.o: test2.cpp fortify.h ufortify.h
test3.o: test3.cpp fortify.h ufortify.h

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,273 @@
/* fortify.h - V2.2 - All C & C++ source files to be fortified should #include this file */
/*
* This software is not public domain. All material in
* this archive is (C) Copyright 1995 Simon P. Bullen. The
* software is freely distributable, with the condition that
* no more than a nominal fee is charged for media.
* Everything in this distribution must be kept together, in
* original, unmodified form.
* The software may be modified for your own personal use,
* but modified files may not be distributed.
* The material is provided "as is" without warranty of
* any kind. The author accepts no responsibility for damage
* caused by this software.
* This software may not be used in any way by Microsoft
* Corporation or its subsidiaries, or current employees of
* Microsoft Corporation or its subsidiaries.
* This software may not be used for the construction,
* development, production, or testing of weapon systems of
* any kind.
* This software may not be used for the construction,
* development, production, or use of plants/installations
* which include the processing of radioactive/fissionable
* material.
*/
/*
* If you use this software at all, I'd love to hear from
* you. All questions, criticisms, suggestions, praise and
* postcards are most welcome.
*
* email: sbullen@cybergraphic.com.au
*
* snail: Simon P. Bullen
* PO BOX 12138
* A'Beckett St.
* Melbourne 3000
* Australia
*/
#ifndef __FORTIFY_H__
#define __FORTIFY_H__
#include <stdlib.h>
#include <string.h>
/* the user's options */
#include "ufortify.h"
/* Ensure the configuration parameters have sensible defaults */
#ifndef FORTIFY_STORAGE
#define FORTIFY_STORAGE
#endif
#ifndef FORTIFY_ALIGNMENT
#define FORTIFY_ALIGNMENT sizeof(double)
#endif
#ifndef FORTIFY_BEFORE_SIZE
#define FORTIFY_BEFORE_SIZE 32
#endif
#ifndef FORTIFY_BEFORE_VALUE
#define FORTIFY_BEFORE_VALUE 0xA3
#endif
#ifndef FORTIFY_AFTER_SIZE
#define FORTIFY_AFTER_SIZE 32
#endif
#ifndef FORTIFY_AFTER_VALUE
#define FORTIFY_AFTER_VALUE 0xA5
#endif
#ifndef FORTIFY_FILL_ON_ALLOCATE_VALUE
#define FORTIFY_FILL_ON_ALLOCATE_VALUE 0xA7
#endif
#ifndef FORTIFY_FILL_ON_DEALLOCATE_VALUE
#define FORTIFY_FILL_ON_DEALLOCATE_VALUE 0xA9
#endif
#ifndef FORTIFY_LOCK
#define FORTIFY_LOCK()
#endif
#ifndef FORTIFY_UNLOCK
#define FORTIFY_UNLOCK()
#endif
#ifndef FORTIFY_CHECKSUM_VALUE
#define FORTIFY_CHECKSUM_VALUE 0x0AD0
#endif
#ifndef FORTIFY_DELETE_STACK_SIZE
#define FORTIFY_DELETE_STACK_SIZE 256
#endif
#ifndef FORTIFY_NEW_HANDLER_FUNC
typedef void (*Fortify_NewHandlerFunc)(void);
#define FORTIFY_NEW_HANDLER_FUNC Fortify_NewHandlerFunc
#endif
/*
* Code to detect and configure for various compilers lives here.
*/
#ifdef __GNUG__
/* GCC configuration */
#define FORTIFY_PROVIDE_ARRAY_NEW
#define FORTIFY_PROVIDE_ARRAY_DELETE
#endif
#ifdef __BC45__
/* Borland C++ 4.5 configuration */
#define FORTIFY_PROVIDE_ARRAY_NEW
#define FORTIFY_PROVIDE_ARRAY_DELETE
#define FORTIFY_FAIL_ON_ZERO_MALLOC
#endif
#ifdef __SASC
/* SAS configuration */
#define FORTIFY_FAIL_ON_ZERO_MALLOC
#endif
/* Allocators */
#define Fortify_Allocator_malloc 0 /* ANSI C */
#define Fortify_Allocator_calloc 1 /* ANSI C */
#define Fortify_Allocator_realloc 2 /* ANSI C */
#define Fortify_Allocator_strdup 3 /* C */
#define Fortify_Allocator_new 4 /* ANSI C++ */
#define Fortify_Allocator_array_new 5 /* Some C++ */
/* Deallocators */
#define Fortify_Deallocator_nobody 0
#define Fortify_Deallocator_free 1 /* ANSI C */
#define Fortify_Deallocator_realloc 2 /* ANSI C */
#define Fortify_Deallocator_delete 3 /* ANSI C++ */
#define Fortify_Deallocator_array_delete 4 /* Some C++ */
/* Public Fortify Types */
typedef void (*Fortify_OutputFuncPtr)(const char *);
#ifdef __cplusplus
extern "C" {
#endif
/* Core Fortify Functions */
void *Fortify_Allocate (size_t size, unsigned char allocator, const char *file, unsigned long line);
void Fortify_Deallocate(void *uptr, unsigned char deallocator, const char *file, unsigned long line);
unsigned long Fortify_CheckAllMemory(const char *file, unsigned long line);
unsigned long Fortify_ListAllMemory (const char *file, unsigned long line);
unsigned long Fortify_DumpAllMemory (const char *file, unsigned long line);
int Fortify_CheckPointer(void *uptr, const char *file, unsigned long line);
void Fortify_LabelPointer(void *uptr, const char *label, const char *file, unsigned long line);
unsigned char Fortify_EnterScope(const char *file, unsigned long line);
unsigned char Fortify_LeaveScope(const char *file, unsigned long line);
void Fortify_OutputStatistics(const char *file, unsigned long line);
unsigned long Fortify_GetCurrentAllocation(const char *file, unsigned long line);
void Fortify_SetAllocationLimit(unsigned long Limit, const char *file, unsigned long line);
int Fortify_SetFailRate(int Percent);
Fortify_OutputFuncPtr Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output);
void Fortify_Disable(const char *file, unsigned long line);
/* Fortify versions of the ANSI C memory allocation functions */
void *Fortify_malloc(size_t size, const char *file, unsigned long line);
void *Fortify_realloc(void *ptr, size_t new_size, const char *file, unsigned long line);
void *Fortify_calloc(size_t num, size_t size, const char *file, unsigned long line);
void Fortify_free(void *uptr, const char *file, unsigned long line);
/* Fortify versions of some non-ANSI C memory allocation functions */
#ifdef FORTIFY_STRDUP
char *Fortify_strdup(const char *oldStr, const char *file, unsigned long line);
#endif
#ifdef __cplusplus
/* Magic global variable */
extern int gbl_FortifyMagic;
#endif
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
#include <new.h>
/* Fortify versions of new and delete */
void *operator new(size_t size);
void *operator new(size_t size, const char *file, unsigned long line);
void operator delete(void *pointer);
void Fortify_PreDelete(const char *file, unsigned long line);
void Fortify_PostDelete();
/* Some compilers use a different new operator for newing arrays.
* This includes GNU G++ (2.6.0) and Borland C++ (4.02)
*/
#ifdef FORTIFY_PROVIDE_ARRAY_NEW
void *operator new[](size_t size);
void *operator new[](size_t size, const char *file, unsigned long line);
#endif
/* Some compilers provide a different delete operator for deleting arrays.
* This incldues GNU G++ (2.6.0)
*/
#ifdef FORTIFY_PROVIDE_ARRAY_DELETE
void operator delete[](void *pointer);
#endif
#endif /* __cplusplus */
#ifndef __FORTIFY_C__ /* Only define the macros if we're NOT in fortify.c */
/* Add file and line information to the fortify calls */
#ifdef FORTIFY
/* Core Fortify Functions */
#define Fortify_CheckAllMemory() Fortify_CheckAllMemory(__FILE__, __LINE__)
#define Fortify_ListAllMemory() Fortify_ListAllMemory (__FILE__, __LINE__)
#define Fortify_DumpAllMemory() Fortify_DumpAllMemory (__FILE__, __LINE__)
#define Fortify_CheckPointer(ptr) Fortify_CheckPointer(ptr, __FILE__, __LINE__)
#define Fortify_LabelPointer(ptr,str) Fortify_LabelPointer(ptr, str, __FILE__, __LINE__)
#define Fortify_EnterScope() Fortify_EnterScope(__FILE__, __LINE__)
#define Fortify_LeaveScope() Fortify_LeaveScope(__FILE__, __LINE__)
#define Fortify_OutputStatistics() Fortify_OutputStatistics(__FILE__, __LINE__)
#define Fortify_GetCurrentAllocation() Fortify_GetCurrentAllocation(__FILE__, __LINE__)
#define Fortify_SetAllocationLimit(x) Fortify_SetAllocationLimit(x, __FILE__, __LINE__)
#define Fortify_Disable() Fortify_Disable(__FILE__, __LINE__)
/* Fortify versions of the ANSI C memory allocation functions */
#define malloc(size) Fortify_malloc(size, __FILE__, __LINE__)
#define realloc(ptr,new_size) Fortify_realloc(ptr, new_size, __FILE__, __LINE__)
#define calloc(num,size) Fortify_calloc(num, size, __FILE__, __LINE__)
#define free(ptr) Fortify_free(ptr, __FILE__, __LINE__)
/* Fortify versions of some non-ANSI C memory allocation functions */
#ifdef FORTIFY_STRDUP
#define strdup(ptr) Fortify_strdup(ptr, __FILE__, __LINE__)
#endif
/* Fortify versions of new and delete */
#ifdef __cplusplus
#define Fortify_New new(__FILE__, __LINE__)
#define Fortify_Delete for(gbl_FortifyMagic = 1, \
Fortify_PreDelete(__FILE__, __LINE__); \
gbl_FortifyMagic; Fortify_PostDelete()) \
gbl_FortifyMagic = 0, delete
#define new Fortify_New
#define delete Fortify_Delete
#endif /* __cplusplus */
#else /* Define the special fortify functions away to nothing */
#define Fortify_CheckAllMemory() 0
#define Fortify_ListAllMemory() 0
#define Fortify_DumpAllMemory() 0
#define Fortify_CheckPointer(ptr) 1
#define Fortify_LabelPointer(ptr,str)
#define Fortify_SetOutputFunc() 0
#define Fortify_SetMallocFailRate(p) 0
#define Fortify_EnterScope() 0
#define Fortify_LeaveScope() 0
#define Fortify_OutputStatistics() 0
#define Fortify_GetCurrentAllocation() 0
#define Fortify_SetAllocationLimit(x) 0
#define Fortify_Disable() 0
#ifdef __cplusplus
#define Fortify_New new
#define Fortify_Delete delete
#endif /* __cplusplus */
#endif /* FORTIFY */
#endif /* __FORTIFY_C__ */
#endif /* __FORTIFY_H__ */

View file

@ -0,0 +1,61 @@
#include <stdio.h>
#include <stdlib.h>
#include "fortify.h"
/*
* NOTE: The Fortify routines will compile away to nothing
* if FORTIFY isn't defined in the makefile.
*
* DO NOT insert #define FORTIFY here. It Will Not Work.
* All files (including fortify.cxx) need FORTIFY to be
* defined. The correct place for this is the makefile.
*/
int
main(int argc, char **argv)
{
char *foo, *bar;
Fortify_EnterScope();
/* note that we never free this memory */
foo = malloc(123);
Fortify_LabelPointer(foo, "we never free this memory!");
/* note that we read this memory after it's been freed */
foo = malloc(124);
*foo = 'X';
free(foo);
printf("Should be X: '%c'\n", *foo);
/* note we've already freed this memory */
free(foo);
/* note we write to memory that's been realloc'd, using the old pointer */
foo = malloc(125);
bar = realloc(foo, 126);
*foo = 'X';
free(bar);
/* note we write before the block */
foo = malloc(127);
Fortify_LabelPointer(foo, "we write before this block!");
*(foo-1) = 'Z';
free(foo);
/* note we write after the block */
bar = "I'm going to eat you little fishie!";
foo = malloc(strlen(bar));
strcpy(foo, bar);
free(foo);
/* we never allocated this memory */
free(bar);
Fortify_LeaveScope();
Fortify_OutputStatistics();
return 42;
}

View file

@ -0,0 +1,66 @@
#include <stdio.h>
#include <stdlib.h>
#include "fortify.h"
/*
* NOTE: The Fortify routines will compile away to nothing
* if FORTIFY isn't defined in the makefile.
*
* DO NOT insert #define FORTIFY here. It Will Not Work.
* All files (including fortify.cxx) need FORTIFY to be
* defined. The correct place for this is the makefile.
*/
class A
{
public:
~A() { delete (char*)123; }
};
int
main(int argc, char **argv)
{
char *foo;
Fortify_EnterScope();
/* zero size test */
foo =(char*) malloc(0);
printf("malloc(0) %s\n", foo ? "succeeded" : "failed");
/* zero size test */
foo = new char[0];
printf("new char[0] %s\n", foo ? "succeeded" : "failed");
foo = new char;
/* note we use the incorrect deallocator */
/* note this will only be detected if FORTIFY_PROVIDE_ARRAY_NEW
* and FORTIFY_PROVIDE_ARRAY_DELETE are both turned on
* (and your compiler supports them) */
delete[] foo;
*foo = 'Z';
foo = new char;
Fortify_LabelPointer(foo, "we use the wrong deallocator on this one");
/* note we use the incorrect dealocator */
free(foo);
/* the destructor of this class does an illegal delete -
* demonstrates the delete-stack
*/
delete new A;
Fortify_LeaveScope();
return 1;
}

View file

@ -0,0 +1,57 @@
#include <stdio.h>
#include <stdlib.h>
#include "fortify.h"
int main() {
//Fortify_EnterScope();
int *a = new int[ 10 ];
int i;
for( i=0; i<10; i++ ) {
a[i] = i;
}
for( i=0; i<10; i++ ) {
printf( "a[i] = %d\n", a[i] );
}
delete [] a;
for( i=0; i<10; i++ ) {
printf( "a[i] = %d\n", a[i] );
}
//Fortify_CheckAllMemory();
for( i=0; i<10; i++ ) {
a[i] = i;
}
for( i=0; i<10; i++ ) {
printf( "a[i] = %d\n", a[i] );
}
//Fortify_CheckAllMemory();
int *b = new int[5];
delete [] b;
delete [] a;
//Fortify_LeaveScope();
return 0;
}

View file

@ -0,0 +1,79 @@
/*
* Modification History
*
* 2002-March-29 Jason Rohrer
* Added prototypes for locking functions.
*/
/*
* FILE:
* ufortify.h
*
* DESCRIPTION:
* User options for fortify. Changes to this file require fortify.c to be
* recompiled, but nothing else.
*/
#define FORTIFY_STORAGE /* storage for public functions */
#define FORTIFY_ALIGNMENT sizeof(double) /* Byte alignment of all memory blocks */
#define FORTIFY_BEFORE_SIZE 32 /* Bytes to allocate before block */
#define FORTIFY_BEFORE_VALUE 0xA3 /* Fill value before block */
#define FORTIFY_AFTER_SIZE 32 /* Bytes to allocate after block */
#define FORTIFY_AFTER_VALUE 0xA5 /* Fill value after block */
#define FORTIFY_FILL_ON_ALLOCATE /* Nuke out malloc'd memory */
#define FORTIFY_FILL_ON_ALLOCATE_VALUE 0xA7 /* Value to initialize with */
#define FORTIFY_FILL_ON_DEALLOCATE /* free'd memory is cleared */
#define FORTIFY_FILL_ON_DEALLOCATE_VALUE 0xA9 /* Value to de-initialize with */
#define FORTIFY_FILL_ON_CORRUPTION /* Nuke out corrupted memory */
#define FORTIFY_CHECK_ALL_MEMORY_ON_ALLOCATE
#define FORTIFY_CHECK_ALL_MEMORY_ON_DEALLOCATE
#define FORTIFY_PARANOID_DEALLOCATE
#define FORTIFY_WARN_ON_ZERO_MALLOC /* A debug is issued on a malloc(0) */
/* #define FORTIFY_FAIL_ON_ZERO_MALLOC */ /* A malloc(0) will fail */
#define FORTIFY_WARN_ON_ALLOCATE_FAIL /* A debug is issued on a failed alloc */
#define FORTIFY_WARN_ON_FALSE_FAIL /* See Fortify_SetAllocateFailRate */
#define FORTIFY_WARN_ON_SIZE_T_OVERFLOW /* Watch for breaking the 64K limit in */
/* some braindead architectures... */
#define FORTIFY_TRACK_DEALLOCATED_MEMORY
#define FORTIFY_DEALLOCATED_MEMORY_LIMIT 1048576 /* Maximum amount of deallocated bytes to keep */
/* #define FORTIFY_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY */
/* #define FORTIFY_VERBOSE_WARN_WHEN_DISCARDING_DEALLOCATED_MEMORY */
/* #define FORTIFY_NO_PERCENT_P */ /* sprintf() doesn't support %p */
#define FORTIFY_STRDUP /* if you use non-ANSI strdup() */
// prototypes
void Fortify_Lock();
void Fortify_Unlock();
#define FORTIFY_LOCK() Fortify_Lock()
#define FORTIFY_UNLOCK() Fortify_Unlock()
#define FORTIFY_DELETE_STACK_SIZE 256
#ifdef __cplusplus /* C++ only options go here */
/* #define FORTIFY_PROVIDE_ARRAY_NEW */
/* #define FORTIFY_PROVIDE_ARRAY_DELETE */
#define FORTIFY_PROVIDE_ARRAY_NEW
#define FORTIFY_PROVIDE_ARRAY_DELETE
/* #define FORTIFY_AUTOMATIC_LOG_FILE */
#define FORTIFY_LOG_FILENAME "fortify.log"
#include <iostream.h>
#define FORTIFY_FIRST_ERROR_FUNCTION cout << "\a\a\aFortify Hit Generated!\n"
#endif /* __cplusplus */

View file

@ -0,0 +1,22 @@
#!/bin/sh
if [ $# -lt 1 ] ; then
echo "Usage: $0 <program [program parameters]>"
exit 1
fi
# this looks in the same directory, this
# LeakCheck script resides; modify to your
# needs:
SHLIB=`dirname $0`/LeakTracer.so
if [ ! -x $SHLIB ] ; then
echo "$SHLIB not found"
exit 1
fi
if [ -z "$LEAKTRACE_FILE" ] ; then
rm -f leak.out
else
rm -f "$LEAKTRACE_FILE"
fi
export LD_PRELOAD=$SHLIB
exec $@

View file

@ -0,0 +1,13 @@
#!/bin/sh
CHECKER=`dirname $0`/LeakCheck" $@"
ANALYZER=`dirname $0`/leak-analyze" $1 leak.out"
echo "Checking with: $CHECKER"
echo ""
$CHECKER
echo ""
echo "Analyzing with: $ANALYZER"
echo ""
$ANALYZER 2>&1 | more

View file

@ -0,0 +1,578 @@
/*
* Modification History
*
* 2002-March-31 Jason Rohrer
* Added support for strdup.
* Fixed bug in strdup.
*
* 2003-October-3 Jason Rohrer
* Added printout of leak contents in report.
*
* 2004-January-16 Jason Rohrer
* Switched to use minorGems platform-independed mutexes.
* Changed to use simpler fopen call to open report.
*/
/*
* Homepage: <http://www.andreasen.org/LeakTracer/>
*
* Authors:
* Erwin S. Andreasen <erwin@andreasen.org>
* Henner Zeller <foobar@to.com>
*
* This program is Public Domain
*/
#ifdef THREAD_SAVE
#define _THREAD_SAVE
//#include <pthread.h>
#include "minorGems/system/MutexLock.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
/*
* underlying allocation, de-allocation used within
* this tool
*/
#define LT_MALLOC malloc
#define LT_FREE free
#define LT_REALLOC realloc
/*
* prime number for the address lookup hash table.
* if you have _really_ many memory allocations, use a
* higher value, like 343051 for instance.
*/
#define SOME_PRIME 35323
#define ADDR_HASH(addr) ((unsigned long) addr % SOME_PRIME)
/**
* Filedescriptor to write to. This should not be a low number,
* because these often have special meanings (stdin, out, err)
* and may be closed by the program (daemons)
* So choose an arbitrary higher FileDescriptor .. e.g. 42
*/
#define FILEDESC 42
/**
* allocate a bit more memory in order to check if there is a memory
* overwrite. Either 0 or more than sizeof(unsigned int). Note, you can
* only detect memory over_write_, not _reading_ beyond the boundaries. Better
* use electric fence for these kind of bugs
* <ftp://ftp.perens.com/pub/ElectricFence/>
*/
typedef unsigned long magic_t;
#define MAGIC ((magic_t) 0xAABBCCDDLu)
/**
* this may be more than sizeof(magic_t); if you want more, then
* sepecify it like #define SAVESIZE (sizeof(magic_t) + 12)
*/
#define SAVESIZE (sizeof(magic_t) + 0)
/**
* on 'new', initialize the memory with this value.
* if not defined - uninitialized. This is very helpful because
* it detects if you initialize your classes correctly .. if not,
* this helps you faster to get the segmentation fault you're
* implicitly asking for :-).
*
* Set this to some value which is likely to produce a
* segmentation fault on your platform.
*/
#define SAVEVALUE 0xAA
/**
* on 'delete', clean memory with this value.
* if not defined - no memory clean.
*
* Set this to some value which is likely to produce a
* segmentation fault on your platform.
*/
#define MEMCLEAN 0xEE
/**
* Initial Number of memory allocations in our list.
* Doubles for each re-allocation.
*/
#define INITIALSIZE 32768
static class LeakTracer {
struct Leak {
const void *addr;
size_t size;
const void *allocAddr;
bool type;
int nextBucket;
};
int newCount; // how many memory blocks do we have
int leaksCount; // amount of entries in the leaks array
int firstFreeSpot; // Where is the first free spot in the leaks array?
int currentAllocated; // currentAllocatedMemory
int maxAllocated; // maximum Allocated
unsigned long totalAllocations; // total number of allocations. stats.
unsigned int abortOn; // resons to abort program (see abortReason_t)
/**
* Have we been initialized yet? We depend on this being
* false before constructor has been called!
*/
bool initialized;
bool destroyed; // Has our destructor been called?
FILE *report; // filedescriptor to write to
/**
* pre-allocated array of leak info structs.
*/
Leak *leaks;
/**
* fast hash to lookup the spot where an allocation is
* stored in case of an delete. map<void*,index-in-leaks-array>
*/
int *leakHash; // fast lookup
#ifdef THREAD_SAVE
MutexLock mutex;
#endif
enum abortReason_t {
OVERWRITE_MEMORY = 0x01,
DELETE_NONEXISTENT = 0x02,
NEW_DELETE_MISMATCH = 0x04
};
public:
LeakTracer() {
initialize();
}
void initialize() {
// Unfortunately we might be called before our constructor has actualy fired
if (initialized)
return;
// fprintf(stderr, "LeakTracer::initialize()\n");
initialized = true;
newCount = 0;
leaksCount = 0;
firstFreeSpot = 1; // index '0' is special
currentAllocated = 0;
maxAllocated = 0;
totalAllocations = 0;
abortOn = OVERWRITE_MEMORY; // only _severe_ reason
report = 0;
leaks = 0;
leakHash = 0;
char uniqFilename[256];
const char *filename = getenv("LEAKTRACE_FILE") ? : "leak.out";
struct stat dummy;
if (stat(filename, &dummy) == 0) {
sprintf(uniqFilename, "%s.%d", filename, getpid());
fprintf(stderr,
"LeakTracer: file exists; using %s instead\n",
uniqFilename);
}
else {
sprintf(uniqFilename, "%s", filename);
}
// not sure why this "open" code is here
// (it doesn't open the file properly in MinGW on win32)
/*
int reportfd = open(uniqFilename,
O_WRONLY|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE);
if (reportfd < 0) {
fprintf(stderr, "LeakTracer: cannot open %s: %m\n",
filename);
report = stderr;
}
else {
int dupfd = dup2(reportfd, FILEDESC);
close(reportfd);
report = fdopen(dupfd, "w");
if (report == NULL) {
report = stderr;
}
}
*/
// simpler version using only fopen
report = fopen( uniqFilename, "w" );
if( report == NULL ) {
fprintf( stderr, "LeakTracer: cannot open %s\n",
uniqFilename );
report = stderr;
}
time_t t = time(NULL);
fprintf (report, "# starting %s", ctime(&t));
leakHash = (int*) LT_MALLOC(SOME_PRIME * sizeof(int));
memset ((void*) leakHash, 0x00, SOME_PRIME * sizeof(int));
#ifdef MAGIC
fprintf (report, "# memory overrun protection of %d Bytes "
"with magic 0x%4lX\n",
SAVESIZE, MAGIC);
#endif
#ifdef SAVEVALUE
fprintf (report, "# initializing new memory with 0x%2X\n",
SAVEVALUE);
#endif
#ifdef MEMCLEAN
fprintf (report, "# sweeping deleted memory with 0x%2X\n",
MEMCLEAN);
#endif
if (getenv("LT_ABORTREASON")) {
abortOn = atoi(getenv("LT_ABORTREASON"));
}
#define PRINTREASON(x) if (abortOn & x) fprintf(report, "%s ", #x);
fprintf (report, "# aborts on ");
PRINTREASON( OVERWRITE_MEMORY );
PRINTREASON( DELETE_NONEXISTENT );
PRINTREASON( NEW_DELETE_MISMATCH );
fprintf (report, "\n");
#undef PRINTREASON
#ifdef THREAD_SAVE
fprintf (report, "# thread save\n");
/*
* create default, non-recursive ('fast') mutex
* to lock our datastructure where we keep track of
* the user's new/deletes
*/
/*if (pthread_mutex_init(&mutex, NULL) < 0) {
fprintf(report, "# couldn't init mutex ..\n");
fclose(report);
_exit(1);
}*/
#else
fprintf(report, "# not thread save; if you use threads, recompile with -DTHREAD_SAVE\n");
#endif
fflush(report);
}
/*
* the workhorses:
*/
void *registerAlloc(size_t size, bool type);
void registerFree (void *p, bool type);
/**
* write a hexdump of the given area.
*/
void hexdump(const unsigned char* area, int size);
/**
* Terminate current running progam.
*/
void progAbort(abortReason_t reason) {
if (abortOn & reason) {
fprintf(report, "# abort; DUMP of current state\n");
writeLeakReport();
fclose(report);
abort();
}
else
fflush(report);
}
/**
* write a Report over leaks, e.g. still pending deletes
*/
void writeLeakReport();
~LeakTracer() {
// fprintf(stderr, "LeakTracer::destroy()\n");
time_t t = time(NULL);
fprintf (report, "# finished %s", ctime(&t));
writeLeakReport();
fclose(report);
free(leaks);
#ifdef THREAD_SAVE
//pthread_mutex_destroy(&mutex);
#endif
destroyed = true;
}
} leakTracer;
void* LeakTracer::registerAlloc (size_t size, bool type) {
initialize();
// fprintf(stderr, "LeakTracer::registerAlloc()\n");
if (destroyed) {
fprintf(stderr, "Oops, registerAlloc called after destruction of LeakTracer (size=%d)\n", size);
return LT_MALLOC(size);
}
void *p = LT_MALLOC(size + SAVESIZE);
// Need to call the new-handler
if (!p) {
fprintf(report, "LeakTracer malloc %m\n");
_exit (1);
}
#ifdef SAVEVALUE
/* initialize with some defined pattern */
memset(p, SAVEVALUE, size + SAVESIZE);
#endif
#ifdef MAGIC
/*
* the magic value is a special pattern which does not need
* to be uniform.
*/
if (SAVESIZE >= sizeof(magic_t)) {
magic_t *mag;
mag = (magic_t*)((char*)p + size);
*mag = MAGIC;
}
#endif
#ifdef THREAD_SAVE
//pthread_mutex_lock(&mutex);
mutex.lock();
#endif
++newCount;
++totalAllocations;
currentAllocated += size;
if (currentAllocated > maxAllocated)
maxAllocated = currentAllocated;
for (;;) {
for (int i = firstFreeSpot; i < leaksCount; i++)
if (leaks[i].addr == NULL) {
leaks[i].addr = p;
leaks[i].size = size;
leaks[i].type = type;
leaks[i].allocAddr=__builtin_return_address(1);
firstFreeSpot = i+1;
// allow to lookup our index fast.
int *hashPos = &leakHash[ ADDR_HASH(p) ];
leaks[i].nextBucket = *hashPos;
*hashPos = i;
#ifdef THREAD_SAVE
//pthread_mutex_unlock(&mutex);
mutex.unlock();
#endif
return p;
}
// Allocate a bigger array
// Note that leaksCount starts out at 0.
int new_leaksCount = (leaksCount == 0) ? INITIALSIZE
: leaksCount * 2;
leaks = (Leak*)LT_REALLOC(leaks,
sizeof(Leak) * new_leaksCount);
if (!leaks) {
fprintf(report, "# LeakTracer realloc failed: %m\n");
_exit(1);
}
else {
fprintf(report, "# internal buffer now %d\n",
new_leaksCount);
fflush(report);
}
memset(leaks+leaksCount, 0x00,
sizeof(Leak) * (new_leaksCount-leaksCount));
leaksCount = new_leaksCount;
}
}
void LeakTracer::hexdump(const unsigned char* area, int size) {
fprintf(report, "# ");
for (int j=0; j < size ; ++j) {
fprintf (report, "%02x ", *(area+j));
if (j % 16 == 15) {
fprintf(report, " ");
for (int k=-15; k < 0 ; k++) {
char c = (char) *(area + j + k);
fprintf (report, "%c", isprint(c) ? c : '.');
}
fprintf(report, "\n# ");
}
}
fprintf(report, "\n");
}
void LeakTracer::registerFree (void *p, bool type) {
initialize();
if (p == NULL)
return;
if (destroyed) {
fprintf(stderr, "Oops, allocation destruction of LeakTracer (p=%p)\n", p);
return;
}
#ifdef THREAD_SAVE
//pthread_mutex_lock(&mutex);
mutex.lock();
#endif
int *lastPointer = &leakHash[ ADDR_HASH(p) ];
int i = *lastPointer;
while (i != 0 && leaks[i].addr != p) {
lastPointer = &leaks[i].nextBucket;
i = *lastPointer;
}
if (leaks[i].addr == p) {
*lastPointer = leaks[i].nextBucket; // detach.
newCount--;
leaks[i].addr = NULL;
currentAllocated -= leaks[i].size;
if (i < firstFreeSpot)
firstFreeSpot = i;
if (leaks[i].type != type) {
fprintf(report,
"S %10p %10p # new%s but delete%s "
"; size %d\n",
leaks[i].allocAddr,
__builtin_return_address(1),
((!type) ? "[]" : " normal"),
((type) ? "[]" : " normal"),
leaks[i].size);
progAbort( NEW_DELETE_MISMATCH );
}
#ifdef MAGIC
if ((SAVESIZE >= sizeof(magic_t)) &&
*((magic_t*)((char*)p + leaks[i].size)) != MAGIC) {
fprintf(report, "O %10p %10p "
"# memory overwritten beyond allocated"
" %d bytes\n",
leaks[i].allocAddr,
__builtin_return_address(1),
leaks[i].size);
fprintf(report, "# %d byte beyond area:\n",
SAVESIZE);
hexdump((unsigned char*)p+leaks[i].size,
SAVESIZE);
progAbort( OVERWRITE_MEMORY );
}
#endif
#ifdef THREAD_SAVE
# ifdef MEMCLEAN
int allocationSize = leaks[i].size;
# endif
//pthread_mutex_unlock(&mutex);
mutex.unlock();
#else
#define allocationSize leaks[i].size
#endif
#ifdef MEMCLEAN
// set it to some garbage value.
memset((unsigned char*)p, MEMCLEAN, allocationSize + SAVESIZE);
#endif
LT_FREE(p);
return;
}
#ifdef THREAD_SAVE
//pthread_mutex_unlock(&mutex);
mutex.unlock();
#endif
fprintf(report, "D %10p # delete non alloc or twice pointer %10p\n",
__builtin_return_address(1), p);
progAbort( DELETE_NONEXISTENT );
}
void LeakTracer::writeLeakReport() {
initialize();
if (newCount > 0) {
fprintf(report, "# LeakReport\n");
fprintf(report, "# %10s | %9s # Pointer Addr\n",
"from new @", "size");
}
for (int i = 0; i < leaksCount; i++)
if (leaks[i].addr != NULL) {
// This ought to be 64-bit safe?
char *memContents = (char *)LT_MALLOC( leaks[i].size + 1 );
memcpy( (void *)memContents, (void *)( leaks[i].addr ),
leaks[i].size );
memContents[ leaks[i].size ] = '\0';
fprintf(report, "L %10p %9ld # %p \"%s\"\n",
leaks[i].allocAddr,
(long) leaks[i].size,
leaks[i].addr,
memContents );
LT_FREE( memContents );
}
fprintf(report, "# total allocation requests: %6ld ; max. mem used"
" %d kBytes\n", totalAllocations, maxAllocated / 1024);
fprintf(report, "# leak %6d Bytes\t:-%c\n", currentAllocated,
(currentAllocated == 0) ? ')' : '(');
if (currentAllocated > 50 * 1024) {
fprintf(report, "# .. that is %d kByte!! A lot ..\n",
currentAllocated / 1024);
}
}
/** -- The actual new/delete operators -- **/
void* operator new(size_t size) {
return leakTracer.registerAlloc(size,false);
}
void* operator new[] (size_t size) {
return leakTracer.registerAlloc(size,true);
}
void operator delete (void *p) {
leakTracer.registerFree(p,false);
}
void operator delete[] (void *p) {
leakTracer.registerFree(p,true);
}
// added by Jason Rohrer
char *strdup( const char *inString ) {
char *outString =
(char*)leakTracer.registerAlloc( strlen( inString ) + 1, true );
strcpy( outString, inString );
return outString;
}
/* Emacs:
* Local variables:
* c-basic-offset: 8
* End:
* vi:set tabstop=8 shiftwidth=8 nowrap:
*/

View file

@ -0,0 +1,64 @@
#
# Modification History
#
# 2004-January-16 Jason Rohrer
# Switched to use minorGems platform-independed mutexes.
#
CC = g++
# Source files
SRC := LeakTracer.cc
ROOT_PATH = ../../../..
# Switch comment to select a platform
# PLATFORM_MUTEX = $(ROOT_PATH)/minorGems/system/win32/MutexLockWin32.cpp
PLATFORM_MUTEX = $(ROOT_PATH)/minorGems/system/linux/MutexLockLinux.cpp -pthread
# Comment both of these out to disable thread safetly
C_THREAD=-DTHREAD_SAVE -D_REENTRANT -D_THREAD_SAFE
O_THREAD = $(PLATFORM_MUTEX)
# Common flags
C_FLAGS = -g -pipe -Wall -W -I$(ROOT_PATH) $(C_THREAD)
O_FLAGS = $(C_FLAGS) $(O_THREAD)
# Object files
OBJ_DIR = .
OBJ := $(patsubst %.cc,$(OBJ_DIR)/%.o,$(SRC))
SHOBJ := $(patsubst %.o,$(OBJ_DIR)/%.so,$(OBJ))
.PHONY: all clean tidy distrib test
all: $(OBJ) $(SHOBJ)
clean: tidy
rm -f $(OBJ) leak.out
tidy:
rm -f *~ *orig *bak *rej
tags: $(SRC) $(INCL)
ctags $(SRC) $(INCL)
distrib: clean all README.html
(cd .. && tar cvfz /root/drylock/LeakTracer/LeakTracer.tar.gz --exclude LeakTracer/CVS --exclude LeakTracer/old --exclude LeakTracer/test LeakTracer/)
$(OBJ_DIR)/%.o: %.cc
$(CC) -fPIC -c $(C_FLAGS) $< -o $@
$(OBJ_DIR)/%.so : $(OBJ_DIR)/%.o
$(CC) $(O_FLAGS) -shared -o $@ $<
README.html: README
/root/ed/mcl/util/htmlize.pl README
test:
$(CC) $(C_FLAGS) test.cc -o test
./test
./LeakCheck ./test
./leak-analyze ./test
# ./compare-test test.template test.result

View file

@ -0,0 +1,230 @@
Introduction
------------
LeakTracer is a small tool I wrote when checking a C++ program for memory
leaks. I couldn't get dmalloc to display what I wanted, and I just saw the
__builtin_return_address gcc-extension mentioned.
To use LeakTracer, run your program using the provided LeakCheck script. It
uses the LD_PRELOAD feature to "overlay" some functions on top of your
functions (no recompile needed). If your platform does not support LD_PRELOAD,
you can add the LeakTracer.o object file to the objects in your Makefile and
run your application.
LeakTracer uses gdb to print out the exact line where the memory was allocated
and not freed - this of course means you have to free all dynamically
allocated data. LeakTracer also overrides the global operator new and operator
delete - this will give problems if you override them as well.
LeakTracer traces only new/new[] and delete calls - it does not look at
malloc/free/realloc.
Here is some example output:
Gathered 8 (8 unique) points of data.
(gdb)
Allocations: 1 / Size: 36
0x80608e6 is in NullArcableInstance::NullArcableInstance(void) (Machine.cc:40).
39 public:
40 NullArcableInstance() : ArcableInstance(new NullArcable) {}
Allocations: 1 / Size: 8
0x8055b02 is in init_types(void) (Type.cc:119).
118 void init_types() {
119 Type::Integer = new IntegerType;
Allocations: 1 / Size: 132 (new[])
0x805f4ab is in Hashtable<NativeCallable, String, false, true>::Hashtable(unsigned int) (ea/h/Hashtable.h:15).
14 Hashtable (uint _size = 32) : size(_size), count(0) {
15 table = new List<E, own> [size];
[...]
Requirements
------------
You need Perl5 and gdb installed to run the leak-analyzer. You need gcc -- I
currently use 2.95 but have used it with previous older versions without
problems.
You also need to run this on an architecture which supports
__builtin_return_address arguments that are greater than 0 - there may be
some problems on MIPS there.
So far this code has been tested under Linux 2.2, x86 system, Solaris and
HP-UX.
Installation
------------
Just type make. There is no install target; you should put LeakTracer
some place you can remember.
Since version 2.0, it is possible to preload the LeakTracer object on
architectures that support LD_PRELOAD (this is at least Linux and probably
others -- please report success/failure). This means it is much easier to use
the program: you do not need to relink your program with LeakTracer.o.
In case your platform does not support LD_PRELOAD, you can use LeakTracer in
the old pre 2.0 way: add LeakTracer.o to your object files -- at the very end
of them (also after -llibrary lines).
In any case your application must also be compiled with debugging enabled
(i.e. -g).
Running with LeakTracer
-----------------------
If you are using the shared object, run the LeakCheck script. This script
should stay in the directory where you install LeakCheck -- it will search for
LeakTracer.so file there and load it. E.g.:
~/src/LeakTracer/LeakCheck yourApplication
(if you put LeakTracer in ~/src/LeakTracer/)
Run your application as normal, performing tasks that you want to be traced
for memory leaks. While the application runs, LeakTracer will write data about
memory allocation to the file "leak.out" in the current directory. You can
override the location of that file by setting the LEAKTRACE_FILE environment
variable.
If you cannot use LD_PRELOAD, just run your application as normal after
relinking it. It will also produce a "leak.out" file when it finishes.
Detectable errors
-----------------
LeakTracer is capable to detect the following problems with your program
1) memory which is allocated but not freed
2) (limited support for) overwritten memory at the end of the allocated
block ( reason = 1 )
3) memory which is tried to be deleted but which is not allocated
(either because of a garbage pointer or twice deletion)
(reason = 2)
4) memory which is allocated with new[] but deleted with simple delete
and vice versa (reason = 4)
For the last three problems, LeakTracer can abort() your program if you
tell it so; the resulting core-dump allows to debug the problem. By default,
only the overwrite memory condition results in an abort of the program
because it is inherently critical. The two other conditions are not critical.
You can influence what LeakTracer does with the environment variable
LT_ABORTREASON
which you can set to some numeric value which is the result of the
sum of the reasons you find in the parentesis in the enumeration above.
To abort on any reason, for example, you would set LT_ABORTREASON to 7.
Analyzing output
----------------
You should then run leak-analyze, since looking at the raw leak.out file will
not help you much. To run leak-analyze, you need Perl as well as gdb
installed (any version of gdb will do). For example:
leak-analyze myprog leak.out
You don't have to specify the leak.out filename if you just use the default
one. leak-analyze will run gdb on the file, sending it a number of commands
that will show the source lines with the memory leaks.
leak-analyze should show you something like this:
Gathered 2 (2 unique) points of data.
#-- Alloc: Different allocation schemes
alloc here :0x80485b7 is in main (test.cc:6).
5
6 int *wrong = new int[10];
..free here :0x80485d9 is in main (test.cc:11).
11 delete wrong;
#-- Leak: Allocations: 1 / Size: 168
0x8048593 is in main (test.cc:3).
2 int main() {
3 int *array = new int [42] ;
#-- Leak: Allocations: 1 / Size: 4
0x80485a5 is in main (test.cc:4).
3 int *array = new int [42] ;
4 int *foo = new int;
This means that total of two allocations happened, in two different places.
First a delete error is shown: you allocated some memory using new[] but you
freed it using delete. leak-analyze will show where you allocated the memory and where you freed it.
Afterwards each allocation is shown in turn. There was 1 allocation from this
line of code (test.cc:3), and it was 168 bytes in size. Note that of the two
lines of code shown, it's the bottom one that created the allocation.
That's all there is to it - now you should find those memory leaks, fix them
and rerun Leak tracer.
Shared libraries and objects
----------------------------
If you want to analyze the leaks in shared libraries in your file, it may be
necessary to make leak-analyze run your program and thus load the shared
libraries before searching for addresses.
To do that, run leak-analyze with the program name, leak name AND another
argument which is where to set the breakpoint, e.g.:
leak-analyze myprog leak.out main
This will make leak-analyze tell gdb to set a breakpoint on "main" and then
run the program. After the analysis is complete, the program will be killed.
If you want to load some shared libraries, you can set a breakpoint on a
different location, e.g. main.cc:42 if you know that once line 42 is reached,
all shared objects have been loaded.
If your program needs some command line arguments, supply them after "main".
Licensing
---------
LeakTracer is public domain (i.e. do with it whatever you feel like).
Credits
-------
Initial version of LeakTracer was written by Erwin Andreasen. Henner Zeller
(foobar@to.com) contributed a rewrite of the code which
introduced dynamic loading of LeakTracer and more.
Revision history
----------------
February 21, 1999 v1.0 - only tested internally
February 23, 1999 v1.1 - added operator new[] / delete[]
February 23, 1999 v1.2 - Oops, forgot to free() the memory..
February 26, 1999 v1.3 - allow delete 0
March 27, 1999 v1.4 - Allow %p format without leading 0x for non-GNU
libc. Option to leak-analyze to run the program.
July 21, 1999 v1.5 - Fix for the above suggested by Alan Gonzalez
August 21, 2000 v1.6 - use a destructor instead of
__attribute__(destructor)
November 19, 2000 v2.0 - Rewrite by Henner Zeller introduces LD_PRELOAD
and much more
February 27, 2001 v2.1 - Further update by Henner: optional thread safety,
choose what should make LeakTracer abort(), better
tracing of delete on non-new'ed pointers
March 2, 2001 v2.2 - Another updated by Henner: hash table to increase
performance with many allocations
June 13, 2001 v2.3 - Made LT more resistant to being called before init
and after destruction
Authors: Erwin Andreasen <erwin@andreasen.org>
Henner Zeller <foobar@to.com>
Homepage: http://www.andreasen.org/LeakTracer/

View file

@ -0,0 +1,210 @@
<h3>Table of contents</h3> <UL>
<LI><a href="#chap0">Introduction</a>
<LI><a href="#chap1">Requirements</a>
<LI><a href="#chap2">Installation</a>
<LI><a href="#chap3">Running with LeakTracer</a>
<LI><a href="#chap4">Detectable errors</a>
<LI><a href="#chap5">Analyzing output</a>
<LI><a href="#chap6">Shared libraries and objects</a>
<LI><a href="#chap7">Licensing</a>
<LI><a href="#chap8">Credits</a>
<LI><a href="#chap9">Revision history</a>
</UL>
<PRE>
</PRE><h3><a name="chap0">Introduction</a></h3><PRE>
LeakTracer is a small tool I wrote when checking a C++ program for memory
leaks. I couldn't get dmalloc to display what I wanted, and I just saw the
__builtin_return_address gcc-extension mentioned.
To use LeakTracer, run your program using the provided LeakCheck script. It
uses the LD_PRELOAD feature to "overlay" some functions on top of your
functions (no recompile needed). If your platform does not support LD_PRELOAD,
you can add the LeakTracer.o object file to the objects in your Makefile and
run your application.
LeakTracer uses gdb to print out the exact line where the memory was allocated
and not freed - this of course means you have to free all dynamically
allocated data. LeakTracer also overrides the global operator new and operator
delete - this will give problems if you override them as well.
LeakTracer traces only new/new[] and delete calls - it does not look at
malloc/free/realloc.
Here is some example output:
Gathered 8 (8 unique) points of data.
(gdb)
Allocations: 1 / Size: 36
0x80608e6 is in NullArcableInstance::NullArcableInstance(void) (Machine.cc:40).
39 public:
40 NullArcableInstance() : ArcableInstance(new NullArcable) {}
Allocations: 1 / Size: 8
0x8055b02 is in init_types(void) (Type.cc:119).
118 void init_types() {
119 Type::Integer = new IntegerType;
Allocations: 1 / Size: 132 (new[])
0x805f4ab is in Hashtable<NativeCallable, String, false, true>::Hashtable(unsigned int) (ea/h/Hashtable.h:15).
14 Hashtable (uint _size = 32) : size(_size), count(0) {
15 table = new List<E, own> [size];
[...]
</PRE><h3><a name="chap1">Requirements</a></h3><PRE>
You need Perl5 and gdb installed to run the leak-analyzer. You need gcc -- I
currently use 2.95 but have used it with previous older versions without
problems.
You also need to run this on an architecture which supports
__builtin_return_address arguments that are greater than 0 - there may be
some problems on MIPS there.
So far this code has been tested under Linux 2.2, x86 system, Solaris and
HP-UX.
</PRE><h3><a name="chap2">Installation</a></h3><PRE>
Just type make. There is no install target; you should put LeakTracer
some place you can remember.
Since version 2.0, it is possible to preload the LeakTracer object on
architectures that support LD_PRELOAD (this is at least Linux and probably
others -- please report success/failure). This means it is much easier to use
the program: you do not need to relink your program with LeakTracer.o.
In case your platform does not support LD_PRELOAD, you can use LeakTracer in
the old pre 2.0 way: add LeakTracer.o to your object files -- at the very end
of them (also after -llibrary lines).
In any case your application must also be compiled with debugging enabled
(i.e. -g).
</PRE><h3><a name="chap3">Running with LeakTracer</a></h3><PRE>
If you are using the shared object, run the LeakCheck script. This script
should stay in the directory where you install LeakCheck -- it will search for
LeakTracer.so file there and load it. E.g.:
~/src/LeakTracer/LeakCheck yourApplication
(if you put LeakTracer in ~/src/LeakTracer/)
Run your application as normal, performing tasks that you want to be traced
for memory leaks. While the application runs, LeakTracer will write data about
memory allocation to the file "leak.out" in the current directory. You can
override the location of that file by setting the LEAKTRACE_FILE environment
variable.
If you cannot use LD_PRELOAD, just run your application as normal after
relinking it. It will also produce a "leak.out" file when it finishes.
</PRE><h3><a name="chap4">Detectable errors</a></h3><PRE>
LeakTracer is capable to detect the following problems with your program
1) memory which is allocated but not freed
2) (limited support for) overwritten memory at the end of the allocated
block ( reason = 1 )
3) memory which is tried to be deleted but which is not allocated
(either because of a garbage pointer or twice deletion)
(reason = 2)
4) memory which is allocated with new[] but deleted with simple delete
and vice versa (reason = 4)
For the last three problems, LeakTracer can abort() your program if you
tell it so; the resulting core-dump allows to debug the problem. By default,
only the overwrite memory condition results in an abort of the program
because it is inherently critical. The two other conditions are not critical.
You can influence what LeakTracer does with the environment variable
LT_ABORTREASON
which you can set to some numeric value which is the result of the
sum of the reasons you find in the parentesis in the enumeration above.
To abort on any reason, for example, you would set LT_ABORTREASON to 7.
</PRE><h3><a name="chap5">Analyzing output</a></h3><PRE>
You should then run leak-analyze, since looking at the raw leak.out file will
not help you much. To run leak-analyze, you need Perl as well as gdb
installed (any version of gdb will do). For example:
leak-analyze myprog leak.out
You don't have to specify the leak.out filename if you just use the default
one. leak-analyze will run gdb on the file, sending it a number of commands
that will show the source lines with the memory leaks.
leak-analyze should show you something like this:
Gathered 2 (2 unique) points of data.
#-- Alloc: Different allocation schemes
alloc here :0x80485b7 is in main (test.cc:6).
5
6 int *wrong = new int[10];
..free here :0x80485d9 is in main (test.cc:11).
11 delete wrong;
#-- Leak: Allocations: 1 / Size: 168
0x8048593 is in main (test.cc:3).
2 int main() {
3 int *array = new int [42] ;
#-- Leak: Allocations: 1 / Size: 4
0x80485a5 is in main (test.cc:4).
3 int *array = new int [42] ;
4 int *foo = new int;
This means that total of two allocations happened, in two different places.
First a delete error is shown: you allocated some memory using new[] but you
freed it using delete. leak-analyze will show where you allocated the memory and where you freed it.
Afterwards each allocation is shown in turn. There was 1 allocation from this
line of code (test.cc:3), and it was 168 bytes in size. Note that of the two
lines of code shown, it's the bottom one that created the allocation.
That's all there is to it - now you should find those memory leaks, fix them
and rerun Leak tracer.
</PRE><h3><a name="chap6">Shared libraries and objects</a></h3><PRE>
If you want to analyze the leaks in shared libraries in your file, it may be
necessary to make leak-analyze run your program and thus load the shared
libraries before searching for addresses.
To do that, run leak-analyze with the program name, leak name AND another
argument which is where to set the breakpoint, e.g.:
leak-analyze myprog leak.out main
This will make leak-analyze tell gdb to set a breakpoint on "main" and then
run the program. After the analysis is complete, the program will be killed.
If you want to load some shared libraries, you can set a breakpoint on a
different location, e.g. main.cc:42 if you know that once line 42 is reached,
all shared objects have been loaded.
If your program needs some command line arguments, supply them after "main".
</PRE><h3><a name="chap7">Licensing</a></h3><PRE>
LeakTracer is public domain (i.e. do with it whatever you feel like).
</PRE><h3><a name="chap8">Credits</a></h3><PRE>
Initial version of LeakTracer was written by Erwin Andreasen. Henner Zeller
(foobar@to.com) contributed a rewrite of the code which
introduced dynamic loading of LeakTracer and more.
</PRE><h3><a name="chap9">Revision history</a></h3><PRE>
February 21, 1999 v1.0 - only tested internally
February 23, 1999 v1.1 - added operator new[] / delete[]
February 23, 1999 v1.2 - Oops, forgot to free() the memory..
February 26, 1999 v1.3 - allow delete 0
March 27, 1999 v1.4 - Allow %p format without leading 0x for non-GNU
libc. Option to leak-analyze to run the program.
July 21, 1999 v1.5 - Fix for the above suggested by Alan Gonzalez
August 21, 2000 v1.6 - use a destructor instead of
__attribute__(destructor)
November 19, 2000 v2.0 - Rewrite by Henner Zeller introduces LD_PRELOAD
and much more
February 27, 2001 v2.1 - Further update by Henner: optional thread safety,
choose what should make LeakTracer abort(), better
tracing of delete on non-new'ed pointers
March 2, 2001 v2.2 - Another updated by Henner: hash table to increase
performance with many allocations
June 13, 2001 v2.3 - Made LT more resistant to being called before init
and after destruction
Authors: Erwin Andreasen <erwin@andreasen.org>
Henner Zeller <foobar@to.com>
Homepage: <a href="http://www.andreasen.org/LeakTracer/">http://www.andreasen.org/LeakTracer/</a>
</PRE>

View file

@ -0,0 +1 @@
2.3

View file

@ -0,0 +1,116 @@
#!/usr/bin/perl
#
# Modification History
#
# 2004-January-17 Jason Rohrer
# Fixed regexps to match both A-F and a-f for hex address strings.
#
# Erwin S. Andreasen <erwin@andreasen.org>
# Henner Zeller <foobar@to.com>
#
# Homepage: http://www.andreasen.org/LeakTracer/
# This program is Public Domain
use IO::Handle;
die "You must supply at least one argument.\n" unless $#ARGV >= 0;
$ExeFile = shift @ARGV;
$LeaksFile = $#ARGV >= 0 ? shift @ARGV : "leak.out";
open (LEAKS, $LeaksFile) or die "Could not open leaks data file $LeaksFile: $!";
if ($#ARGV >= 0) {
$BreakOn = shift @ARGV;
# Rest in @ARGV are program arguments
}
$n = $u = 0;
while (<LEAKS>) {
chop;
next if (m/^\s*#/);
# 1 2 3 4 5 6 7
#if (/^\s*L\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)\s+(\d+)/) {
# Allocations, which have not been freed or deallocations which have not
# been allocated.
# 1 2 3
if (/^\s*L\s+(0x)?([0-9a-fA-F]+)\s+(\d+)/) {
$addr="$2"; # ",$4,$6";
$u++ if not exists $Type{$addr};
$Count{$addr}++;
$Size{$addr} += $3; # $7;
$Type{$addr} = "Leak";
$n++;
}
elsif (/^\s*D\s+(0x)?([0-9a-fA-F]+)/) {
$addr="$2"; # ",$4,$6";
$u++ if not exists $Type{$addr};
$Count{$addr}++;
$Type{$addr} = "delete on not allocated memory";
$n++;
}
# allocations/deallocations with other errornous conditions
# 1 2 3 4 5
elsif (/^\s*([SO])\s+(0x)?([0-9a-fA-F]+)\s+(0x)?([0-9a-fA-F]+)/) {
$addrs = "$3,$5,$1";
$AllocDealloc{$addrs} = ("$1" =~ m/S/)
? "Different allocation schemes"
: "This Memory was overwritten";
}
}
print STDERR "Gathered $n ($u unique) points of data.\n";
close (LEAKS);
# Instead of using -batch, we just run things as usual. with -batch,
# we quit on the first error, which we don't want.
open (PIPE, "|gdb -q $ExeFile") or die "Cannot start gdb";
#open (PIPE, "|cat");
# Change set listsize 2 to something else to show more lines
print PIPE "set prompt\nset complaints 1000\nset height 0\n";
# Optionally, run the program
if (defined($BreakOn)) {
print PIPE "break $BreakOn\n";
print PIPE "run ", join(" ", @ARGV), " \n";
}
print PIPE "set listsize 2\n";
foreach (sort keys %AllocDealloc) {
print PIPE "echo \\n#-- Alloc: $AllocDealloc{$_}\\nalloc here :\n";
@addrs = split(/,/,$_);
print PIPE "l *0x" . (shift @addrs) . "\necho ..free here :\n";
print PIPE "set listsize 1\n";
print PIPE "l *0x" . (shift @addrs) . "\n";
}
foreach (sort keys %Type) {
print PIPE "echo \\n#-- $Type{$_}: counted $Count{$_}x";
if ($Size{$_} > 0) {
print PIPE " / total Size: $Size{$_}";
}
print PIPE "\\n\n";
@addrs = split(/,/,$_);
print PIPE "set listsize 2\n";
print PIPE "l *0x" . (shift @addrs) . "\n";
#print PIPE "echo ..called from :\n";
#print PIPE "set listsize 1\n";
# gdb bails out, if it cannot find an address.
#print PIPE "l *0x" . (shift @addrs) . "\necho ..called from :\n";
#print PIPE "l *0x" . (shift @addrs) . "\n";
}
if (defined($BreakOn)) {
print PIPE "kill\n";
}
print PIPE "quit\n";
PIPE->flush();
wait();
close (PIPE);

View file

@ -0,0 +1,27 @@
/*
* Modification History
*
* 2002-March-31 Jason Rohrer
* Added test of strdup support.
*/
#include <string.h>
// Small leaky test program
void foo() {
int *x = new int;
}
int main() {
char *str = strdup( "Test String" );
int *z = new int[10];
foo();
foo();
delete z;
delete z; // delete value twice
}

View file

@ -0,0 +1,277 @@
/*
* Modification History
*
* 2002-October-17 Jason Rohrer
* Created.
*
* 2002-October-18 Jason Rohrer
* Changed to use custom list instead of SimpleVector because SimpleVector
* uses debugMemory.
* Added static initialization counting class.
* Changed to use struct and malloc for AllocationList to avoid
* circular new and delete calls.
*
* 2002-October-19 Jason Rohrer
* Fixed a bug in adding to the alloc list.
* Improved printing behavior.
* Added support for clearing memory on allocation and deallocation.
* Fixed to ignore deallocation of our own static lock.
* Fixed bug in allocation count.
* Added message for NULL pointer deallocation.
* Put locks in place around print statements, which are not atomic on win32.
* Changed to use hex notation when printing pointers.
*
* 2002-October-19 Jason Rohrer
* Added ifdef for DEBUG_MEMORY.
*
* 2002-October-20 Jason Rohrer
* Removed file and line arguments from deallocation calls.
*/
#ifdef DEBUG_MEMORY
#include "minorGems/util/development/memory/MemoryTrack.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int MemoryTrackStaticInitCounter::mCount = 0;
MutexLock *MemoryTrack::mLock;
AllocationList *MemoryTrack::mListHead;
char MemoryTrack::mTracking = false;
int MemoryTrack::mTotalAllocationSize = 0;
int MemoryTrack::mTotalDeallocationSize = 0;
int MemoryTrack::mNumberOfAllocations = 0;
void MemoryTrack::addAllocation( void *inPointer,
unsigned int inAllocationSize,
int inAllocationType,
const char *inFileName,
int inLineNumber ) {
mLock->lock();
if( !mTracking ) {
printf( "Tracking off on allocation (0x%x) [%d bytes] %s:%d.\n",
(unsigned int)inPointer,
inAllocationSize,
inFileName, inLineNumber );
mLock->unlock();
return;
}
// insert after head of list
AllocationList *element =
(AllocationList *)malloc( sizeof( AllocationList ) );
element->mPrevious = (void *)mListHead;
element->mNext = mListHead->mNext;
mListHead->mNext = (void *)element;
AllocationList *nextElement = (AllocationList *)( element->mNext );
if( nextElement != NULL ) {
nextElement->mPrevious = (void *)element;
}
element->mPointer = inPointer;
element->mAllocationSize = inAllocationSize;
element->mAllocationType = inAllocationType;
element->mFileName = inFileName;
element->mLineNumber = inLineNumber;
mTotalAllocationSize += inAllocationSize;
mNumberOfAllocations ++;
// wipe this block of memory
clearMemory( inPointer, inAllocationSize );
mLock->unlock();
}
int MemoryTrack::addDeallocation( void *inPointer,
int inDeallocationType ) {
mLock->lock();
if( inPointer == NULL ) {
printf( "NULL pointer (0x%x) deallocated\n",
(unsigned int)inPointer );
}
if( inPointer == (void *)mLock ) {
// we're seeing the deallocation of our own static lock as
// the system exits
// ignore it
mLock->unlock();
return 0;
}
if( !mTracking ) {
printf( "Tracking off on deallocation (0x%x)\n",
(unsigned int)inPointer );
mLock->unlock();
return 0;
}
AllocationList *element = (AllocationList *)( mListHead->mNext );
while( element != NULL ) {
void *pointer = element->mPointer;
if( pointer == inPointer ) {
unsigned int allocationSize = element->mAllocationSize;
int allocationType = element->mAllocationType;
const char *allocFileName = element->mFileName;
int allocLineNumber = element->mLineNumber;
// remove from list, whether or not types match
AllocationList *previousElement =
(AllocationList *)( element->mPrevious );
AllocationList *nextElement =
(AllocationList *)( element->mNext );
// patch list
previousElement->mNext = (void *)( nextElement );
if( nextElement != NULL ) {
nextElement->mPrevious = (void *)( previousElement );
}
free( element );
mTotalDeallocationSize += allocationSize;
if( allocationType == inDeallocationType ) {
// found and types match
mLock->unlock();
// wipe this block of memory
clearMemory( inPointer, allocationSize );
return 0;
}
else {
// allocation types don't match
printf( "Attempt to deallocate (0x%x) [%d bytes] with wrong"
" delete form\n"
" %s:%d (location of original allocation)\n",
(unsigned int)inPointer,
allocationSize,
allocFileName, allocLineNumber );
mLock->unlock();
return 2;
}
}
element = (AllocationList *)( element->mNext );
}
// not found (delete of unallocated memory)
printf( "Attempt to deallocate (0x%x) unallocated memory\n",
(unsigned int)inPointer );
mLock->unlock();
return 1;
}
void MemoryTrack::printLeaks() {
mLock->lock();
printf( "\n\n---- debugMemory report ----\n" );
printf( "Number of Allocations: %d\n", mNumberOfAllocations );
printf( "Total allocations: %d bytes\n", mTotalAllocationSize );
printf( "Total deallocations: %d bytes\n", mTotalDeallocationSize );
int leakEstimate = mTotalAllocationSize - mTotalDeallocationSize;
AllocationList *element = (AllocationList *)( mListHead->mNext );
if( element == NULL ) {
printf( "No leaks detected.\n" );
}
else {
printf( "Leaks detected:\n" );
}
int leakSum = 0;
while( element != NULL ) {
printf( "Not deallocated (0x%x) [%d bytes]\n"
" %s:%d (location of original allocation)\n",
(unsigned int)( element->mPointer ),
element->mAllocationSize,
element->mFileName,
element->mLineNumber );
leakSum += element->mAllocationSize;
element = (AllocationList *)( element->mNext );
}
if( leakSum != leakEstimate ) {
printf( "Warning: Leak sum does not equal leak estimate.\n" );
}
printf( "Leaked memory: %d bytes\n", leakSum );
printf( "---- END debugMemory report ----\n\n" );
mLock->unlock();
}
void MemoryTrack::clearMemory( void *inPointer, unsigned int inSize ) {
unsigned char *charArray = (unsigned char *)inPointer;
for( unsigned int i=0; i<inSize; i++ ) {
charArray[i] = (unsigned char)0xAA;
}
}
#endif

View file

@ -0,0 +1,230 @@
/*
* Modification History
*
* 2002-October-17 Jason Rohrer
* Created.
*
* 2002-October-18 Jason Rohrer
* Changed to use custom list instead of SimpleVector because SimpleVector
* uses debugMemory.
* Added static initialization counting class.
* Changed to use struct and malloc for AllocationList to avoid
* circular new and delete calls.
*
* 2002-October-19 Jason Rohrer
* Changed to print leak list upon final destruction.
* Improved printing behavior.
* Added support for clearing memory on allocation and deallocation.
*
* 2002-October-20 Jason Rohrer
* Removed file and line arguments from deallocation calls.
*/
#ifndef MEMORY_TRACK_INCLUDED
#define MEMORY_TRACK_INCLUDED
#include "minorGems/system/MutexLock.h"
#include <stdio.h>
#include <stdlib.h>
#define SINGLE_ALLOCATION 0
#define ARRAY_ALLOCATION 1
/**
* Linked list of memory allocations.
*
* @author Jason Rohrer
*/
typedef struct {
void *mPrevious;
void *mNext;
void *mPointer;
unsigned int mAllocationSize;
int mAllocationType;
const char *mFileName;
int mLineNumber;
} AllocationList;
/**
* Class that tracks memory allocations and deallocations.
*
* @author Jason Rohrer
*/
class MemoryTrack {
public:
/**
* Adds an allocation to this tracker and clears the allocated
* memory block.
*
* @param inPointer a pointer to the allocated memory.
* @param inAllocationType the type of allocation,
* either SINGLE_ALLOCATION or ARRAY_ALLOCATION.
* @param inAllocationSize the size of the allocation in bytes.
* @param inFileName the name of the source file in which the
* allocation took place.
* @param inLineNumber the line number in the source file
* on which the allocation took place.
*/
static void addAllocation( void *inPointer,
unsigned int inAllocationSize,
int inAllocationType,
const char *inFileName,
int inLineNumber );
/**
* Adds a deallocation to this tracker and clears the block
* to be deallocated.
* Must be called *before* the memory is deallocated
*
* @param inPointer a pointer to the memory being deallocated.
* @param inDeallocationType the type of deallocation,
* either SINGLE_ALLOCATION or ARRAY_ALLOCATION.
* @return 0 if the deallocation deallocates
* an allocated block of memory, or 1 if it
* deallocates a block of memory that is not currently allocated,
* and 2 if it is the wrong deallocation type for the specified
* block.
*/
static int addDeallocation( void *inPointer, int inDeallocationType );
/**
* Prints a list of all memory leaks (allocations that have never
* been deallocated).
*/
static void printLeaks();
// these are public so initializer can get to them
static MutexLock *mLock;
// dummy place holder for list head
static AllocationList *mListHead;
// true if we're tracking
static char mTracking;
static int mTotalAllocationSize;
static int mTotalDeallocationSize;
static int mNumberOfAllocations;
protected:
/**
* Clears memory so that reading from it will not produce
* anything useful. Good for checking for reads to memory that
* has been deallocated.
*
* @param inPointer pointer to the memory to clear.
* @Param inSize the number of bytes to clear starting at inPointer.
*/
static void clearMemory( void *inPointer, unsigned int inSize );
};
/**
* Class that initializes MemoryTrack's static members.
*
* *All* files that use MemoryTrack will instantiate a static
* instance of this class (see static instance below).
*
* This class counts how many static instantiations have happened so
* far, making sure to init/destroy MemoryTrack's static members only once.
*
* Adapted from:
* http://www.hlrs.de/organization/par/services/tools/docu/kcc/
* tutorials/static_initialization.html
*/
class MemoryTrackStaticInitCounter {
public:
MemoryTrackStaticInitCounter() {
if( mCount == 0 ) {
// allocate static members
MemoryTrack::mLock = new MutexLock();
MemoryTrack::mListHead =
(AllocationList *)
malloc( sizeof( AllocationList ) );
MemoryTrack::mListHead->mPrevious = NULL;
MemoryTrack::mListHead->mNext = NULL;
MemoryTrack::mTotalAllocationSize = 0;
MemoryTrack::mTotalDeallocationSize = 0;
MemoryTrack::mNumberOfAllocations = 0;
MemoryTrack::mTracking = true;
}
mCount++;
}
~MemoryTrackStaticInitCounter() {
mCount--;
if( mCount == 0 ) {
// print leaks... we should only get here after
// all static members of classes that use MemoryTrack
// have been destroyed.
MemoryTrack::printLeaks();
MemoryTrack::mTracking = false;
// deallocate static members
free( MemoryTrack::mListHead );
delete MemoryTrack::mLock;
}
}
private:
// only allocate/deallocate when mCount == 0
static int mCount;
};
// This will be included in *every* file that includes MemoryTrack.h
static MemoryTrackStaticInitCounter memoryTrackInitializer;
#endif

View file

@ -0,0 +1 @@
g++ -Wall -g -DDEBUG_MEMORY -o testDebugMemory -I../../../.. debugMemory.cpp MemoryTrack.cpp testDebugMemory.cpp ../../../../minorGems/system/linux/MutexLockLinux.cpp

View file

@ -0,0 +1,95 @@
/*
* Modification History
*
* 2002-October-17 Jason Rohrer
* Created.
*
* 2002-October-18 Jason Rohrer
* Added static initialization counting class for MemoryTrack.
*
* 2002-October-19 Jason Rohrer
* Added more detail to error message.
* Improved printing behavior.
* Moved include of debugMemory.h to work better with IDE compilers.
* Fixed to deal with differences between malloc and new[] on some platforms.
*
* 2002-October-20 Jason Rohrer
* Removed delete macro trick that was causing crashes in tinyxml.
* Removed function that was no longer being used.
*/
#include "minorGems/util/development/memory/debugMemory.h"
#ifdef DEBUG_MEMORY
#include "minorGems/util/development/memory/MemoryTrack.h"
#include "stdlib.h"
#include "stdio.h"
void *debugMemoryNew( unsigned int inSize,
const char *inFileName, int inLine ) {
void *allocatedPointer = (void *)malloc( inSize );
MemoryTrack::addAllocation( allocatedPointer, inSize,
SINGLE_ALLOCATION,
inFileName, inLine );
return allocatedPointer;
}
void *debugMemoryNewArray( unsigned int inSize,
const char *inFileName, int inLine ) {
unsigned int mallocSize = inSize;
if( inSize == 0 ) {
// always allocate at least one byte to circumvent differences
// between malloc and new[] on some platforms
// (new int[0] returns a pointer to an array of length 0, while
// malloc( 0 ) can return NULL on some platforms)
mallocSize = 1;
}
void *allocatedPointer = (void *)malloc( mallocSize );
MemoryTrack::addAllocation( allocatedPointer, inSize,
ARRAY_ALLOCATION,
inFileName, inLine );
return allocatedPointer;
}
void debugMemoryDelete( void *inPointer ) {
MemoryTrack::addDeallocation( inPointer, SINGLE_ALLOCATION );
free( inPointer );
}
void debugMemoryDeleteArray( void *inPointer ) {
MemoryTrack::addDeallocation( inPointer, ARRAY_ALLOCATION );
free( inPointer );
}
#endif

View file

@ -0,0 +1,127 @@
/*
* Modification History
*
* 2002-October-17 Jason Rohrer
* Created.
*
* 2002-October-18 Jason Rohrer
* Added static initialization counting class for MemoryTrack.
*
* 2002-October-20 Jason Rohrer
* Removed delete macro trick that was causing crashes in tinyxml.
*/
#ifndef DEBUG_MEMORY_INCLUDED
#define DEBUG_MEMORY_INCLUDED
#ifdef DEBUG_MEMORY
#include "minorGems/util/development/memory/MemoryTrack.h"
// internal function prototypes
void *debugMemoryNew( unsigned int inSize,
const char *inFileName, int inLine );
void *debugMemoryNewArray( unsigned int inSize,
const char *inFileName, int inLine );
void debugMemoryDelete( void *inPointer );
void debugMemoryDeleteArray( void *inPointer );
// overrided primitive operators... must be inline?
/**
* Overrides the new operator to track memory allocations.
*
* @param inSize the size of the allocation.
* @param inFileName the name of the source file where the allocation
* occurred.
* @param inLine the line in the source file where the allocation occurred.
*/
inline void *operator new( unsigned int inSize,
const char *inFileName, int inLine ) {
return debugMemoryNew( inSize, inFileName, inLine );
}
/**
* Overrides the new [] operator to track memory allocations.
*
* @param inSize the size of the allocation.
* @param inFileName the name of the source file where the allocation
* occurred.
* @param inLine the line in the source file where the allocation occurred.
*/
inline void * operator new [] ( unsigned int inSize,
const char *inFileName, int inLine ) {
return debugMemoryNewArray( inSize, inFileName, inLine );
}
/**
* Overrides the delete operator to track memory allocations.
*
* @param inPointer a pointer to the memory to deallocate.
*/
inline void operator delete( void *inPointer ) {
debugMemoryDelete( inPointer );
}
/**
* Overrides the delete [] operator to track memory allocations.
*
* @param inPointer a pointer to the memory to deallocate.
*/
inline void operator delete [] ( void *inPointer ) {
debugMemoryDeleteArray( inPointer );
}
#endif
// macro trickery to pass file name and line number into new
#ifdef DEBUG_MEMORY
#define DEBUG_NEW new( __FILE__, __LINE__ )
#else
#define DEBUG_NEW new
#endif
#define new DEBUG_NEW
#endif

View file

@ -0,0 +1,85 @@
/*
* Modification History
*
* 2002-October-17 Jason Rohrer
* Created.
*
* 2002-October-18 Jason Rohrer
* Added static initialization counting class for MemoryTrack.
*
* 2002-October-19 Jason Rohrer
* Removed call to debugMemoryPrintLeaksAndStopTracking.
* Made test cases more interesting.
* Added test cases for deleting NULL pointers.
*/
#include "minorGems/util/development/memory/debugMemory.h"
#include <stdio.h>
class TestClass {
public:
TestClass() {
mX = new int[5];
}
~TestClass() {
delete [] mX;
}
int *mX;
};
int main() {
int *x = new int[5];
printf( "array contents before initializing elements:\n"
"%d, %d, %d, %d, %d\n\n",
x[0], x[1], x[2], x[3], x[4] );
x[0] = 1;
x[1] = 2;
x[2] = 3;
x[3] = 4;
x[4] = 5;
printf( "array contents before deleting:\n"
"%d, %d, %d, %d, %d\n\n",
x[0], x[1], x[2], x[3], x[4] );
delete [] x;
printf( "array contents after deleting:\n"
"%d, %d, %d, %d, %d\n\n",
x[0], x[1], x[2], x[3], x[4] );
int *y = new int[4];
y[0] = 1;
TestClass *t = new TestClass();
delete t;
int *z = new int[7];
delete z;
//delete t;
int *badPointer = NULL;
delete badPointer;
int *badPointer2 = NULL;
delete [] badPointer2;
return 0;
}