Delphi – FastMM: Using FastMM4 for debugging your memory allocations – part 1: Introduction
Posted by jpluimers on 2009/07/29
I’m using FastMM in our projects when debugging memory allocations.
It is a great tool, but documentation is sparse.
Hence this post: point to some introductory articles and add some of my own experiences.
Later on, I will show some more advanced use.
These posts are already available in this series:
- Delphi – FastMM: Using FastMM4 for debugging your memory allocations – part 1: Introduction
- Delphi – Using FastMM4 part 2: TDataModule descendants exposing interfaces, or the introduction of a TInterfacedDataModule
So now lets get on with the introduction:
There are already some basic introductions (at StackOverflow, or the EurekaLog blog).
You should read those first, then continue here.
If you like watching a video explanation, then you should download and watch “Fighting Memory Leaks for Dummies” by Francois Gaillard — WideOrbit, Inc. from the CodeRage II page.
First of all you need to download FastMM4 from the FastMM sourceforge project page. Although FastMM is the default memory mamanger since Delphi 2006, the one included with Delphi 2006 has fewer options than the current download.
Further, let me show our usual project directory structure:
...\Project\prj - project files ...\Project\src - regular sources and include files (notably Defines.inc) ...\Project\bin - project binaries (.exe, .dll, etc; FastMM_FullDebugMode.dll goes here) ...\Project\prj - unit and package binaries (.dcu, .bpl, .dcp)
When using FastMM in full debug mode, you need to copy FastMM_FullDebugMode.dll into the bin directory.
If you don’t, then you will get error messages like below when debugging your application:
--------------------------- Debugger Fault Notification --------------------------- Project C:\Documents and Settings\Developer\My Documents\RAD Studio\Projects\InterfacedDataModules\bin\InterfacedDataModulesProject.exe faulted with message: 'access violation at 0x7c937a50: write of address 0x00030d24'. Process Stopped. Use Step or Run to continue. --------------------------- OK ---------------------------
This meaningless message you can get when FastMM_FullDebugMode.dll cannot be found usually puts you into the wrong direction; if it is the first time you see such a message after enable FastMM, it isabout the FastMM_FullDebugMode.dll.
--------------------------- InterfacedDataModulesProject.exe - Unable To Locate Component --------------------------- This application has failed to start because FastMM_FullDebugMode.dll was not found. Re-installing the application may fix this problem. --------------------------- OK ---------------------------
This is the meaningfull message you can get when FastMM_FullDebugMode.dll cannot be found: it actually describes what goes wrong.
I have changed the FastMM4 files a bit in order to make it easier for our projects to configure.
All our project files contain a central Defines.inc include file.
For us, it is much easier to put conditional defines there, because if you change the file, all units will automatically be recompiled.
If you change your conditional defines in the ‘project options’ page of the IDE, you have to remember that you rebuild all your units.
It’s a matter of taste: include files are a bit slower, but for us they are fare more practical.
So, I changed the FastMM4Options.inc file like this:
- I added the line {$I Defines.inc}
- I changed the line {$define EnableMemoryLeakReporting} into {.$define EnableMemoryLeakReporting}
Then I added the file Defines.inc to the project:
{$ifdef DEBUG} {.$define EnableMemoryLeakReporting} {.$define FullDebugMode} {.$define RawStackTraces} {$endif DEBUG} {--- do not modify below this line ---} {$ifdef RawStackTraces} {$define FullDebugMode} {$endif RawStackTraces} {$ifdef FullDebugMode} {$define EnableMemoryLeakReporting} {$endif FullDebugMode}
For us, those are the most important options in FastMM.
When debugging, we usually enable EnableMemoryLeakReporting to show reports like below.
Since FullDebugMode can use a lot of of CPU power, we only enable that when needed: either because of a memory leak, or if we suspect other memory management issues (like memory overwrites, dangling pointers, or other forms of memory corruption).
We enable RawStackTraces only when we cannot find the errors in a regular way, as it uses even more CPU power.
A sample of stack traces is shown later in this blog post.
A quick sample of a memory leak is this one:
procedure TMainForm.SimulateLeakButtonClick(Sender: TObject); begin TButton.Create(nil); end;
When enabling FullDebugMode, this will result in a leak report dialog like this:
--------------------------- InterfacedDataModulesProject.exe: Memory Leak Detected --------------------------- This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer): 21 - 36 bytes: TPadding x 1, TBrush x 1, TFont x 1, TMargins x 1, TSizeConstraints x 1, Unknown x 1 53 - 68 bytes: Unknown x 1 597 - 660 bytes: TButton x 1 Note: Memory leak detail is logged to a text file in the same folder as this application. To disable this memory leak check, undefine "EnableMemoryLeakReporting". --------------------------- OK ---------------------------
Now in the same directory as your .exe (in this case InterfacedDataModulesProject.exe, there is a full report file named InterfacedDataModulesProject_MemoryManager_EventLog.txt, that contains sections like this:
A memory block has been leaked. The size is: 660 This block was allocated by thread 0x4C8, and the stack trace (return addresses) at the time was: 40305E 43FEAC 42B299 43FFFC 43FEAC 450FB0 43F5D3 41F926 7E418734 [Unknown function at GetDC] 7E418816 [Unknown function at GetDC] 7E41B89B [Unknown function at GetParent] The block is currently used for an object of class: TButton
The only useful thing here is TButton, but the memory addresses make no sense.
In order to have them make sense, you will need the Delphi linker to emit TD32 symbol information, which you can change in the project options like the screen dump on the right shows.
You can get partial information by enabling the detailed .MAP file in the same dialog, but the TD32 debug information is more extended, so you will now see detailed information like this:
A memory block has been leaked. The size is: 660 This block was allocated by thread 0x144, and the stack trace (return addresses) at the time was: 40305E [System][@GetMem] 44E218 [Controls][TWinControl.WndProc] 45A979 [StdCtrls][TButtonControl.WndProc] 44E368 [Controls][DoControlMsg] 44E218 [Controls][TWinControl.WndProc] 431828 [Forms][TCustomForm.WndProc] 44D93F [Controls][TWinControl.MainWndProc] 41F926 [Classes][StdWndProc] 7E418734 [Unknown function at GetDC] 7E418816 [Unknown function at GetDC] 7E41B89B [Unknown function at GetParent] The block is currently used for an object of class: TButton
This gets a bit better: you have unit names, but it still is not really good.
That’s why we are going to change a some of the compiler options as well:
- We disable “Optimization”
- We enable “Stack Frames”
- We enable “Debug DCUs’
Now the stack trace is like this:
A memory block has been leaked. The size is: 660 This block was allocated by thread 0x174, and the stack trace (return addresses) at the time was: 40305E [sys\system.pas][System][@GetMem][2654] 43C5E2 [Controls.pas][Controls][TControl.Click][5229] 43FF84 [Controls.pas][Controls][TWinControl.WndProc][7304] 42B371 [StdCtrls.pas][StdCtrls][TButtonControl.WndProc][3684] 4400D4 [Controls.pas][Controls][DoControlMsg][7353] 43FF84 [Controls.pas][Controls][TWinControl.WndProc][7304] 451088 [Forms.pas][Forms][TCustomForm.WndProc][3512] 43F6AB [Controls.pas][Controls][TWinControl.MainWndProc][7073] 41F926 [common\Classes.pas][Classes][StdWndProc][11583] 7E418734 [Unknown function at GetDC] 7E418816 [Unknown function at GetDC] The block is currently used for an object of class: TButton
Still not enough, so lets see what happens if we put the allocation in it’s own method:
procedure TMainForm.SimulateLeakButtonClick(Sender: TObject); begin LeakAButton; end; procedure TMainForm.LeakAButton; begin TButton.Create(nil); end;
Now the stack trace becomes this:
A memory block has been leaked. The size is: 660 This block was allocated by thread 0x3A0, and the stack trace (return addresses) at the time was: 40305E [sys\system.pas][System][@GetMem][2654] 45B05C [..\src\MainFormUnit.pas][MainFormUnit][TMainForm.SimulateLeakButtonClick][28] 44EEDA [Controls.pas][Controls][TControl.Click][5229] 45287C [Controls.pas][Controls][TWinControl.WndProc][7304] 42B371 [StdCtrls.pas][StdCtrls][TButtonControl.WndProc][3684] 4529CC [Controls.pas][Controls][DoControlMsg][7353] 45287C [Controls.pas][Controls][TWinControl.WndProc][7304] 432900 [Forms.pas][Forms][TCustomForm.WndProc][3512] 451FA3 [Controls.pas][Controls][TWinControl.MainWndProc][7073] 41F926 [common\Classes.pas][Classes][StdWndProc][11583] 7E418734 [Unknown function at GetDC] The block is currently used for an object of class: TButton
The reason that we do not see the LeakAButton in the stack trace is that the GetMem function in the System unit is compiled without proper stack frames.
What we learn from this is that it is not wise to put your code in event methods themselves: by putting them in another method, you get one extra level of stack tracing which can give you a clue where your actual leak is.
As an alternative measure, you can enable {$define RawStackTraces} in Defines.inc.
Then the stacktrace will look like this:
A memory block has been leaked. The size is: 660 This block was allocated by thread 0xC70, and the stack trace (return addresses) at the time was: 40305E [sys\system.pas][System][@GetMem][2654] 403C1B [sys\system.pas][System][TObject.NewInstance][8807] 403F8A [sys\system.pas][System][@ClassCreate][9472] 42B44E [StdCtrls.pas][StdCtrls][TButton.Create][3731] 45B075 [..\src\MainFormUnit.pas][MainFormUnit][TMainForm.LeakAButton][33] 45B05C [..\src\MainFormUnit.pas][MainFormUnit][TMainForm.SimulateLeakButtonClick][28] 43C5E2 [Controls.pas][Controls][TControl.Click][5229] 42B4C7 [StdCtrls.pas][StdCtrls][TButton.Click][3745] 42B5C5 [StdCtrls.pas][StdCtrls][TButton.CNCommand][3797] 43C0DA [Controls.pas][Controls][TControl.WndProc][5146] 7E426288 [Unknown function at IsDlgButtonChecked] The block is currently used for an object of class: TButton
This uses a bit more CPU than the other traces, but it will get you a much better stacktrace!
This introduction has shown you the basic steps to get FastMM working, and some of the settings you need to examine your leaks in more detail:
- Make sure that FastMM_FullDebugMode.dll is in the same directory as your .exe
- Enable EnableMemoryLeakReporting to see if there are leaks at all
- If there are leaks then enable FullDebugMode, TD32 debug information and set the right compiler settings to examine them in detail
- When you need more detail, enableRawStackTraces and be prepared to wait a bit longer because of the added CPU usage
Let me know if you have any FastMM questions, as I plan to write a few more blog articles about using and extending FastMM.
–jeroen
How to: Is there a way to programmatically tell if particular block of memory was not freed by FastMM? | SevenNet said
[…] https://wiert.me/2009/07/29/delphi-fastmm-using-fastmm4-for-debugging-your-memory-allocations-part-1-… […]
Vishal Tiwari said
it’s not working, using Delphi7. I followed all steps, but no success. cld u tell me where is Defines.inc ?
jpluimers said
You should be able to find a version Defines.inc here: http://bo.codeplex.com/SourceControl/changeset/view/80654#1278016
I’m not sure if that code is Delphi 7 compatible though, as it has been more than 5 years ago since I used Delphi 7 frequently.
blabla said
I am using it on Delphi 7 with no issues except that the setup instructions are not very clear. However, I got it. Here is what I did:
1. Follow the instructions as described in the readme file for FastMM4.
2. Make sure you modify FastMM4OPtions.inc file as described in the readme file.
3. Make sure you
3.1 enable {$define NoMessageBoxes} option (this will create a txt file with more info instead of showing leaks in message box which is also what you must do in case you are debugging service app)
3.2 disable {.$define RequireDebuggerPresenceForLeakReporting} option
This should get you going.
Thanks,
Dino
jpluimers said
Thanks for the instructions.
I need to check the NoMessageBoxes, as I think I have both the log file and NoMessageBoxes enabled, and I think all the required information is already in the log file.
Regards,
–jeroen
Delphi MemCheck 2.75 und Delphi XE2 - Seite 2 - Delphi-PRAXiS said
[…] […]
Paulo Eduardo Neves said
I can’t register the FastMM_FullDebugMode.dll with Regsvr32 since I’m not administrator of the machine. Would it still work? I’m having problems configuring it.
jpluimers said
Not sure, please ask that again on stackoverflow.
–jeroen
dino said
I am using Delphi 7 and am trying to use FastMM on a windows service application (non-gui). I followed all steps explained but with no success. I run the win service app by attaching it to a process (a gui app) (Menu->Run->Attach to Process).
I was able to make it work fine with regular Windows Forms applications but no success with windows service app.
Anyone who tried the same scenario had any luck?
Much appreciated
dino
jpluimers said
Services don’t have a UI (unless you fiddle with desktop interaction), so they can’t show the dialog.
Often services run with with reduced privileges, so they can’t always write the files indicating the memory leaks.
Dino said
FastMM4Options.inc has an option to supress message box which i have enabled so that should not be an issue. The option is {$define NoMessageBoxes}.
In regards to priviledges, I doubt this is an issue as well as I can make my service write a text file to its directory, so writing report with memory leaks should not be any different either. I have also introduced memory leaks to make sure there are any.
Dino said
Quick update on this should anyone else have same issues. I got it working for Win Services as well.
1. Follow the instructions as described in the readme file for FastMM4.
2. Make sure you modify FastMM4OPtions.inc file as described in the readme file.
3. Meke sure you
3.1 enable {$define NoMessageBoxes} option
3.2 disable {.$define RequireDebuggerPresenceForLeakReporting} option
This should get you going.
Thanks,
Dino
Anonymous said
[…] […]
my 2010 blog in review « The Wiert Corner – irregular stream of Wiert stuff said
[…] twitter kylix_rd (Allen Bauer)Streaming your mp3 collection through an Icecast server using ezstreamDelphi – FastMM: Using FastMM4 for debugging your memory allocations – part 1: IntroductionVMware ESXi 4.0 / ESXi 4.1: enable SSH login for non-root users (and only them)Google Calendar – […]
John said
Hi,
Does it (FastMM4) work on C++ Builder?
On a side note, ReportMemoryLeaksOnShutdown was still not working on C++ BDS2007 (http://qc.embarcadero.com/wc/qcmain.aspx?d=22963). I’ve been trying since ever to get something like
System::ReportMemoryLeaksOnShutdown = IsDebuggerPresent();
to work, but with no luck :|
So, maybe replacing built-in FastMM by FastMM4.pas will solve it…
Thank you for the insight.
jpluimers said
FastMM4 should work with C++ builder; I browsed through the sources and their seem to be some portions dedicated to C++ specific memory handling.
That bug is a good one.
I never notices, as I never used the internal one for debugging; I always used the full version :-)
Thanks for the tip!
–jeroen
John said
Hi Jeroen,
Just to let you know that I’ve finally tried FastMM 4.92 with C++ RAD 2007, and it (mostly) works.
Regarding QC22963, I’ve got word that it has not been fixed yet for RAD 2010 (sadly, has been deferred… again).
Thanks for all the info :)
John said
Just an update. An year later and QC22963 (ReportMemoryLeaksOnShutdown) is still open on the brand new XE :(
jpluimers said
I’m not into C++, so I can’t check if it is still wrong and what it would take to fix it.
The best way to get QC reports a higher priority is to have people vote for your reports.
–jeroen
Delphi – Using FastMM4 part 3: wrong persistent field type (TSmallIntField or TWordField) when getting IsNull results in memory corruption « The Wiert Corner – Jeroen Pluimers’ irregular stream of Wiert stuff said
[…] data fr…jpluimers on Delphi – Using FastMM4 p…Delphi – Using… on Delphi – FastMM: Using F…Delphi – FastM… on Delphi – Using FastMM4 […]
Delphi – Using FastMM4 part 2: TDataModule descendants exposing interfaces, or the introduction of a TInterfacedDataModule « The Wiert Corner – Jeroen Pluimers’ irregular stream of Wiert stuff said
[…] Comments Delphi – FastM… on Delphi – Using FastMM4 p…Torbins on Delphi – Using FastMM4 p…jpluimers […]
John G said
Damn useful man!
Please provide a pdf(scribd) version of this article!
jpluimers said
Thx!
I’ve never used scribd, do you have some steps to get that done as easy as possible?
–jeroen