Open dcabale opened 3 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)
PS: anyway, here is the project -> Projects.zip. Do you experience the same?
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?
A.S. Funny thing, you seem to only partially configure the project, or so it seems to me.
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.
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
@the-Arioch , many thanks for this detailed analysis!
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.
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 ...
-> Did I miss a setting in FastMM4Options.inc ?