Как обрабатывать ложную/ожидаемую память в DUnit с помощью LeakCheck?

Чтобы устранить все утечки памяти, я пытаюсь включить библиотеку LeakCheck в DUnit. Проблема в том, что LeakCheck будет сообщать мне об утечках памяти, которые не являются настоящими утечками. Это объекты или другие вещи, выделенные RTL и уничтоженные при выходе из программы.

LeakCheck содержит множество подпрограмм, которые позволяют указать, что следует игнорировать, и я смог использовать их для игнорирования большинства таких «утечек». Однако я не знаю, как избавиться от одного, в частности:

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.

Создание экземпляра TThread (изначально это был TThread.CreateAnonymousThread, но результаты те же) вызывает 64-байтовую утечку памяти:

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

В других случаях LeakCheck предоставил мне имя класса, из которого произошел утечка экземпляра, чтобы я мог добавить его в список игнорируемых, но в этом случае это не так. Как подавить эту "утечку"?

Одно примечание: эта и другие утечки, с которыми я столкнулся, не происходят в приложении с графическим интерфейсом. Я предполагаю, что RTL предварительно выделяет память для большинства этих объектов перед запуском тестов.


person Wodzu    schedule 18.07.2018    source источник
comment
Вы пробовали встроенную проверку утечек fastmm?   -  person whosrdaddy    schedule 18.07.2018
comment
Проблема в том, что LeakCheck сообщает мне об утечках памяти, которые не являются настоящими утечками. Это объекты или другое содержимое, выделенное RTL и уничтоженное при выходе из программы.   -  person Wodzu    schedule 18.07.2018
comment
afaik FastMM довольно бесполезен для модульных тестов, потому что он не дает вам подробной информации между двумя моментами времени, которая вам нужна здесь для обнаружения утечек, происходящих во время выполнения теста.   -  person Stefan Glienke    schedule 18.07.2018
comment
LeakCheck поддерживает функцию RTL System.RegisterExpectedMemoryLeak(). Передайте адреса любых блоков памяти, которые вы хотите игнорировать в отчетах об утечках.   -  person Remy Lebeau    schedule 19.07.2018
comment
@RemyLebeau, когда я использую эту функцию, есть вероятность, что адрес изменится, когда я изменю что-то в тестируемом классе или в самом тесте. Верно?   -  person Wodzu    schedule 19.07.2018
comment
@Wodzu, как следует из названия, используйте эту функцию только для ожидаемых утечек. Если ваш тест вызывает изменение адреса памяти, этот адрес не является хорошим кандидатом для этой функции, если только это изменение не является преднамеренным и намеренно создает утечку. Эта функция предназначена для вызова во время выполнения после выделения адреса памяти, предназначенного для последующей утечки.   -  person Remy Lebeau    schedule 19.07.2018


Ответы (1)


FWIW результаты, которые я получаю после удаления Forms и Application.Initialize, таковы:

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?

Из использования LeakCheck в модульных тестах Spring4D я знаю, что это происходит из-за ленивой инициализации некоторых экземпляров в TThread.GetCurrentThread, которые вызываются во время вашей процедуры LeakMemory, точнее во время TThread.Destroy, который вызывает WaitFor (см. System.Classes.pas строку 15764 в Delphi 10.2.3). Это создает экземпляры, которые вы видите в отчете, который я опубликовал.

То, что мы делаем в Spring4D, а также в тестах на работе, вызывает все виды методов и подпрограмм, которые, как мы знаем, вызывают некоторую ленивую инициализацию экземпляров (например, TEncoding — еще один кандидат) перед запуском любого теста. Это предотвращает отложенные инициализации во время выполнения теста, которые затем проявляются в разнице памяти до и после теста. См. InitializeLeakCheck в Spring.TestRunner.pas

Хотя вы можете настроить LeakCheck на игнорирование этих утечек, это значительно повлияет на производительность, потому что он найдет утечки с самого начала. Если эти экземпляры инициализированы до того, как будут внесены изменения, то позже игнорировать их будет нечего.

person Stefan Glienke    schedule 18.07.2018
comment
Спасибо за ваш ответ. Кстати, я начал работать с LeakCheck благодаря вашему обсуждению здесь. степень двойки :) Я удалил Forms и Application.Initialize и получил аналогичные результаты. - person Wodzu; 18.07.2018