Dec 282017
Check for memory leaks in Turbo Vision 2.0. | |||
---|---|---|---|
File Name | File Size | Zip Size | Zip Type |
MEMCHECK.CPP | 1297 | 423 | deflated |
MEMCHECK.H | 713 | 329 | deflated |
READ.ME | 5739 | 2518 | deflated |
Download File TVMEMCHK.ZIP Here
Contents of the READ.ME file
Patrick Reilly
70274,161BCPPDOS forum
This file is released to the public domain.
I developed the MemCheck class (contained in MEMCHECK.H and MEMCHECK.CPP)
as an aid for debugging Turbo Vision programs that have memory leaks. Using
it can help isolate the source of the memory leak.
MemCheck is a fairly simple class.
mark()
This method returns 0 on failure, non-zero on success. It: ensures
the heap is not corrupt (fails if corrupt), allocates a block of memory
(fails if allocation failed), and then stores the pointers to all heap-
allocated memory (except the internal items alloc pointer). In other
words, it stores the 'state' of the heap with respect to allocated blocks.
release()
This method returns 0 on failure, non-zero on success. It: is
somewhat similar to mark(), but release() does NOT store the pointers
that were recorded with mark(). In other words, after release(),
MemCheck contains all of the pointers that were allocated since a call
to mark(). This is the whole purpose for MemCheck: call mark() prior to
running code suspected of leaking memory, and call release() after; if
MemCheck has pointers, that code allocated some memory and didn't free
it up. Using this at finer and finer levels of the code will isolate
the area that is leaking memory.
getCount()
This method returns the number of pointers currently in MemCheck.
Though it CAN be called at any time, it was designed to be called after
a call to release(): if getCount() > 0 then there's pointers in MemCheck
and a memory leak between the mark() call and the release() call.
forEach( mcAppFunc app, void * arg )
mcAppFunc is typedef'd as: typedef void (*mcAppFunc)(void *,void *)
This method is an iterator: when called, it will apply a pointer from
MemCheck and the argument to the function. This is in case
you want to print the pointers, or something like that.
isItem( void *item )
This method returns true (non-zero) if item is equal to one of the
pointers in MemCheck, false (0) otherwise. ONE WORD OF CAUTION!!!!!!
If NDEBUG was **NOT** defined when TV.LIB was created, then the global
new operator actually returned pointers that were different that the
heap pointer: for example, isItem(p) will FAIL if p was allocated with
new and NDEBUG was not defined. In this case, you have to subtract 16
from the pointer and check for that: ie isItem(p-16);
clear()
This method clears MemCheck, returning any allocated memory to the
heap. The MemCheck destructor calls clear, but you can call it manually
to clear the instance before reusing.
That's all there is to it! An example:
static void printPointers( void *pi, void *ps )
{
*((ostream *)ps) << pi << endl;
}
void TMyApp::handleEvent( TEvent& event )
{
TApplication::handleEvent(event);
if( event.what == evCommand && event.message.command == cmDoDialog )
{
MemCheck mem;
prnstream ps(0);
mem.mark();
TMyDialog *pd = new TMyDialog;
if( validView(pd) != 0 )
{
deskTop->execView(pd);
destroy(pd);
}
mem.release();
if( mem.getCount() )
{
ps << "Leftover pointers:" << endl;
mem.forEach( printPointers, &ps );
ps << '\f' << flush;
messageBox( "TMyDialog leaks!", mfError|mfOKButton);
}
mem.clear();
clearEvent(event);
}
}
In this example, a new class TMyDialog (derived from TDialog) had been
developed, but after putting it in use (the above code without the MemCheck
stuff) the heap viewer started showing a memory leak. Of course one of those
leaks is from the TMyDialog::getPalette() method (and expected). To verify
that the TMyDialog was leaking, MemCheck was installed as above. If the
messageBox pops up on execution, then - yes, TMyDialog has allocated
something and neglected to free it. Now you can strip this MemCheck stuff out
and start putting some in the TMyDialog code to try and narrow down what is
causing the leak. As an added benefit, we also use a printer stream (in
PRNSTR.ZIP: library 11 of BCPPDOS forum on CIS) and print all the pointers
that were left over, using forEach() and printPointers(). This way we could
step through the TMyDialog code and record the pointers that were allocated;
when the above prints the list, we can go through and see which from our
list is in the printed list of leftovers.
Judicious use of the printer stream could have automated this even
further; in the TMyDialog ctor (assumed to do the allocation), we could have
put in things like:
TMyDialog::TMyDialog() :
TDialog( TRect(0,0,60,9), "My Dialog" ),
TWindowInit( &TMyDialog::initFrame )
{
prnstream ps(0);
...
char *p = newStr( "Hello, world!" );
ps << (void *)p << " p allocated with newStr" << endl;
...
}
This would record (print) the pointers and who they're allocated to for us.
Last note: MemCheck is ONLY a debugging tool - I do not recommend
sprinkling its use throughout a finished product. Though it was designed with
Turbo Vision in mind, its quite usable in any program that ultimately uses
the run time library memory allocation functions. It IS public domain, so
feel free to modify the code as necessary to suit your particular task. The
only thing I ask is that if you distribute MemCheck elsewhere, include the
files READ.ME (this file), MEMCHECK.H, and MEMCHECK.CPP in their original
form; you can ADD files if you'd like, but I don't want to try answering
messages from people for code inserted that I know nothing about: if the
additions are in seperate files, I can point out that I didn't write them.
Pat Reilly
70274,161BCPPDOS forum
This file is released to the public domain.
I developed the MemCheck class (contained in MEMCHECK.H and MEMCHECK.CPP)
as an aid for debugging Turbo Vision programs that have memory leaks. Using
it can help isolate the source of the memory leak.
MemCheck is a fairly simple class.
mark()
This method returns 0 on failure, non-zero on success. It: ensures
the heap is not corrupt (fails if corrupt), allocates a block of memory
(fails if allocation failed), and then stores the pointers to all heap-
allocated memory (except the internal items alloc pointer). In other
words, it stores the 'state' of the heap with respect to allocated blocks.
release()
This method returns 0 on failure, non-zero on success. It: is
somewhat similar to mark(), but release() does NOT store the pointers
that were recorded with mark(). In other words, after release(),
MemCheck contains all of the pointers that were allocated since a call
to mark(). This is the whole purpose for MemCheck: call mark() prior to
running code suspected of leaking memory, and call release() after; if
MemCheck has pointers, that code allocated some memory and didn't free
it up. Using this at finer and finer levels of the code will isolate
the area that is leaking memory.
getCount()
This method returns the number of pointers currently in MemCheck.
Though it CAN be called at any time, it was designed to be called after
a call to release(): if getCount() > 0 then there's pointers in MemCheck
and a memory leak between the mark() call and the release() call.
forEach( mcAppFunc app, void * arg )
mcAppFunc is typedef'd as: typedef void (*mcAppFunc)(void *,void *)
This method is an iterator: when called, it will apply a pointer from
MemCheck and the
you want to print the pointers, or something like that.
isItem( void *item )
This method returns true (non-zero) if item is equal to one of the
pointers in MemCheck, false (0) otherwise. ONE WORD OF CAUTION!!!!!!
If NDEBUG was **NOT** defined when TV.LIB was created, then the global
new operator actually returned pointers that were different that the
heap pointer: for example, isItem(p) will FAIL if p was allocated with
new and NDEBUG was not defined. In this case, you have to subtract 16
from the pointer and check for that: ie isItem(p-16);
clear()
This method clears MemCheck, returning any allocated memory to the
heap. The MemCheck destructor calls clear, but you can call it manually
to clear the instance before reusing.
That's all there is to it! An example:
static void printPointers( void *pi, void *ps )
{
*((ostream *)ps) << pi << endl;
}
void TMyApp::handleEvent( TEvent& event )
{
TApplication::handleEvent(event);
if( event.what == evCommand && event.message.command == cmDoDialog )
{
MemCheck mem;
prnstream ps(0);
mem.mark();
TMyDialog *pd = new TMyDialog;
if( validView(pd) != 0 )
{
deskTop->execView(pd);
destroy(pd);
}
mem.release();
if( mem.getCount() )
{
ps << "Leftover pointers:" << endl;
mem.forEach( printPointers, &ps );
ps << '\f' << flush;
messageBox( "TMyDialog leaks!", mfError|mfOKButton);
}
mem.clear();
clearEvent(event);
}
}
In this example, a new class TMyDialog (derived from TDialog) had been
developed, but after putting it in use (the above code without the MemCheck
stuff) the heap viewer started showing a memory leak. Of course one of those
leaks is from the TMyDialog::getPalette() method (and expected). To verify
that the TMyDialog was leaking, MemCheck was installed as above. If the
messageBox pops up on execution, then - yes, TMyDialog has allocated
something and neglected to free it. Now you can strip this MemCheck stuff out
and start putting some in the TMyDialog code to try and narrow down what is
causing the leak. As an added benefit, we also use a printer stream (in
PRNSTR.ZIP: library 11 of BCPPDOS forum on CIS) and print all the pointers
that were left over, using forEach() and printPointers(). This way we could
step through the TMyDialog code and record the pointers that were allocated;
when the above prints the list, we can go through and see which from our
list is in the printed list of leftovers.
Judicious use of the printer stream could have automated this even
further; in the TMyDialog ctor (assumed to do the allocation), we could have
put in things like:
TMyDialog::TMyDialog() :
TDialog( TRect(0,0,60,9), "My Dialog" ),
TWindowInit( &TMyDialog::initFrame )
{
prnstream ps(0);
...
char *p = newStr( "Hello, world!" );
ps << (void *)p << " p allocated with newStr" << endl;
...
}
This would record (print) the pointers and who they're allocated to for us.
Last note: MemCheck is ONLY a debugging tool - I do not recommend
sprinkling its use throughout a finished product. Though it was designed with
Turbo Vision in mind, its quite usable in any program that ultimately uses
the run time library memory allocation functions. It IS public domain, so
feel free to modify the code as necessary to suit your particular task. The
only thing I ask is that if you distribute MemCheck elsewhere, include the
files READ.ME (this file), MEMCHECK.H, and MEMCHECK.CPP in their original
form; you can ADD files if you'd like, but I don't want to try answering
messages from people for code inserted that I know nothing about: if the
additions are in seperate files, I can point out that I didn't write them.
Pat Reilly
December 28, 2017
Add comments