Page 1 of 1

UPX completely messes up leak report

Posted: Tue Jan 19, 2021 8:30 pm
by aehimself
I started to experiment with MadExcept in my home projects - mainly to be able to fill the stack trace of exception objects. As I accomplished that, I looked into the leak reporting - FastMM shipped with Delphi is really crippled, it's nice to have a stack trace.

All is working fine. Debug or Release config, my test leak shows up just fine... until I UPX the executable. I left a leak in a frame constructor to check and after UPX, objects do not even show up anymore (only GetMem and UnicodeString) and they point to a unit initialization stack rather than a button click -> frame constructor.

For home, I prohibited MadExcept to adjust project clauses, complier and linker options or conditional defines. I have a small unit which acts as a wrapper for MadExcept, it is added as the first unit in the project:

Unit uMyMadExcept;

Interface

Code: Select all

Uses MadExcept;

Type
 TLeakDetectedMethod = Reference To Procedure(Const inLeakText: String);

Var
 OnLeakDetected: TLeakDetectedMethod;

Procedure EnableLeakChecking;

Implementation

Uses System.SysUtils;

Procedure EnableLeakChecking;
Begin
 WaitForCleanExitProcess(True);
 ShowNoLeaksWindow(False);
 ShowLeakProgressWindow(False);
 SetChildLeakFiltering(True);
 StartLeakChecking;
End;

Function GetExceptionStackInfoProc(P: PExceptionRecord): Pointer;
Var
 stacktrace: String;
Begin
 stacktrace := GetCrashStackTrace(True);
 Result := StrAlloc(Length(stacktrace));
 StrCopy(Result, PChar(stacktrace));
End;

Function GetStackInfoStringProc(Info: Pointer): String;
Begin
 Result := PChar(Info);
End;

Procedure CleanUpStackInfoProc(Info: Pointer);
Begin
 StrDispose(PChar(Info));
End;

Initialization
 OnLeakDetected := nil;
 Exception.GetExceptionStackInfoProc := GetExceptionStackInfoProc;
 Exception.GetStackInfoStringProc := GetStackInfoStringProc;
 Exception.CleanUpStackInfoProc := CleanUpStackInfoProc;
 {$IFDEF DEBUG}
 EnableLeakChecking;
 {$ENDIF}

Finalization
 Exception.GetExceptionStackInfoProc := nil;
 Exception.GetStackInfoStringProc := nil;
 Exception.CleanUpStackInfoProc := nil;
 If Assigned(OnLeakDetected) Then
 Begin
   OnLeakDetected(MadExcept.GetLeakReport);
   StopLeakChecking;
 End;
End.
This is my first experience with MadExcept, especially by not using the package directly. Can someone assist me in pointing out where my logic is flawed?

Thanks!

Re: UPX completely messes up leak report

Posted: Wed Jan 20, 2021 8:01 am
by madshi
Do you have madExcept enabled in your madExcept project settings and the 3 linker options checked here?

http://help.madshi.net/madExceptSettings1.htm

Re: UPX completely messes up leak report

Posted: Wed Jan 20, 2021 9:03 am
by aehimself
I guess yes... cannot confirm - see my other post. Without those I'd have no readable stack trace / object list in non-compressed executables, I suppose...?

Let me re-phrase the question, it seems I did not express myself well.
I have an application with leak checking enabled. Test leak shows up fine in compiled .EXE.
Now, I compress this .EXE with UPX. A leak still shows up, but object names are missing (they show up as GetMem / UnicodeString instead) and sometimes the stack trace of the leak is pointing to the wrong place.
The only difference is UPX.

I guess it's not UPXs fault, it'll be me / my project. I just don't know what :)

Re: UPX completely messes up leak report

Posted: Wed Jan 20, 2021 9:56 am
by madshi
madExcept is disabled by default. If you haven't enabled it manually it's not enabled. So the answer to my question probably is "no". Activating madExcept properly for your project could potentially help with UPX. I'm not 100% sure, though.

Re: UPX completely messes up leak report

Posted: Sun Jan 24, 2021 11:34 pm
by aehimself
When I checked my MadExcept project settings, it showed me that MadExcept is enabled.
I completely removed my custom unit and let MadExcept to modify whatever it wants. A new version was built and packed with UPX - I'll return with my findings if it helped.

Re: UPX completely messes up leak report

Posted: Mon Jan 25, 2021 8:30 pm
by aehimself
Yes, there is an issue, maybe with objects in threads...?

Code: Select all

Type
 TMyThread = Class(TThread)
 protected
  Procedure Execute; Override;
 End;

procedure TMyThread.Execute;
begin
  inherited;
  TStringList.Create;
end;

procedure TForm1.FormCreate(Sender: TObject);
Var
 tmt: TMyThread;
begin
 tmt := TmyThread.Create(True);
 tmt.FreeOnTerminate := True;
 tmt.Start;
end;
Without UPX:
leak.PNG
leak.PNG (23.44 KiB) Viewed 23716 times
With UPX:
leak_upx.PNG
leak_upx.PNG (12.35 KiB) Viewed 23716 times
UPX v3.96, command line used: UPX --best --ultra-brute Project1.exe

Re: UPX completely messes up leak report

Posted: Mon Jan 25, 2021 8:38 pm
by madshi
Not sure why it doesn't work. Does the callstack work for exceptions, though?

FWIW, leak reporting is considered to be a feature that is used on the development PC, not on the end user PC. So it should be no problem doing leak checking with UPX disabled.

Re: UPX completely messes up leak report

Posted: Tue Jan 26, 2021 1:46 pm
by aehimself
I was attempting to check the call stack for exceptions in threads, but I realized that the TThread.FatalException object is now being swallowed.
I made a new property and allowed read-write access to it:

Procedure TMyForm.OnMadException(Const exceptIntf: IMEException; Var Handled: Boolean);
Begin
Case exceptIntf.Source Of
esTThreadExecute: TWorkerThread(exceptIntf.RelatedObject).FatalException := Exception(exceptIntf.ExceptObject);
[...]
End;
End;

From the setter I tried simply assigning the exception object or raising an outer exception but the original exception object is freed up in all occasions.

Is there a way to keep the TThread's FatalException object intact while using MadExcept...?

Re: UPX completely messes up leak report

Posted: Tue Jan 26, 2021 4:29 pm
by madshi
Not really, madExcept hooks deep into the RTL and replaces much of the RTL's internal exception handling. Some things unfortunately get broken this way. TThread.FatalException is one of those things.

Re: UPX completely messes up leak report

Posted: Tue Jan 26, 2021 7:58 pm
by aehimself
That's a shame. Anyway, since I'm using a descendant of the TThread class this was easy to achieve:

Code: Select all

 TWorkerThread = Class(TThread)
 strict private
  _fatalexception: Exception;
 strict protected
  Procedure InternalExecute; Virtual; Abstract;
 protected
  Procedure Execute; Override;
 public
  Property FatalException: Exception Read _fatalexception;
 End;

Procedure TWorkerThread.Execute;
Begin
 Try
  Self.InternalExecute;
 Except
  _fatalexception := Exception(AcquireExceptionObject);
 End;
End;
With this achieved I can confirm:
- Before UPX, all stack traces (exception & leak) are correct
- After UPX, exception stack traces are intact, leak stack traces are messed up in threads. Leaks in the VCL thread seem to be correct.

I trashed my custom unit, now I'm using the MadExcept project settings to turn on leak checking in the application.

Re: UPX completely messes up leak report

Posted: Tue Jan 26, 2021 9:04 pm
by madshi
Well, at least exception stack traces are working ok. I guess you'll have to disable UPX for leak checking, sadly...