pleriche / FastMM4

A memory manager for Delphi and C++ Builder with powerful debugging facilities
443 stars 162 forks source link

[console] console application leak origin detail not logged #89

Open dcabale opened 2 years ago

dcabale commented 2 years ago
  1. consider this simple console application project:

program Project7;

{$APPTYPE CONSOLE} {$INCLUDE .\FastMM4Options.inc}

{$R *.res}

uses FastMM4 , System.SysUtils ;

begin ReportMemoryLeaksOnShutdown := true; try try { TODO -oUser -cConsole Main : Insert code here } TObject.Create; finally end; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.

  1. enabling FullDebugMode in FastMM4Options.inc, the resulting Project7_MemoryManager_EventLog.txt is as follows: --------------------------------2021/10/11 10:35:34-------------------------------- A memory block has been leaked. The size is: 12.

This block was allocated by thread 0x2C90, and the stack trace (return addresses) at the time was: 40707E [System.pas][System][@GetMem$qqri][4843] 4088AB [System.pas][System][TObject.NewInstance][17861] 408FD2 [System.pas][System][@ClassCreate$qqrpvzc][19251] 408990 [System.pas][System][TObject.Create][17920] 40A1C0 [System.pas][System][InitUnits$qqrv][23832] 4F9986 76A00419 [BaseThreadInitThunk] 77B372AD [RtlGetAppContainerNamedObjectPath] 77B3727D [RtlGetAppContainerNamedObjectPath]

The block is currently used for an object of class: System.TObject The allocation number is: 537 ...

  1. Note the absence of reference to the Project7.dpr source code, in the line referenced by the 4F9986 memory address

-> Did I miss a setting in FastMM4Options.inc ?

the-Arioch commented 2 years ago

I'd suppose you lack debug information in your .EXE file. It has to be enabled in Project's options twice - in compiler page the generation of it, and in linker page saving it into .EXE/.DLL file

You can press ctrl+o, o in the editor to dump current compiler options into the file, but i am not sure linker's options would be dumped too.

See Also:

A text in Russian from EurekaLog's (or madExcept's? i personally use JCL so am not sure which is which of those two commercial swissknives) developer: https://www.gunsmoker.ru/2009/04/delphi_19.html

Official docs: https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Linking

Also ntCore's CFF Explorer Suite might be handy to peep into the generated EXE/DLL file to see if there among standard PE sections exists and extra one with debug-info (this is compiler-specific, not PE-standard, so harder to identify)

dcabale commented 2 years ago
  1. Thanks for your reply Arioch
  2. I dumped the compiler options (as advised), and here it is -> project7_buildoptions.txt
  3. some screen-shots of my compile /linking options image image
  4. about the presence of the debug infos in the .exe: if these would not have been present, the Project7_MemoryManager_EventLog.txt would not have contained logs like: 408990 [System.pas][System][TObject.Create][17920]. Wouldn't they? .. Yet, the problem still remains

PS: anyway, here is the project -> Projects.zip. Do you experience the same?

the-Arioch commented 2 years ago

Would anything change if you do instead

uses
  FastMM4
  , System.SysUtils
  ;

procedure LeakProc;
begin TObject.Create; end;

begin
  ReportMemoryLeaksOnShutdown := true;
  try
     LeakProc;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Also, make Win32 Map file "detailed" and then read it in Notepad, what would be there for your 4F9986 ? I suspect it would be anonymous line number, as there is no any name associated with it, it is not some named function or something, is it?

dcabale commented 2 years ago
  1. No, "detailed" Map file does not resolve.
  2. Yes, within a function, the leak detail is reported, with the correct line number: 421797 [Project7.dpr][Project7][LeakProc$qqrv][14] -> Should not this be considered as a bug?
the-Arioch commented 2 years ago

A.S. Funny thing, you seem to only partially configure the project, or so it seems to me.

2021-10-12 22_58_03-Project7 - Delphi XE2 - Project7 dproj 2021-10-12 22_56_40-Project Options for Project7 exe  (Win32 - Debug)

I was not nitpicking on the specific chapter you opened in the config dialog (not specific enough), and it seems it did not affected this case, but you have to be aware that you was probably looking into the wrong options chapter.

No, "detailed" Map file does not resolve.

I did not say it would resolve anything, i said it would give you more information, i toold you to read the file in the Notepad, did you?

Here is the relevant map block as generated in Delphi XE2

Line numbers for Project7(Project7.dpr) segment .itext

    13 0002:00000390    14 0002:000003BE    15 0002:000003C6    16 0002:000003D4
    19 0002:000003E2    21 0002:000003FB    23 0002:0000041E    24 0002:00000423
    26 0002:00000466

The address in question is 4213EE and the sections list in the maps' header is

 Start         Length     Name                   Class
 0001:00401000 0001F0ACH .text                   CODE
 0002:00421000 000004A0H .itext                  ICODE
 0003:00422000 00001ED4H .data                   DATA

So, your suspicious address should be 0x42100 + 0x3EE or in seg:ofs format, 0002:000003EE

Can you find this address ANYWHERE in the map file, either among identifiers (funcitons, variables, etc) or among line numbers?

Seems, Delphi just does not generate any information for that address, so what FastMM4 can do, without bloating much itself? And, for such a niche case, that almost no one uses, as project file in Delphi is only a "hub" bringing together many procedures to be called and to actually do the program logic.

I also tried to add an anonymous procedure instead:

    (procedure begin
       TObject.Create;
    end)();

Sadly, in Delphi XE2 this leads to AV during execution.

    TProc(procedure begin
       TObject.Create;
    end)();

This works though, but again - line number's address and memory allocation's address do not match.

the-Arioch commented 2 years ago

And here is a simplified program's Debug View -> CPU

begin
  ReportMemoryLeaksOnShutdown := true;
  try
     TObject.Create;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
Project7.dpr.16: ReportMemoryLeaksOnShutdown := true;
004213BE A1C43D4200       mov eax,[$00423dc4]
004213C3 C60001           mov byte ptr [eax],$01
Project7.dpr.17: try
004213C6 33C0             xor eax,eax
004213C8 55               push ebp
004213C9 68EA134200       push $004213ea
004213CE 64FF30           push dword ptr fs:[eax]
004213D1 648920           mov fs:[eax],esp
Project7.dpr.18: TObject.Create;
004213D4 B201             mov dl,$01
004213D6 A148134000       mov eax,[$00401348]
004213DB E8E43AFEFF       call TObject.Create
004213E0 33C0             xor eax,eax
004213E2 5A               pop edx
004213E3 59               pop ecx
004213E4 59               pop ecx
004213E5 648910           mov fs:[eax],edx
004213E8 EB59             jmp $00421443
004213EA E9CD43FEFF       jmp @HandleOnException
004213EF 0100             add [eax],eax
004213F1 0000             add [eax],al
004213F3 387B41           cmp [ebx+$41],bh
004213F6 00FB             add bl,bh
004213F8 134200           adc eax,[edx+$00]
Project7.dpr.20: on E: Exception do
004213FB A31CC84200       mov [$0042c81c],eax
Project7.dpr.21: Writeln(E.ClassName, ': ', E.Message);
00421400 8D55EC           lea edx,[ebp-$14]
00421403 A11CC84200       mov eax,[$0042c81c]
00421408 8B00             mov eax,[eax]
0042140A E84D39FEFF       call TObject.ClassName
40548A [System.pas][System][@ClassCreate$qqrpvzc][14164]
404ED0 [System.pas][System][TObject.$bctr$qqrv][13015]
4213E0 
7648343D [BaseThreadInitThunk]
76EC9812 [Unknown function at RtlInitializeExceptionChain]

As you can see 4213E0 is the return address of the call TObject.Create - IOW the NEXT command that would be executed when the flow comes returned out of the function.

I suppose, because there is no identifer in map/debug info - FastMM4 just has no anchor to scan back addresses to find a last known address and to report something like (Line 18 + $0C) as more advanced exception trackers would do.

Would this be implemented (and complicate FastMM's code) - will this be safe? will it never backfire by mis-attributting addresses in other places? I am not sure. And that is for quite a niche case going against how practical Delphi programs are actually designed.

It is up to you to scratch your itch, if you feel it is worth it. Or it is up to Pierre to scratch your itch, if he feels so. But personally i would not bother. "Above all else, do no harm".


Here is another fundamentally similar case, with inline code-generation.

procedure Leaker;
begin     //   Line 16
    TProc(procedure begin
       TObject.Create;     // Line 18
    end)();
end;

begin
  ReportMemoryLeaksOnShutdown := true;
  try
     // TObject.Create;
     Leaker;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
 Start         Length     Name                   Class
 0001:00401000 0001F534H .text                   CODE
 0002:00421000 00000478H .itext                  ICODE
 0003:00422000 00001ED4H .data                   DATA

Line numbers for Project7(Project7.dpr) segment .text

    17 0001:0001DBC0    18 0001:0001DBC7    19 0001:0001DBD3    16 0001:0001DBD8
    17 0001:0001DC12    20 0001:0001DC21

Line numbers for Project7(Project7.dpr) segment .itext

    22 0002:00000390    23 0002:000003BE    24 0002:000003C6    26 0002:000003D4
    28 0002:000003F4    29 0002:000003F9    31 0002:0000043C

Good luck finding 0x4213D9 address above. Line 18 is reported to even belong to a different segment, probably at 0x00401000 + 0x0001DBC7 address.

This block was allocated by thread 0x1A28, and the stack trace (return addresses) at the time was:
40405A [System.pas][System][@GetMem$qqri][3454]
4050D3 [System.pas][System][TObject.NewInstance$qqrv][13000]
4056C2 [System.pas][System][@ClassCreate$qqrpvzc][14164]
405108 [System.pas][System][TObject.$bctr$qqrv][13015]
408B1A [System.pas][System][TInterfacedObject._AddRef$qqsv][30143]
408A98 [System.pas][System][@IntfCopy$qqrr45System.%DelphiInterface$t17System.IInterface%x45System.%DelphiInterface$t17System.IInterface%][30012]
41EBD3 [Project7.dpr][Project7][Leaker$141$ActRec.$0$Body$qqrv][18]
41EC21 [Project7.dpr][Project7][Leaker$qqrv][17]
4213D9 
7648343D [BaseThreadInitThunk]
76EC9812 [Unknown function at RtlInitializeExceptionChain]
Project7.dpr.16: begin
0041EBD8 55               push ebp
0041EBD9 8BEC             mov ebp,esp
0041EBDB 83C4F8           add esp,-$08
0041EBDE 33C0             xor eax,eax
0041EBE0 8945F8           mov [ebp-$08],eax
0041EBE3 33C0             xor eax,eax
0041EBE5 55               push ebp
0041EBE6 6837EC4100       push $0041ec37
0041EBEB 64FF30           push dword ptr fs:[eax]
0041EBEE 648920           mov fs:[eax],esp
0041EBF1 B201             mov dl,$01
0041EBF3 A11CEB4100       mov eax,[$0041eb1c]
0041EBF8 E8FF64FEFF       call TObject.Create
0041EBFD 8945FC           mov [ebp-$04],eax
0041EC00 8D45F8           lea eax,[ebp-$08]
0041EC03 8B55FC           mov edx,[ebp-$04]
0041EC06 85D2             test edx,edx
0041EC08 7403             jz $0041ec0d
0041EC0A 83EAF8           sub edx,-$08
0041EC0D E87A9EFEFF       call @IntfCopy
Project7.dpr.17: TProc(procedure begin
0041EC12 8B45FC           mov eax,[ebp-$04]
0041EC15 85C0             test eax,eax
0041EC17 7403             jz $0041ec1c
0041EC19 83E8F4           sub eax,-$0c
0041EC1C 8B10             mov edx,[eax]
0041EC1E FF520C           call dword ptr [edx+$0c]
Project7.dpr.20: end;
0041EC21 33C0             xor eax,eax
0041EC23 5A               pop edx
0041EC24 59               pop ecx
0041EC25 59               pop ecx
0041EC26 648910           mov fs:[eax],edx
0041EC29 683EEC4100       push $0041ec3e
0041EC2E 8D45F8           lea eax,[ebp-$08]
0041EC31 E83E9EFEFF       call @IntfClear
0041EC36 C3               ret 
0041EC37 E9406FFEFF       jmp @HandleFinally
0041EC3C EBF0             jmp $0041ec2e
0041EC3E 59               pop ecx
0041EC3F 59               pop ecx
0041EC40 5D               pop ebp
0041EC41 C3               ret 
dcabale commented 2 years ago

@the-Arioch , many thanks for this detailed analysis!