A FreeBasic Memory Leak Detector

Written by Daniel Verkamp (DrV)

INTRODUCTION

FreeBasic, as you might already know, includes a C-like memory management system, which includes full-fledged pointers, as well as routines to allocate blocks of memory dynamically at runtime. While this leads to great flexibility for the programmer, it also creates the possibility to "shoot yourself in the foot", a la Stroustrup. If you choose to use pointers and dynamic memory allocation, FreeBasic will not protect you from yourself. Using pointers, you can easily access memory that you have not allocated for your use; the compiler cannot check for out-of-bounds accesses because it cannot know the limits of a particular pointer. However, you can allocate a block of memory and forget to deallocate it, or you can try to free the same block of memory twice; this sort of problem is more easily detected. In fact, this article describes the use and implementation of just this sort of tool - a memory leak detector for FreeBasic.

USAGE

The FreeBasic Memory Leak Detector (FBMLD) is very simple to use; just include the header fbmld.bi in each source file of your program:

#include "fbmld.bi"
Then use FreeBasic's built-in memory management routines Allocate, CAllocate, Reallocate, and Deallocate as you would normally. When you run your program, FBMLD will print out diagnostic messages starting with (FBMLD) to inform you of memory management errors in your code.

As an example, here is a (fabricated) example program with some memory management problems:

Option Explicit #include "fbmld.bi" Dim George As Any Ptr, John As Any Ptr, Paul As Any Ptr, Ringo As Any Ptr Print "FreeBasic Memory Leak Detector test" George = CAllocate(123) John = Allocate(456) George = ReAllocate(George, 789) Deallocate(John) Paul = Reallocate(0, 5309) Deallocate(George) Deallocate(George) ' Note that this is a "double free" - we are ' attempting to deallocate George, which has ' already been deallocated. ' ' This will be caught and reported immediately. Ringo = Allocate(867) ' We have not deallocated Paul or Ringo, so ' they will be reported as allocated but ' not deallocated. Print "fbmld test - End"

When the second Deallocate(George) is executed, FBMLD will print:

(FBMLD) test.bas(20): Deallocate on unallocated memory (&H322630)
George has been deallocated already, so trying to execute it again is not acceptable. In this case, the problem is simple to solve - just remove the second Deallocate(George) line. Some other cases might require more thought, such as when a particular block of memory could be deallocated in more than one place in the code.

When the program ends, two errors relating to memory that has been allocated but not deallocated will be printed:

(FBMLD) test.bas(28): 867 bytes allocated here was not deallocated (&H3265F8) (FBMLD) test.bas(17): 5309 bytes allocated here was not deallocated (&H323FE0)
These bugs can be fixed by adding Deallocate(Paul) and Deallocate(Ringo) to the end of the program.

Note that the messages will not necessarily contain the exact same memory addresses as given here; the address is printed as a debugging aid only and should not be assumed to be constant.

IMPLEMENTATION

FBMLD is written in FreeBasic and contained in a single header file, requiring no extra libraries or external resources beyond the standard C library, which is always linked (FreeBasic's runtime library depends on it). The standard memory management functions are removed from the namespace with the Option NoKeyword statement. Then these keywords were redefined with #define to call replacement functions, using the predefined __FILE__ and __LINE__ preprocessor defines to track where each function was called. These replacement functions use the standard C library functions malloc(), calloc(), realloc(), and free(). Each function is declared Private to allow the use of FBMLD in projects with multiple source code files.

A global list is maintained of all allocated memory blocks, and when any memory-management function is called, the list is manipulated accordingly. If allocated memory is freed correctly, it is removed from the list. If any elements remain in the list at the end of the program, they are reported as being allocated but not deallocated.

For thread safety, the built-in mutex routines are used. If you do not wish your program to be linked to the multithreaded library, just define FBMLD_NO_MULTITHREADING before including the header:

#define FBMLD_NO_MULTITHREADING #include "fbmld.bi"

Normally, FBMLD will print all of its messages with the standard Print statement; if you would like it to output the messages somewhere else (a file, for example), you can modify fbmld_print accordingly. Just keep in mind that the report at program termination might happen after the FreeBasic runtime's termination code has already been executed (file handles closed and so forth).

Conclusion

FBMLD was written by Daniel Verkamp. You can contact him at i_am_drv@users.sourceforge.net. You can download the fbmld.bi header here.