Componentes.Terceros.jcl/official/2.1.1/experts/stacktraceviewer/APIExamples/FastMM/FastMMParser.pas

897 lines
26 KiB
ObjectPascal
Raw Normal View History

unit FastMMParser;
{$I jcl.inc}
interface
uses
SysUtils, Classes, Contnrs,
{$IFNDEF NOVIEW}
JclStackTraceViewerClasses,
{$ENDIF ~NOVIEW}
JclDebug;
type
{$IFDEF NOVIEW}
TFastMMLocationInfoList = class(TJclCustomLocationInfoList)
private
function GetItems(AIndex: Integer): TJclLocationInfoEx;
public
constructor Create; override;
function Add(Addr: Pointer): TJclLocationInfoEx;
property Items[AIndex: Integer]: TJclLocationInfoEx read GetItems; default;
end;
{$ELSE ~NOVIEW}
TFastMMLocationInfoList = TJclStackTraceViewerLocationInfoList;
{$ENDIF ~NOVIEW}
TFastMMMemoryArray = array [0..255] of Byte;
TFastMMReport = class;
TFastMMLeak = class(TObject)
private
FAddress: Integer;
FAllocationNumber: Integer;
FBlockClass: string;
FDateStr: string;
FMemory: TFastMMMemoryArray;
FFoundMemory: Boolean;
FLeakSize: Integer;
FParent: TFastMMReport;
FThreadID: Integer;
FStack: TFastMMLocationInfoList;
public
constructor Create(AParent: TFastMMReport);
destructor Destroy; override;
property Address: Integer read FAddress write FAddress;
property AllocationNumber: Integer read FAllocationNumber write FAllocationNumber;
property BlockClass: string read FBlockClass write FBlockClass;
property DateStr: string read FDateStr write FDateStr;
property Memory: TFastMMMemoryArray read FMemory write FMemory;
property FoundMemory: Boolean read FFoundMemory write FFoundMemory;
property LeakSize: Integer read FLeakSize write FLeakSize;
property Parent: TFastMMReport read FParent;
property Stack: TFastMMLocationInfoList read FStack;
property ThreadID: Integer read FThreadID write FThreadID;
end;
TFastMMLeakGroup = class(TObject)
private
FItems: TList;
FLeakSize: Integer;
FLeakSizeUpdate: Boolean;
function GetCount: Integer;
function GetItems(AIndex: Integer): TFastMMLeak;
function GetLeakSize: Integer;
public
constructor Create;
destructor Destroy; override;
procedure Add(ALeak: TFastMMLeak);
property Count: Integer read GetCount;
property Items[AIndex: Integer]: TFastMMLeak read GetItems; default;
property LeakSize: Integer read GetLeakSize;
end;
TFastMMVMOnFreedObject = class(TObject)
private
FAddress: Integer;
FAllocationNumber: Integer;
FObjectClass: string;
FMemory: TFastMMMemoryArray;
FFoundMemory: Boolean;
FStack1: TFastMMLocationInfoList;
FStack1Thread: Integer;
FStack2: TFastMMLocationInfoList;
FStack2Thread: Integer;
FStack3: TFastMMLocationInfoList;
FStack3Thread: Integer;
FVirtualMethod: string;
FVirtualMethodAddress: Integer;
public
constructor Create;
destructor Destroy; override;
property Address: Integer read FAddress write FAddress;
property AllocationNumber: Integer read FAllocationNumber write FAllocationNumber;
property ObjectClass: string read FObjectClass write FObjectClass;
property Memory: TFastMMMemoryArray read FMemory write FMemory;
property FoundMemory: Boolean read FFoundMemory write FFoundMemory;
property Stack1Thread: Integer read FStack1Thread write FStack1Thread;
property Stack1: TFastMMLocationInfoList read FStack1;
property Stack2Thread: Integer read FStack2Thread write FStack2Thread;
property Stack2: TFastMMLocationInfoList read FStack2;
property Stack3Thread: Integer read FStack3Thread write FStack3Thread;
property Stack3: TFastMMLocationInfoList read FStack3;
property VirtualMethod: string read FVirtualMethod write FVirtualMethod;
property VirtualMethodAddress: Integer read FVirtualMethodAddress write FVirtualMethodAddress;
end;
TFastMMReport = class(TObject)
private
FLeakGroups: TObjectList;
FLeaks: TObjectList;
FLeakSummary: TStringList;
FReportCompilerVersion: Double;
FVMOnFreedObjects: TObjectList;
function GetLeakCount: Integer;
function GetLeaks(AIndex: Integer): TFastMMLeak;
function GetLeakGroupCount: Integer;
function GetLeakGroupItems(AIndex: Integer): TFastMMLeakGroup;
function SameStack(AStack1, AStack2: TFastMMLocationInfoList): Boolean;
function GetVMOnFreedObjectCount: Integer;
function GetVMOnFreedObjectItems(AIndex: Integer): TFastMMVMOnFreedObject;
public
constructor Create;
destructor Destroy; override;
function AddLeak: TFastMMLeak;
function AddLeakGroup: TFastMMLeakGroup;
function AddVMOnFreedObject: TFastMMVMOnFreedObject;
procedure BuildGroups;
property LeakCount: Integer read GetLeakCount;
property LeakGroupCount: Integer read GetLeakGroupCount;
property LeakGroupItems[AIndex: Integer]: TFastMMLeakGroup read GetLeakGroupItems;
property LeakItems[AIndex: Integer]: TFastMMLeak read GetLeaks;
property LeakSummary: TStringList read FLeakSummary;
property ReportCompilerVersion: Double read FReportCompilerVersion write FReportCompilerVersion;
property VMOnFreedObjectCount: Integer read GetVMOnFreedObjectCount;
property VMOnFreedObjectItems[AIndex: Integer]: TFastMMVMOnFreedObject read GetVMOnFreedObjectItems;
end;
TFastMMFileParser = class(TObject)
private
procedure FixStack(ALocationInfoList: TFastMMLocationInfoList);
public
function ParseFile(const AFileName: string; AReportList: TObjectList): Integer;
end;
implementation
{$IFDEF NOVIEW}
function TFastMMLocationInfoList.Add(Addr: Pointer): TJclLocationInfoEx;
begin
Result := InternalAdd(Addr);
end;
constructor TFastMMLocationInfoList.Create;
begin
inherited Create;
FOptions := [];
end;
function TFastMMLocationInfoList.GetItems(AIndex: Integer): TJclLocationInfoEx;
begin
Result := TJclLocationInfoEx(FItems[AIndex]);
end;
{$ENDIF NOVIEW}
{ TFastMMLeak }
constructor TFastMMLeak.Create(AParent: TFastMMReport);
begin
inherited Create;
FAddress := -1;
FAllocationNumber := -1;
FBlockClass := '';
FFoundMemory := False;
FLeakSize := -1;
FParent := AParent;
FThreadID := -1;
FStack := TFastMMLocationInfoList.Create;
end;
destructor TFastMMLeak.Destroy;
begin
FStack.Free;
inherited Destroy;
end;
{ TFastMMLeakGroup }
constructor TFastMMLeakGroup.Create;
begin
inherited Create;
FItems := TList.Create;
FLeakSizeUpdate := True;
end;
destructor TFastMMLeakGroup.Destroy;
begin
FItems.Free;
inherited Destroy;
end;
procedure TFastMMLeakGroup.Add(ALeak: TFastMMLeak);
begin
FItems.Add(ALeak);
end;
function TFastMMLeakGroup.GetCount: Integer;
begin
Result := FItems.Count
end;
function TFastMMLeakGroup.GetItems(AIndex: Integer): TFastMMLeak;
begin
Result := TFastMMLeak(FItems[AIndex]);
end;
function TFastMMLeakGroup.GetLeakSize: Integer;
var
I: Integer;
begin
if FLeakSizeUpdate then
begin
FLeakSizeUpdate := False;
FLeakSize := 0;
for I := 0 to Count - 1 do
Inc(FLeakSize, Items[I].LeakSize);
end;
Result := FLeakSize;
end;
{ TFastMMVMOnFreedObject }
constructor TFastMMVMOnFreedObject.Create;
begin
inherited Create;
FAddress := -1;
FAllocationNumber := -1;
FFoundMemory := False;
FStack1 := TFastMMLocationInfoList.Create;
FStack1Thread := -1;
FStack2 := TFastMMLocationInfoList.Create;
FStack2Thread := -1;
FStack3 := TFastMMLocationInfoList.Create;
FStack3Thread := -1;
end;
destructor TFastMMVMOnFreedObject.Destroy;
begin
FStack3.Free;
FStack2.Free;
FStack1.Free;
inherited Destroy;
end;
{ TFastMMReport }
constructor TFastMMReport.Create;
begin
inherited Create;
FLeakGroups := TObjectList.Create;
FLeaks := TObjectList.Create;
FLeakSummary := TStringList.Create;
{$IFDEF CONDITIONALEXPRESSIONS}
FReportCompilerVersion := CompilerVersion;
{$ELSE ~CONDITIONALEXPRESSIONS}
FReportCompilerVersion := 5.01;
{$ENDIF ~CONDITIONALEXPRESSIONS}
FVMOnFreedObjects := TObjectList.Create;
end;
destructor TFastMMReport.Destroy;
begin
FVMOnFreedObjects.Free;
FLeakSummary.Free;
FLeaks.Free;
FLeakGroups.Free;
inherited Destroy;
end;
function TFastMMReport.AddLeak: TFastMMLeak;
begin
FLeaks.Add(TFastMMLeak.Create(Self));
Result := TFastMMLeak(FLeaks.Last);
end;
function TFastMMReport.AddLeakGroup: TFastMMLeakGroup;
begin
FLeakGroups.Add(TFastMMLeakGroup.Create);
Result := TFastMMLeakGroup(FLeakGroups.Last);
end;
function TFastMMReport.AddVMOnFreedObject: TFastMMVMOnFreedObject;
begin
FVMOnFreedObjects.Add(TFastMMVMOnFreedObject.Create);
Result := TFastMMVMOnFreedObject(FVMOnFreedObjects.Last);
end;
procedure TFastMMReport.BuildGroups;
var
I: Integer;
LeftLeaks: TList;
LeakGroup: TFastMMLeakGroup;
FirstLeak: TFastMMLeak;
begin
FLeakGroups.Clear;
if LeakCount > 0 then
begin
LeftLeaks := TList.Create;
try
for I := 0 to LeakCount - 1 do
LeftLeaks.Add(LeakItems[I]);
while LeftLeaks.Count > 0 do
begin
LeakGroup := AddLeakGroup;
FirstLeak := TFastMMLeak(LeftLeaks[0]);
LeakGroup.Add(FirstLeak);
LeftLeaks.Delete(0);
for I := LeftLeaks.Count - 1 downto 0 do
if SameStack(FirstLeak.Stack, TFastMMLeak(LeftLeaks[I]).Stack) then
begin
LeakGroup.Add(TFastMMLeak(LeftLeaks[I]));
LeftLeaks.Delete(I);
end;
end;
finally
LeftLeaks.Free;
end;
end;
end;
function TFastMMReport.GetLeakCount: Integer;
begin
Result := FLeaks.Count;
end;
function TFastMMReport.GetLeakGroupCount: Integer;
begin
Result := FLeakGroups.Count
end;
function TFastMMReport.GetLeakGroupItems(AIndex: Integer): TFastMMLeakGroup;
begin
Result := TFastMMLeakGroup(FLeakGroups[AIndex]);
end;
function TFastMMReport.GetLeaks(AIndex: Integer): TFastMMLeak;
begin
Result := TFastMMLeak(FLeaks[AIndex]);
end;
function TFastMMReport.GetVMOnFreedObjectCount: Integer;
begin
Result := FVMOnFreedObjects.Count;
end;
function TFastMMReport.GetVMOnFreedObjectItems(AIndex: Integer): TFastMMVMOnFreedObject;
begin
Result := TFastMMVMOnFreedObject(FVMOnFreedObjects[AIndex]);
end;
function TFastMMReport.SameStack(AStack1, AStack2: TFastMMLocationInfoList): Boolean;
var
I: Integer;
begin
Result := Assigned(AStack1) and Assigned(AStack2) and (AStack1.Count = AStack2.Count);
if Result then
for I := 0 to AStack1.Count - 1 do
if AStack1[I].Address <> AStack2[I].Address then
begin
Result := False;
Break;
end;
end;
function GetLocationInfoFromFastMMLine(AStr: string; var ALocationInfo: TJclLocationInfoEx): Boolean;
var
I: Integer;
BlockOpen, LastIsNumber: Boolean;
C: Char;
S: string;
Blocks: TStringList;
begin
Result := False;
BlockOpen := False;
Blocks := TStringList.Create;
try
S := '';
for I := 1 to Length(AStr) do
begin
C := AStr[I];
if C = '[' then
begin
if BlockOpen then
begin
Blocks.Clear;
Break;
end
else
begin
BlockOpen := True;
S := '';
end;
end
else
if C = ']' then
begin
if BlockOpen then
begin
BlockOpen := False;
Blocks.Add(S);
end
else
begin
Blocks.Clear;
Break;
end;
end
else
S := S + C;
end;
if Blocks.Count > 0 then
begin
LastIsNumber := False;
S := Blocks[Blocks.Count - 1];
if S <> '' then
begin
LastIsNumber := True;
for I := 1 to Length(S) do
{$IFDEF COMPILER12_UP}
if not CharInSet(S[I], ['0'..'9']) then
{$ELSE !COMPILER12_UP}
if not (S[I] in ['0'..'9']) then
{$ENDIF !COMPILER12_UP}
begin
LastIsNumber := False;
Break;
end;
end;
if LastIsNumber then
begin
if Blocks.Count = 4 then
begin
ALocationInfo.SourceName := Blocks[0];
ALocationInfo.SourceUnitName := Blocks[1];
ALocationInfo.ProcedureName := Blocks[2];
ALocationInfo.LineNumber := StrToInt(Blocks[3]);
Result := True;
end
else
if Blocks.Count = 3 then
begin
ALocationInfo.SourceUnitName := Blocks[0];
ALocationInfo.ProcedureName := Blocks[1];
ALocationInfo.LineNumber := StrToInt(Blocks[2]);
Result := True;
end;
end
else
if Blocks.Count = 1 then
begin
ALocationInfo.ProcedureName := Blocks[0];
Result := True;
end
else
if Blocks.Count = 2 then
begin
ALocationInfo.SourceUnitName := Blocks[0];
ALocationInfo.ProcedureName := Blocks[1];
Result := True;
end;
if Result then
begin
S := '';
for I := 1 to Length(AStr) do
begin
C := AStr[I];
{$IFDEF COMPILER12_UP}
if CharInSet(C, ['0'..'9', 'A'..'F']) then
{$ELSE !COMPILER12_UP}
if C in ['0'..'9', 'A'..'F'] then
{$ENDIF !COMPILER12_UP}
S := S + C
else
if C = ' ' then
begin
if S <> '' then
ALocationInfo.Address := Pointer(StrToInt('$' + S));
Break;
end
else
Break;
end;
end;
end;
finally
Blocks.Free;
end;
end;
{ TFastMMFileParser }
procedure TFastMMFileParser.FixStack(ALocationInfoList: TFastMMLocationInfoList);
var
I: Integer;
FixProcedureName: Boolean;
S: string;
LocationInfoEx: TJclLocationInfoEx;
begin
if ALocationInfoList.Count > 0 then
begin
FixProcedureName := True;
for I := 0 to ALocationInfoList.Count - 1 do
begin
LocationInfoEx := ALocationInfoList[I];
if (LocationInfoEx.SourceUnitName <> '') and
(Pos(LocationInfoEx.SourceUnitName + '.', LocationInfoEx.ProcedureName) <> 1) then
begin
FixProcedureName := False;
Break;
end;
end;
if FixProcedureName then
for I := 0 to ALocationInfoList.Count - 1 do
begin
LocationInfoEx := ALocationInfoList[I];
if LocationInfoEx.SourceUnitName <> '' then
begin
S := LocationInfoEx.ProcedureName;
Delete(S, 1, Length(LocationInfoEx.SourceUnitName) + 1);
LocationInfoEx.ProcedureName := S;
end;
end;
end;
end;
{ TODO : Parse compiler version when they exist in the report }
function TFastMMFileParser.ParseFile(const AFileName: string; AReportList: TObjectList): Integer;
type { TODO : There is at least one other report type (FastMM4Messages.InterfaceErrorHeader) }
TReportType = (rtUnknown, rtMemoryLeak, rtVMOnFreedObject);
const
//Leak constants
cDateTime = '--------------------------------2';
cLeakSize = 'A memory block has been leaked. The size is: ';
cThread = 'This block was allocated by thread 0x';
cStack = 'the stack trace (return addresses) at the time was:';
cBlockClass = 'The block is currently used for an object of class: ';
cAllocNo = 'The allocation number is: ';
cMemory = 'Current memory dump of 256 bytes starting at pointer address ';
cReportEnd = 'This application has leaked memory.';
cReportSummaryPart = ' bytes: ';
//Virtual method call on freed object
cVMFOStart = 'FastMM has detected an attempt to call a virtual method on a freed object. An access violation will now be raised in order to abort the current operation.';
cVMFOClass = 'Freed object class: ';
cVMFOVirtualMethod = 'Virtual method: ';
cVMFOVirtualMethodAddress = 'Virtual method address: ';
cVMFOAllocNo = 'The allocation number was: ';
cVMFOStack1Thread = 'The object was allocated by thread 0x';
cVMFOStack1Stack = 'and the stack trace (return addresses) at the time was:';
cVMFOStack2Thread = 'The object was subsequently freed by thread 0x';
cVMFOStack2Stack = 'and the stack trace (return addresses) at the time was:';
cVMFOStack3Thread = 'The current thread ID is 0x';
cVMFOStack3Stack = 'and the stack trace (return addresses) leading to this error is:';
cVMFOMemory = 'Current memory dump of 256 bytes starting at pointer address ';
var
TSL: TStringList;
I, J, K, P: Integer;
Report: TFastMMReport;
Leak: TFastMMLeak;
VMOnFreedObject: TFastMMVMOnFreedObject;
S, S2: string;
LI: TJclLocationInfoEx;
LocationInfoEx: TJclLocationInfoEx;
MemoryArray: TFastMMMemoryArray;
CreateNewReport: Boolean;
ReportType, LastReportType: TReportType;
begin
Result := -1;
if FileExists(AFileName) and Assigned(AReportList) then
begin
TSL := TStringList.Create;
try
TSL.LoadFromFile(AFileName);
TSL.Text := AdjustLineBreaks(TSL.Text);
I := 0;
Leak := nil;
VMOnFreedObject := nil;
Report := nil;
CreateNewReport := True;
ReportType := rtUnknown;
LastReportType := rtUnknown;
while I < TSL.Count do
begin
S := TSL[I];
if Pos(cLeakSize, S) = 1 then
begin
ReportType := rtMemoryLeak;
if CreateNewReport or (LastReportType <> ReportType) then
begin
AReportList.Add(TFastMMReport.Create);
Report := TFastMMReport(AReportList.Last);
CreateNewReport := False;
end;
LastReportType := ReportType;
Leak := Report.AddLeak;
Delete(S, 1, Length(cLeakSize));
Leak.LeakSize := StrToIntDef(S, -1);
if (I > 1) then
begin
S := TSL[I - 1];
if Pos(cDateTime, S) = 1 then
begin
Delete(S, 1, Length(cDateTime) - 1);
P := Pos('-', S);
if P > 1 then
Leak.DateStr := Copy(S, 1, P - 1);
end;
end;
end
else
if Pos(cVMFOStart, S) = 1 then
begin
ReportType := rtVMOnFreedObject;
if CreateNewReport or (LastReportType <> ReportType) then
begin
AReportList.Add(TFastMMReport.Create);
Report := TFastMMReport(AReportList.Last);
CreateNewReport := False;
end;
LastReportType := ReportType;
VMOnFreedObject := Report.AddVMOnFreedObject;
end
else
if (ReportType = rtMemoryLeak) and Assigned(Leak) then
begin
if Pos(cThread, S) = 1 then
begin
Delete(S, 1, Length(cThread));
P := Pos(',', S);
if P > 1 then
Leak.ThreadID := StrToIntDef('$' + Copy(S, 1, P - 1), -1);
end;
if Pos(cStack, S) > 0 then
begin
Inc(I);
LI := TJclLocationInfoEx.Create(nil, nil);
try
while (Trim(TSL[I]) = '') or GetLocationInfoFromFastMMLine(TSL[I], LI) do
begin
if Trim(TSL[I]) <> '' then
begin
LocationInfoEx := Leak.Stack.Add(nil);
LocationInfoEx.Assign(LI);
LI.Clear;
end;
Inc(I);
end;
finally
LI.Free;
end;
Dec(I);
end;
if Pos(cBlockClass, S) = 1 then
begin
Delete(S, 1, Length(cBlockClass));
Leak.BlockClass := S;
end;
if Pos(cAllocNo, S) = 1 then
begin
Delete(S, 1, Length(cAllocNo));
Leak.AllocationNumber := StrToIntDef(S, -1);
end;
if Pos(cMemory, S) = 1 then
begin
Delete(S, 1, Length(cMemory));
P := Pos(':', S);
if P > 1 then
begin
Leak.Address := StrToIntDef('$' + Copy(S, 1, P - 1), -1);
Inc(I);
for J := 0 to 7 do
begin
S := Trim(TSL[I]);
if Length(S) = 95 then
begin
for K := 0 to 31 do
begin
S2 := Copy(S, K * 3 + 1, 2);
MemoryArray[J * 32 + K] := StrToIntDef('$' + S2, -1);
end;
end
else
Break;
Inc(I);
if J = 7 then
begin
Leak.FoundMemory := True;
Leak.Memory := MemoryArray;
end;
end;
Dec(I);
end;
end;
if Pos(cReportEnd, S) > 0 then
begin
Inc(I);
while (I < TSL.Count) and ((TSL[I]) = '') do
Inc(I);
while (I < TSL.Count) and (Pos(cReportSummaryPart, TSL[I]) > 0) do
begin
Report.LeakSummary.Add(TSL[I]);
Inc(I);
end;
CreateNewReport := True;
end;
end
else
if (ReportType = rtVMOnFreedObject) and Assigned(VMOnFreedObject) then
begin
if Pos(cVMFOClass, S) = 1 then
begin
Delete(S, 1, Length(cVMFOClass));
VMOnFreedObject.ObjectClass := S;
end
else
if Pos(cVMFOVirtualMethod, S) = 1 then
begin
Delete(S, 1, Length(cVMFOVirtualMethod));
VMOnFreedObject.VirtualMethod := S;
end
else
if Pos(cVMFOVirtualMethodAddress, S) = 1 then
begin
Delete(S, 1, Length(cVMFOVirtualMethodAddress));
VMOnFreedObject.VirtualMethodAddress := StrToIntDef('$' + S, -1);
end
else
if Pos(cVMFOAllocNo, S) = 1 then
begin
Delete(S, 1, Length(cVMFOAllocNo));
VMOnFreedObject.AllocationNumber := StrToIntDef(S, -1);
end
else
if Pos(cVMFOStack1Thread, S) = 1 then
begin
Delete(S, 1, Length(cVMFOStack1Thread));
P := Pos(',', S);
if P > 1 then
VMOnFreedObject.Stack1Thread := StrToIntDef('$' + Copy(S, 1, P - 1), -1);
if Pos(cVMFOStack1Stack, S) > 0 then
begin
Inc(I);
LI := TJclLocationInfoEx.Create(nil, nil);
try
while (Trim(TSL[I]) = '') or GetLocationInfoFromFastMMLine(TSL[I], LI) do
begin
if Trim(TSL[I]) <> '' then
begin
LocationInfoEx := VMOnFreedObject.Stack1.Add(nil);
LocationInfoEx.Assign(LI);
LI.Clear;
end;
Inc(I);
end;
finally
LI.Free;
end;
Dec(I);
end;
end
else
if Pos(cVMFOStack2Thread, S) = 1 then
begin
Delete(S, 1, Length(cVMFOStack2Thread));
P := Pos(',', S);
if P > 1 then
VMOnFreedObject.Stack2Thread := StrToIntDef('$' + Copy(S, 1, P - 1), -1);
if Pos(cVMFOStack2Stack, S) > 0 then
begin
Inc(I);
LI := TJclLocationInfoEx.Create(nil, nil);
try
while (Trim(TSL[I]) = '') or GetLocationInfoFromFastMMLine(TSL[I], LI) do
begin
if Trim(TSL[I]) <> '' then
begin
LocationInfoEx := VMOnFreedObject.Stack2.Add(nil);
LocationInfoEx.Assign(LI);
LI.Clear;
end;
Inc(I);
end;
finally
LI.Free;
end;
Dec(I);
end;
end
else
if Pos(cVMFOStack3Thread, S) = 1 then
begin
Delete(S, 1, Length(cVMFOStack3Thread));
P := Pos(',', S);
if P > 1 then
VMOnFreedObject.Stack3Thread := StrToIntDef('$' + Copy(S, 1, P - 1), -1);
if Pos(cVMFOStack3Stack, S) > 0 then
begin
Inc(I);
LI := TJclLocationInfoEx.Create(nil, nil);
try
while (Trim(TSL[I]) = '') or GetLocationInfoFromFastMMLine(TSL[I], LI) do
begin
if Trim(TSL[I]) <> '' then
begin
LocationInfoEx := VMOnFreedObject.Stack3.Add(nil);
LocationInfoEx.Assign(LI);
LI.Clear;
end;
Inc(I);
end;
finally
LI.Free;
end;
Dec(I);
end;
end
else
if Pos(cVMFOMemory, S) = 1 then
begin
Delete(S, 1, Length(cVMFOMemory));
P := Pos(':', S);
if P > 1 then
begin
VMOnFreedObject.Address := StrToIntDef('$' + Copy(S, 1, P - 1), -1);
Inc(I);
for J := 0 to 7 do
begin
while Trim(TSL[I]) = '' do
Inc(I);
S := Trim(TSL[I]);
if Length(S) = 95 then
begin
for K := 0 to 31 do
begin
S2 := Copy(S, K * 3 + 1, 2);
MemoryArray[J * 32 + K] := StrToIntDef('$' + S2, -1);
end;
end
else
Break;
Inc(I);
if J = 7 then
begin
VMOnFreedObject.FoundMemory := True;
VMOnFreedObject.Memory := MemoryArray;
end;
end;
Dec(I);
end;
end;
end;
Inc(I);
end;
finally
TSL.Free;
end;
for I := 0 to AReportList.Count - 1 do
begin
Report := TFastMMReport(AReportList[I]);
Report.BuildGroups;
for J := 0 to Report.LeakCount - 1 do
FixStack(Report.LeakItems[J].Stack);
for J := 0 to Report.VMOnFreedObjectCount - 1 do
begin
FixStack(Report.VMOnFreedObjectItems[J].Stack1);
FixStack(Report.VMOnFreedObjectItems[J].Stack2);
FixStack(Report.VMOnFreedObjectItems[J].Stack3);
end;
end;
Result := AReportList.Count;
end;
end;
end.