2

In order to prune out all of memory leaks I am trying to incorporate LeakCheck library into DUnit. The problem is that LeakCheck will report me memory leaks that are not true leaks. These are objects or other stuff allocated by RTL and destroyed on program exit.

LeakCheck contains a lot of handful routines that allow you to specify what to ignore and I was able to use them to ignore most of such "leaks". However, I don't know how to get rid one in particular:

program LeakCheckMemLeak;
{$APPTYPE CONSOLE}
uses
  LeakCheck, TestFramework, LeakCheck.DUnit, LeakCheck.Utils,  LeakCheck.Setup.Trace, System.SysUtils,
  Forms, System.Classes;

{$R *.RES}

procedure LeakMemory;
var
  LThread: TThread;
begin
  LThread := TThread.Create(True);
  LThread.Free;
end;

procedure DetectLeak;
var
  Snapshot: TLeakCheck.TSnapshot;
  Report: LeakString;
begin
  Snapshot.Create;
  LeakMemory;
  Report := TLeakCheck.GetReport(Snapshot.Snapshot);
  try
    Writeln(string(Report));
  finally
    Report.Free;
  end;
end;

begin
  Application.Initialize;
  DetectLeak;
  Readln;
end.

Creating instance of TThread (originally it was TThread.CreateAnonymousThread but results are the same) causes 64 bytes of memory leak:

Total allocation count: 297 (12592 B)
Leak detected 02BB3DC0 size 64 B
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 E8 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ????????????????????????????????
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ????????????????????????????????
Stack trace when the memory block was allocated:
  $00406E76 - LeakCheckMemLeak.exe - System.AllocMem + $A (4562 +25)
  $004C4A53 - LeakCheckMemLeak.exe - System.Classes.TThread.WaitFor + $8F (15565 +23)
  $005E4EB0 - LeakCheckMemLeak.exe - LeakCheckMemLeak.DetectLeak + $34 (24 +3)
  $005ED5B9 - LeakCheckMemLeak.exe - LeakCheckMemLeak.LeakCheckMemLeak + $29 (35 +3)
  $763E343D - kernel32.dll
  $76F19832 - ntdll.dll

In other cases LeakCheck provided me name of class that instance leaked so I could add it to list of ignored, but in this case it does not. How can I suppress this "leak"?

One a side note, this and other leaks that I've encountered don't happen in GUI application. I guess, RTL preallocates memory for most of these objects before tests run.

Wodzu
  • 6,932
  • 10
  • 65
  • 105
  • did you try built-in fastmm leakchecking? – whosrdaddy Jul 18 '18 at 13:42
  • _The problem is that LeakCheck will report me memory leaks that are not true leaks. These are objects or other stuff allocated by RTL and destroyed on program exit._ – Wodzu Jul 18 '18 at 14:18
  • afaik FastMM is rather useless for unit tests because it does not give you detailed information between two points in time which you need here to detect leaks happening during test execution. – Stefan Glienke Jul 18 '18 at 14:26
  • LeakCheck supports the RTL's [`System.RegisterExpectedMemoryLeak()`](http://docwiki.embarcadero.com/Libraries/en/System.RegisterExpectedMemoryLeak) function. Pass in the address of whatever memory block(s) you want to ignore in leak reports. – Remy Lebeau Jul 18 '18 at 21:38
  • @RemyLebeau when I use this function there is a chance that address will change when I change something in my class under test or the test itself. Right? – Wodzu Jul 19 '18 at 10:10
  • @Wodzu as its name implies, only use the function for **expected** leaks. If your test causes a memory address to change, that address is not a good candidate for this function unless that change is intentional and creates a leak on purpose. This function is intended to be called at runtime after a memory address is allocated that is intended to be subsequently leaked. – Remy Lebeau Jul 19 '18 at 15:23

1 Answers1

2

FWIW the results I get after removing Forms and Application.Initialize is this:

Total allocation count: 113 (4152 B)
Leak detected 0262F540 size 44 B for class: TExternalThread
Leak detected 0260A988 size 20 B for class: TThreadList<System.Classes.TThread>
Leak detected 02618A90 size 8 B for class: TObject
Leak detected 026451F8 size 52 B for class: TList<System.Classes.TThread>
Leak detected 02618AC8 size 12 B
  01 00 00 00 01 00 00 00 40 F5 62 02 | ????????@?b?

I know from using LeakCheck in Spring4D unit tests that this comes from the lazy initialization of some instances in TThread.GetCurrentThread which gets called during your LeakMemory routine - more precisely during TThread.Destroy which calls WaitFor (see System.Classes.pas line 15764 in Delphi 10.2.3). This creates the instances that you see in the report I posted.

What we are doing in Spring4D and also in the tests at work is calling all kinds of methods and routines that we know cause some lazy initialization of instances (TEncoding for example is another candidate) before running any test. This prevents lazy initializations during the test run which then manifest in the memory delta before and after the test. See InitializeLeakCheck in Spring.TestRunner.pas

While you could configure LeakCheck to ignore these leaks it will affect performance significantly because it would find leaks to begin with. If these instances are initialized before changes are there is nothing to ignore later.

Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102
  • Thanks for your response. BTW I got started with LeakCheck thanks to your discussion [here](https://plus.google.com/+StefanGlienke/posts/6vJ3riHys5G) So I am grateful to the power of two:) I've removed Forms and Application.Initialize and got similar results. – Wodzu Jul 18 '18 at 14:47