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
Post a Comment