Initial source commit
This commit is contained in:
commit
f1384c11ee
335 changed files with 52715 additions and 0 deletions
746
minorGems/util/development/fortify/FORTIFY.DOC
Normal file
746
minorGems/util/development/fortify/FORTIFY.DOC
Normal 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.
|
||||
|
73
minorGems/util/development/fortify/Makefile
Normal file
73
minorGems/util/development/fortify/Makefile
Normal 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
|
2339
minorGems/util/development/fortify/fortify.cpp
Normal file
2339
minorGems/util/development/fortify/fortify.cpp
Normal file
File diff suppressed because it is too large
Load diff
273
minorGems/util/development/fortify/fortify.h
Normal file
273
minorGems/util/development/fortify/fortify.h
Normal 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__ */
|
61
minorGems/util/development/fortify/test.c
Normal file
61
minorGems/util/development/fortify/test.c
Normal 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;
|
||||
}
|
66
minorGems/util/development/fortify/test2.cpp
Normal file
66
minorGems/util/development/fortify/test2.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
57
minorGems/util/development/fortify/test3.cpp
Normal file
57
minorGems/util/development/fortify/test3.cpp
Normal 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;
|
||||
}
|
79
minorGems/util/development/fortify/ufortify.h
Normal file
79
minorGems/util/development/fortify/ufortify.h
Normal 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 */
|
22
minorGems/util/development/leakTracer/LeakCheck
Executable file
22
minorGems/util/development/leakTracer/LeakCheck
Executable 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 $@
|
13
minorGems/util/development/leakTracer/LeakCheckAnalyze
Executable file
13
minorGems/util/development/leakTracer/LeakCheckAnalyze
Executable 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
|
578
minorGems/util/development/leakTracer/LeakTracer.cc
Normal file
578
minorGems/util/development/leakTracer/LeakTracer.cc
Normal 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:
|
||||
*/
|
64
minorGems/util/development/leakTracer/Makefile
Normal file
64
minorGems/util/development/leakTracer/Makefile
Normal 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
|
230
minorGems/util/development/leakTracer/README
Normal file
230
minorGems/util/development/leakTracer/README
Normal 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/
|
||||
|
210
minorGems/util/development/leakTracer/README.html
Normal file
210
minorGems/util/development/leakTracer/README.html
Normal 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>
|
1
minorGems/util/development/leakTracer/VERSION
Normal file
1
minorGems/util/development/leakTracer/VERSION
Normal file
|
@ -0,0 +1 @@
|
|||
2.3
|
116
minorGems/util/development/leakTracer/leak-analyze
Executable file
116
minorGems/util/development/leakTracer/leak-analyze
Executable 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);
|
27
minorGems/util/development/leakTracer/test.cc
Normal file
27
minorGems/util/development/leakTracer/test.cc
Normal 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
|
||||
|
||||
|
||||
}
|
277
minorGems/util/development/memory/MemoryTrack.cpp
Normal file
277
minorGems/util/development/memory/MemoryTrack.cpp
Normal 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
|
||||
|
||||
|
||||
|
||||
|
230
minorGems/util/development/memory/MemoryTrack.h
Normal file
230
minorGems/util/development/memory/MemoryTrack.h
Normal 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
|
||||
|
||||
|
1
minorGems/util/development/memory/compileTestDebugMemory
Executable file
1
minorGems/util/development/memory/compileTestDebugMemory
Executable file
|
@ -0,0 +1 @@
|
|||
g++ -Wall -g -DDEBUG_MEMORY -o testDebugMemory -I../../../.. debugMemory.cpp MemoryTrack.cpp testDebugMemory.cpp ../../../../minorGems/system/linux/MutexLockLinux.cpp
|
95
minorGems/util/development/memory/debugMemory.cpp
Normal file
95
minorGems/util/development/memory/debugMemory.cpp
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
127
minorGems/util/development/memory/debugMemory.h
Normal file
127
minorGems/util/development/memory/debugMemory.h
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
85
minorGems/util/development/memory/testDebugMemory.cpp
Normal file
85
minorGems/util/development/memory/testDebugMemory.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue