The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My work

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,311 other followers

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:

  1. Delphi – FastMM: Using FastMM4 for debugging your memory allocations – part 1: Introduction
  2. 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

23 Responses to “Delphi – FastMM: Using FastMM4 for debugging your memory allocations – part 1: Introduction”

  1. […] https://wiert.me/2009/07/29/delphi-fastmm-using-fastmm4-for-debugging-your-memory-allocations-part-1-… […]

  2. 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

  3. […] […]

  4. 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.

  5. 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

  6. Anonymous said

    […] […]

  7. […] 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 – […]

  8. 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

  9. […] data fr…jpluimers on Delphi – Using FastMM4 p…Delphi – Using… on Delphi – FastMM: Using F…Delphi – FastM… on Delphi – Using FastMM4 […]

  10. […] Comments Delphi – FastM… on Delphi – Using FastMM4 p…Torbins on Delphi – Using FastMM4 p…jpluimers […]

  11. John G said

    Damn useful man!

    Please provide a pdf(scribd) version of this article!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: