Help me fix a small unit?
From: Raptor (bogus_at_none.com)
Date: 02/26/05
- Next message: Rob Kennedy: "Re: Help me fix a small unit?"
- Previous message: Raptor: "Re: Comments in Ini files?"
- Next in thread: Rob Kennedy: "Re: Help me fix a small unit?"
- Reply: Rob Kennedy: "Re: Help me fix a small unit?"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Fri, 25 Feb 2005 17:37:32 -0800
I've tried a few memory leak detectors, and two of them show some promise.
The most compact (which, as a fan of Small Code, naturally drew my
attention) was written for "virtual Pascal and Delphi." With a few TEMPORARY
KLUGE changes it compiles on D4, but raises a memory write error when it
hits some assembly code.
It would be great if we could get this working. I'll list the unit here,
with most comments abbreviated, and note the spots about which I have
questions. My notes are in CAPS WITH ARROWS --->
// Memory leak detector for Virtual Pascal and Delphi
// (c) 1998 Joerg Pleumann
unit MemLeaks;
{$H-}
{$X+} // ORIG --> {$X+, Delphi+}
interface
uses
SysUtils;
implementation
{$IFNDEF VIRTUALPASCAL}
uses
Windows;
{$ENDIF}
{$IFDEF VIRTUALPASCAL}
uses
VpSysLow;
{$ENDIF VIRTUALPASCAL}
function NewGetMem(Size: LongInt): Pointer; forward;
function NewFreeMem(P: Pointer): LongInt; forward;
function NewReallocMem(P: Pointer; Size: LongInt): Pointer; forward;
const
MemBlockDelta = 1024;
NewMemoryManager: TMemoryManager =
( GetMem: NewGetMem;
FreeMem: NewFreeMem;
ReAllocMem: NewReAllocMem );
type
PMemBlock = ^TMemBlock;
TMemBlock = record
FAddress: Pointer;
FSize: LongInt;
FCaller: Pointer; (* ATC -- Stores the caller's address *)
FThread: LongInt; (* ATC -- Stores the caller's thread *)
end;
PMemBlockList = ^TMemBlockList;
TMemBlockList = array[0..MaxInt div SizeOf(TMemBlock) - 1] of TMemBlock;
var
MemBlockList: PMemBlockList = nil; // Currently allocated blocks
MemBlockCount: LongInt = 0;
MemBlockLimit: LongInt = 0;
MemBlockTotal: LongInt = 0; // Total memory allocated minus heap heap.
OldMemoryManager: TMemoryManager;
// Finds address of the caller by walking stack.
function CallerAddr2org:Pointer;
// {$FRAME-} <-- DELPHI COMPILE ERROR
assembler;
asm
mov eax, [ebp]
mov eax, [eax+4]
sub eax, 5
end;
function CallerAddr2:Pointer;
asm
mov eax,[ebp+4] <-- MEMORY WRITE ERROR HERE ON UNIT LOAD
sub eax,5
mov edx,offset _MemNew
cmp eax,edx
jb @not_MemNew
add edx,$20
cmp eax,edx
jnb @not_MemNew
// Who has called "New" or "GetMem" ?
mov eax,[ebp+$14]
sub eax,5
jmp @ret
@not_MemNew:
// inside "_MemRealloc" (Realloc) ?
mov edx,offset _MemRealloc
cmp eax,edx
jb @not_MemRealloc
add edx,$20
cmp eax,edx
jnb @not_MemRealloc_0
add edx,$56-$20
cmp eax,edx
jnb @not_MemRealloc
// Who has called "ReallocMem" ?
mov eax,[ebp+$1c]
sub eax,5
jmp @ret
@not_MemRealloc_0:
mov eax,[ebp+$18]
sub eax,5
jmp @ret
@not_MemRealloc:
// more detection ..
@ret:
end;
// Adds a block to list of currrently allocated memory blocks.
procedure AddBlock(Address: Pointer; Size: LongInt; Caller: Pointer);
begin
if Address <> nil then
begin
if MemBlockCount = MemBlockLimit then
begin
MemBlockList := OldMemoryManager.ReAllocMem(MemBlockList,
(MemBlockLimit + MemBlockDelta) * SizeOf(TMemBlock));
Inc(MemBlockLimit, MemBlockDelta);
end;
with MemBlockList^[MemBlockCount] do
begin
FAddress := Address;
FSize := Size;
FCaller := Caller;
// --> WAS FThread := GetThreadID;
FThread := GetCurrentThreadID;
// IS THIS THE RIGHT SUBSTITUTE?
end;
Inc(MemBlockCount);
Inc(MemBlockTotal, Size);
end;
end;
// Deletes block from list of currently allocated memory.
procedure DeleteBlock(Address: Pointer);
var
I: LongInt;
begin
if Address <> nil then
begin
I := MemBlockCount - 1;
while (I <> -1) and (MemBlockList^[I].FAddress <> Address) do
Dec(I);
if I <> - 1 then
begin
Dec(MemBlockCount);
Dec(MemBlockTotal, MemBlockList^[I].FSize);
Move(MemBlockList^[I + 1], MemBlockList^[I],
(MemBlockCount - I) * SizeOf(TMemBlock));
end;
end;
end;
(**
* Tries to classify the memory block. This is more or
* less a guess, but works most of the time. :-) The
* function is able to detect AnsiStrings and object
* instances (new object model only). Everything else
* is returned as 'Unknown'.
*)
function MemClassify(Address: Pointer; Size: LongInt): string;
type
TStrRec = record
RefCnt: LongInt;
Length: LongInt;
Data: array[0..2000000000] of Char; <-- KLUGE PATCH
// ORIG --> Data: array[0..MaxLongInt] of Char;
// DELPHI 4 GIVES "EXCEEDS 2 GB1 ERROR. I THOUGHT
// MaxLongInt *WAS* 2 GB.
end;
var
AString: ^TStrRec absolute Address;
AObject: TObject absolute Address;
begin
if Size > 8 then
with AString^ do
begin
if (Length + 9 = Size) and (Data[Length] = #0) then
begin
Result := 'AnsiString (Length=' + IntToStr(Length)
+ ', RefCnt=' + IntToStr(RefCnt) + ')';
Exit;
end;
end;
try
if AObject.InstanceSize = Size then
begin
Result := TObject(Address).ClassName;
Exit;
end;
except
end;
Result := 'Unknown';
end;
// Dumps a given block of memory to a string.
function MemToHexAsc(Address: PChar; Length: Integer): string;
var
I: Integer;
begin
Result := '';
for I := 0 to Length - 1 do
Result := Result + IntToHex(Byte(Address[I]), 2) + ' ';
for I := Length to 15 do
Result := Result + ' ';
Result := Result + ' ';
for I := 0 to Length - 1 do
begin
if Address[I] >= ' ' then
Result := Result + Address[I]
else
Result := Result + '.';
end;
end;
function CallerInfo(Caller: Pointer; Thread:LongInt): string;
var
LineNum : Integer;
begin
// I THINK THIS LINE MUST BE VIRTUAL PASCAL SPECIFIC AND DON'T
// KNOW THE DELPHI EQUUIVALENT.
// ---> if GetLocationInfo(Caller,Result,LineNum) <> nil then
Result := Format('%s line %d (thread %d)',[Result,LineNum,Thread])
// TEMP else
// TEMP Result := Format('unknown: %p (thread %d)',[Caller,Thread])
end;
procedure DumpHeap;
var
F: Text;
I, L: LongInt;
P: PChar;
begin
Assign(F, 'MemLeaks.dmp');
Rewrite(F);
WriteLn(F, '; Dump of memory leak detector.');
WriteLn(F, ';');
WriteLn(F, '; Executable file : ', ParamStr(0));
WriteLn(F, '; Date and time : ', FormatDateTime('yyyy"/"mm"/"dd
hh":"nn":"ss', Now));
WriteLn(F, '; Number of leaks : ', MemBlockCount);
WriteLn(F, '; Total memory loss: ', MemBlockTotal, ' bytes (without
overhead)');
WriteLn(F, ';');
WriteLn(F, '; Leaks are listed in order of allocation.');
WriteLn(F);
for I := 0 to MemBlockCount - 1 do
begin
with MemBlockList^[I] do
begin
P := FAddress;
L := FSize;
WriteLn(F, 'Address : ', IntToHex(LongInt(P), 8));
WriteLn(F, 'Size : ', IntToHex(L, 8), ' (', L, ' bytes)');
WriteLn(F, 'Caller : ', CallerInfo(FCaller, FThread));
WriteLn(F, 'Type : ', MemClassify(FAddress, FSize));
Write (F, 'Contents: ');
while L > 16 do
begin
WriteLn(F, MemToHexAsc(P, 16));
Write (F, ' ');
Inc(P, 16);
Dec(L, 16);
end;
WriteLn(F, MemToHexAsc(P, L));
WriteLn(F);
end;
end;
WriteLn(F, '; End of file.');
Close(F);
end;
function NewGetMem(Size: LongInt): Pointer;
begin
Result := OldMemoryManager.GetMem(Size);
AddBlock(Result, Size, CallerAddr2);
end;
function NewFreeMem(P: Pointer): Longint;
begin
DeleteBlock(P);
Result := OldMemoryManager.FreeMem(P);
end;
function NewReallocMem(P: Pointer; Size: LongInt): Pointer;
begin
DeleteBlock(P);
Result := OldMemoryManager.ReAllocMem(P, Size);
AddBlock(Result, Size, CallerAddr2);
end;
initialization
GetMem(MemBlockList, MemBlockDelta * SizeOf(TMemBlock));
GetMemoryManager(OldMemoryManager);
SetMemoryManager(NewMemoryManager);
finalization
SetMemoryManager(OldMemoryManager);
if (ExitCode = 0) and (MemBlockCount <> 0) then
begin
{$IFNDEF VIRTUALPASCAL} (* DELPHI ? *)
MessageBeep($FFFFFFFF);
MessageBeep($FFFFFFFF);
MessageBeep($FFFFFFFF);
{$ENDIF}
{$IFDEF VIRTUALPASCAL} (* VP 2.0 OS/2,Win32,DPMI32 *)
SysBeepEx(440, 100);
SysCtrlSleep(500);
SysBeepEx(440, 100);
SysCtrlSleep(500);
SysBeepEx(440, 100);
{$ENDIF VIRTUALPASCAL}
DumpHeap;
end;
FreeMem(MemBlockList);
end.
- Next message: Rob Kennedy: "Re: Help me fix a small unit?"
- Previous message: Raptor: "Re: Comments in Ini files?"
- Next in thread: Rob Kennedy: "Re: Help me fix a small unit?"
- Reply: Rob Kennedy: "Re: Help me fix a small unit?"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|