Donate. I desperately need donations to survive due to my health

Get paid by answering surveys Click here

Click here to donate

Remote/Work from Home jobs

Non-standard use of asserts and exceptions in Delphi

My program has memory leaks (sometimes it produces out of memory error). I try to locate these leaks via ReportMemoryLeaksOnShutdown := True, but this feature is not convenient enough: it does not show where the bugs in my code are concretely situated. So I decided to write my own “Garbage collector” as follows:

unit custommanagers_u;

interface
uses system.classes, System.Types, system.sysutils, vcl.forms, math,
 simplefuncs;

type
TSafeObject=class
public
constructor Create;
destructor Destroy; override;
end;


TGarbageCollector=class
private
FObjects:tlist;
FObjectsSaved:tlist;
FSaveRepFileName:tfilename;
FReport:tstringlist;
FSectionStarted:boolean;
FSectionsCount:integer;
FTwiceDestroyedObjectsCount:integer;
FTotalObjectsNotDestroyedCount:integer;
FTotalTwiceDestroyedObjectsCount:integer;
public
procedure StartSection;
procedure FinishSection;
procedure ObjectCreated(obj:tsafeobject);
procedure ObjectDestroyed(obj:tsafeobject);
constructor Create(repfilename:tfilename);
procedure FinalizeReport;
procedure WriteReportToFile;
destructor Destroy; override;
end;

var
ProjectGarbageCollector:TGarbageCollector;

implementation

{ TSafeObject }

constructor TSafeObject.Create;
begin
  inherited;
  if fulldebugmode then ProjectGarbageCollector.ObjectCreated(self);
end;

destructor TSafeObject.Destroy;
begin
  if fulldebugmode then ProjectGarbageCollector.ObjectDestroyed(self);
  inherited;
end;

If I make all classes in my project the descentants of my TSafeObject, the object ProjectGarbageCollector writes a log about the classes which have not been destroyed, and it provides some more information besides the one provided by the ReportMemoryLeaksOnShutdown option. However I still need some more information. I am thinking of some kind of non-standard use of asserts or exceptions in Delphi, like this:

procedure TmpAssertProc(const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);
var
  str:string;
  strprogname:string;
begin
  str:=filename+' line '+inttostr(linenumber);
  str:=str+message;
  ProjectGarbageCollector.LastConstructorWasFromSource(str);
end;


{ TSafeObject }

constructor TSafeObject.Create;
var
  oldassertproc:TAssertErrorProc;
begin
  inherited;

  if FullDebugMode then
  begin
    ProjectGarbageCollector.ObjectCreated(self);

    oldassertproc:=AssertErrorProc;
    AssertErrorProc := TmpAssertProc;
    assert(false);
    AssertErrorProc:=oldassertproc;

  end;
end;

Concretely this code is not useful, because it will show the position of the tsafeobject.create constructor, not of the code which called this constructor. Can I find a way to analyze the stack or the ErrorAddr variable to find this information (e.g. what was the name of the procedure or function from which the tsafeobject.create constructor was called)? Or maybe I can do something similar with standard exceptions? If this is possible, finding bugs with my ProjectGarbageCollector object will be easy.

Comments