├── .gitignore
├── BorlndMM DLL
├── BorlndMM.dpr
├── BorlndMM.dproj
└── Precompiled
│ ├── 32Bit
│ ├── Debug
│ │ └── BorlndMM.dll
│ └── Release
│ │ └── BorlndMM.dll
│ └── 64Bit
│ ├── Debug
│ └── BorlndMM.dll
│ └── Release
│ └── BorlndMM.dll
├── Demos
├── Debug Features
│ ├── Log Leaks and Errors To File
│ │ ├── LogLeaksAndErrorsToFile.dpr
│ │ └── LogLeaksAndErrorsToFile.dproj
│ └── Memory Corruption Detection
│ │ ├── MemoryCorruptionDetectionDemo.dpr
│ │ └── MemoryCorruptionDetectionDemo.dproj
├── Memory Manager Sharing
│ ├── Delay loaded DLL
│ │ ├── FastMMInitSharing.pas
│ │ ├── TestApp.dpr
│ │ └── TestLibrary.dpr
│ ├── ShareMem
│ │ ├── ShareMemDemo.dpr
│ │ ├── ShareMemDemo.dproj
│ │ ├── ShareMemDemo.res
│ │ ├── TestLibrary.dpr
│ │ ├── TestLibrary.dproj
│ │ └── TestLibrary.res
│ └── Statically Linked DLL
│ │ ├── FastMMInitSharing.pas
│ │ ├── TestApp.dpr
│ │ └── TestLibrary.dpr
└── Usage Tracker
│ ├── DemoForm.dfm
│ ├── DemoForm.pas
│ ├── FastMMUsageTracker.dfm
│ ├── FastMMUsageTracker.pas
│ ├── UsageTrackerDemo.dpr
│ └── UsageTrackerDemo.dproj
├── FastMM5.pas
├── FullDebugMode DLL
├── FastMM_FullDebugMode.dpr
├── FastMM_FullDebugMode.dproj
└── Precompiled
│ ├── FastMM_FullDebugMode.dll
│ └── FastMM_FullDebugMode64.dll
├── Images
├── FastMM5-social.png
├── FastMM5.jpg
├── FastMM5.png
├── FastMMv5-square.jpg
└── FastMMv5-square.png
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Uncomment these types if you want even more clean repository. But be careful.
2 | # It can make harm to an existing project source. Read explanations below.
3 | #
4 | # Resource files are binaries containing manifest, project icon and version info.
5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files.
6 | #*.res
7 | #
8 | # Type library file (binary). In old Delphi versions it should be stored.
9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored.
10 | #*.tlb
11 | #
12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7.
13 | # Uncomment this if you are not using diagrams or use newer Delphi version.
14 | #*.ddp
15 | #
16 | # Visual LiveBindings file. Added in Delphi XE2.
17 | # Uncomment this if you are not using LiveBindings Designer.
18 | #*.vlb
19 | #
20 | # Deployment Manager configuration file for your project. Added in Delphi XE2.
21 | # Uncomment this if it is not mobile development and you do not use remote debug feature.
22 | #*.deployproj
23 | #
24 | # C++ object files produced when C/C++ Output file generation is configured.
25 | # Uncomment this if you are not using external objects (zlib library for example).
26 | #*.obj
27 | #
28 |
29 | # Delphi compiler-generated binaries (safe to delete)
30 | *.exe
31 | *.dll
32 | *.bpl
33 | *.bpi
34 | *.dcp
35 | *.so
36 | *.apk
37 | *.drc
38 | *.map
39 | *.dres
40 | *.rsm
41 | *.tds
42 | *.dcu
43 | *.lib
44 | *.a
45 | *.o
46 | *.ocx
47 |
48 | # Delphi autogenerated files (duplicated info)
49 | *.cfg
50 | *.hpp
51 | *Resource.rc
52 |
53 | # Delphi local files (user-specific info)
54 | *.local
55 | *.identcache
56 | *.projdata
57 | *.tvsconfig
58 | *.dsk
59 |
60 | # Delphi history and backups
61 | __history/
62 | __recovery/
63 | *.~*
64 |
65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi)
66 | *.stat
67 |
68 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss
69 | modules/
70 |
--------------------------------------------------------------------------------
/BorlndMM DLL/BorlndMM.dpr:
--------------------------------------------------------------------------------
1 | {
2 |
3 | Replacement BorlndMM.DLL using FastMM5
4 |
5 | Description:
6 | A replacement borlndmm.dll using FastMM5 instead of the RTL memory manager. This DLL may be used instead of the
7 | default DLL together with your own applications, exposing the benefits of using FastMM5 to them.
8 |
9 | Usage:
10 | 1) Compile this DLL
11 | 2) Ship it with your applications that currently use the borlndmm.dll file that ships with Delphi.
12 |
13 | }
14 |
15 | {$IMAGEBASE $00D20000}
16 |
17 | // JCL_DEBUG_EXPERT_INSERTJDBG ON
18 | library BorlndMM;
19 |
20 | uses
21 | FastMM5 in '..\FastMM5.pas',
22 | {System.SysUtils is needed for exception handling.}
23 | System.SysUtils;
24 |
25 | {$R *.RES}
26 |
27 | function GetAllocMemCount: Integer;
28 | begin
29 | Result := 0;
30 | end;
31 |
32 | function GetAllocMemSize: Integer;
33 | begin
34 | Result := 0;
35 | end;
36 |
37 | procedure DumpBlocks;
38 | begin
39 | {Do nothing}
40 | end;
41 |
42 | function HeapRelease: Integer;
43 | begin
44 | {Do nothing}
45 | Result := 2;
46 | end;
47 |
48 | function HeapAddRef: Integer;
49 | begin
50 | {Do nothing}
51 | Result := 2;
52 | end;
53 |
54 | function FastMM_GetOutputDebugStringEvents: TFastMM_MemoryManagerEventTypeSet;
55 | begin
56 | Result := FastMM_OutputDebugStringEvents;
57 | end;
58 |
59 | procedure FastMM_SetOutputDebugStringEvents(AEventTypes: TFastMM_MemoryManagerEventTypeSet);
60 | begin
61 | FastMM_OutputDebugStringEvents := AEventTypes;
62 | end;
63 |
64 | function FastMM_GetLogToFileEvents: TFastMM_MemoryManagerEventTypeSet;
65 | begin
66 | Result := FastMM_LogToFileEvents;
67 | end;
68 |
69 | procedure FastMM_SetLogToFileEvents(AEventTypes: TFastMM_MemoryManagerEventTypeSet);
70 | begin
71 | FastMM_LogToFileEvents := AEventTypes;
72 | end;
73 |
74 | function FastMM_GetMessageBoxEvents: TFastMM_MemoryManagerEventTypeSet;
75 | begin
76 | Result := FastMM_MessageBoxEvents;
77 | end;
78 |
79 | procedure FastMM_SetMessageBoxEvents(AEventTypes: TFastMM_MemoryManagerEventTypeSet);
80 | begin
81 | FastMM_MessageBoxEvents := AEventTypes;
82 | end;
83 |
84 | {$ifdef DEBUG}
85 | {The debug support library must be statically linked in order to prevent it from being unloaded before the leak check
86 | can be performed.}
87 | function LogStackTrace(AReturnAddresses: PNativeUInt; AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
88 | external {$if SizeOf(Pointer) = 4}'FastMM_FullDebugMode.dll'{$else}'FastMM_FullDebugMode64.dll'{$endif}
89 | name 'LogStackTrace';
90 | {$endif}
91 |
92 | exports
93 | GetAllocMemSize name 'GetAllocMemSize',
94 | GetAllocMemCount name 'GetAllocMemCount',
95 | FastMM_GetHeapStatus name 'GetHeapStatus',
96 | DumpBlocks name 'DumpBlocks',
97 | System.ReallocMemory name 'ReallocMemory',
98 | System.FreeMemory name 'FreeMemory',
99 | System.GetMemory name 'GetMemory',
100 | {$ifdef DEBUG}
101 | FastMM_DebugReallocMem name '@Borlndmm@SysReallocMem$qqrpvi',
102 | FastMM_DebugFreeMem name '@Borlndmm@SysFreeMem$qqrpv',
103 | FastMM_DebugGetMem name '@Borlndmm@SysGetMem$qqri',
104 | FastMM_DebugAllocMem name '@Borlndmm@SysAllocMem$qqri',
105 | {$else}
106 | FastMM_ReallocMem name '@Borlndmm@SysReallocMem$qqrpvi',
107 | FastMM_FreeMem name '@Borlndmm@SysFreeMem$qqrpv',
108 | FastMM_GetMem name '@Borlndmm@SysGetMem$qqri',
109 | FastMM_AllocMem name '@Borlndmm@SysAllocMem$qqri',
110 | {$endif}
111 | FastMM_RegisterExpectedMemoryLeak(ALeakedPointer: Pointer) name '@Borlndmm@SysRegisterExpectedMemoryLeak$qqrpi',
112 | FastMM_UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer) name '@Borlndmm@SysUnregisterExpectedMemoryLeak$qqrpi',
113 | HeapRelease name '@Borlndmm@HeapRelease$qqrv',
114 | HeapAddRef name '@Borlndmm@HeapAddRef$qqrv',
115 | {Export additional calls in order to make FastMM specific functionality available to the application and/or library.}
116 | FastMM_WalkBlocks,
117 | FastMM_ScanDebugBlocksForCorruption,
118 | FastMM_GetUsageSummary,
119 | FastMM_LogStateToFile,
120 | FastMM_EnterMinimumAddressAlignment,
121 | FastMM_ExitMinimumAddressAlignment,
122 | FastMM_GetCurrentMinimumAddressAlignment,
123 | FastMM_SetDefaultEventLogFilename,
124 | FastMM_SetEventLogFilename,
125 | FastMM_GetEventLogFilename,
126 | FastMM_DeleteEventLogFile,
127 | FastMM_GetOutputDebugStringEvents,
128 | FastMM_SetOutputDebugStringEvents,
129 | FastMM_GetLogToFileEvents,
130 | FastMM_SetLogToFileEvents,
131 | FastMM_GetMessageBoxEvents,
132 | FastMM_SetMessageBoxEvents;
133 |
134 | begin
135 | {$ifdef DEBUG}
136 | {Touch LogStackTrace in order to prevent the linker from eliminating the static link to the debug support library.}
137 | if @LogStackTrace <> nil then
138 | begin
139 | FastMM_EnterDebugMode;
140 | FastMM_MessageBoxEvents := FastMM_MessageBoxEvents + [mmetUnexpectedMemoryLeakDetail, mmetUnexpectedMemoryLeakSummary];
141 | end;
142 | {$endif}
143 | end.
144 |
--------------------------------------------------------------------------------
/BorlndMM DLL/BorlndMM.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {E9ECF6BA-0002-4690-B2C9-B93DEFE83ADC}
4 | BorlndMM.dpr
5 | True
6 | Release
7 | 3
8 | Library
9 | None
10 | 19.5
11 | Win32
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Base
29 | true
30 |
31 |
32 | true
33 | Cfg_1
34 | true
35 | true
36 |
37 |
38 | true
39 | Cfg_1
40 | true
41 | true
42 |
43 |
44 | true
45 | Base
46 | true
47 |
48 |
49 | true
50 | Cfg_2
51 | true
52 | true
53 |
54 |
55 | true
56 | Cfg_2
57 | true
58 | true
59 |
60 |
61 | false
62 | false
63 | false
64 | false
65 | false
66 | 00400000
67 | true
68 | BorlndMM
69 | 1033
70 | CompanyName=;FileDescription=FastMM5 BorlndMM library;FileVersion=5.0.4.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=5.0.4.0;Comments=;CFBundleName=
71 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)
72 | true
73 | 5
74 | 3
75 | 4
76 |
77 |
78 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
79 | Debug
80 | CompanyName=;FileDescription=FastMM5 BorlndMM library;FileVersion=5.0.4.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=5.0.4.0;Comments=;ProgramID=
81 | (None)
82 | none
83 |
84 |
85 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
86 | Debug
87 | CompanyName=;FileDescription=FastMM5 BorlndMM library;FileVersion=5.0.4.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=;ProductName=$(MSBuildProjectName);ProductVersion=5.0.4.0;Comments=
88 | (None)
89 | none
90 |
91 |
92 | RELEASE;$(DCC_Define)
93 |
94 |
95 | CompanyName=;FileDescription=FastMM5 BorlndMM library;FileVersion=5.0.4.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=5.0.4.0;Comments=;ProgramID=
96 |
97 |
98 | CompanyName=;FileDescription=FastMM5 BorlndMM library;FileVersion=5.0.4.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=;ProductName=$(MSBuildProjectName);ProductVersion=5.0.4.0;Comments=
99 |
100 |
101 | DEBUG;$(DCC_Define)
102 |
103 |
104 | CompanyName=;FileDescription=FastMM5 Debug BorlndMM library;FileVersion=5.0.4.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=5.0.4.0;Comments=;ProgramID=
105 |
106 |
107 | CompanyName=;FileDescription=FastMM5 Debug BorlndMM library;FileVersion=5.0.4.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=;ProductName=$(MSBuildProjectName);ProductVersion=5.0.4.0;Comments=
108 |
109 |
110 |
111 | MainSource
112 |
113 |
114 |
115 | Base
116 |
117 |
118 | Cfg_1
119 | Base
120 |
121 |
122 | Cfg_2
123 | Base
124 |
125 |
126 |
127 | Delphi.Personality.12
128 |
129 |
130 |
131 |
132 | BorlndMM.dpr
133 |
134 |
135 |
136 | True
137 | True
138 |
139 |
140 | 12
141 |
142 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/BorlndMM DLL/Precompiled/32Bit/Debug/BorlndMM.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/BorlndMM DLL/Precompiled/32Bit/Debug/BorlndMM.dll
--------------------------------------------------------------------------------
/BorlndMM DLL/Precompiled/32Bit/Release/BorlndMM.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/BorlndMM DLL/Precompiled/32Bit/Release/BorlndMM.dll
--------------------------------------------------------------------------------
/BorlndMM DLL/Precompiled/64Bit/Debug/BorlndMM.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/BorlndMM DLL/Precompiled/64Bit/Debug/BorlndMM.dll
--------------------------------------------------------------------------------
/BorlndMM DLL/Precompiled/64Bit/Release/BorlndMM.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/BorlndMM DLL/Precompiled/64Bit/Release/BorlndMM.dll
--------------------------------------------------------------------------------
/Demos/Debug Features/Log Leaks and Errors To File/LogLeaksAndErrorsToFile.dpr:
--------------------------------------------------------------------------------
1 | {Memory error and leak logging demo application.
2 |
3 | When deploying this application, be sure to include the .map file generated by the linker, as well as the debug mode
4 | support DLL: FastMM_FullDebugMode.dll (FastMM_FullDebugMode64.dll for 64-bit) in the same folder as the executable.}
5 |
6 | program LogLeaksAndErrorsToFile;
7 |
8 | {$APPTYPE CONSOLE}
9 |
10 | {$R *.res}
11 |
12 | uses
13 | FastMM5,
14 | System.SysUtils;
15 |
16 | procedure ConfigureFastMM;
17 | begin
18 | {Puts FastMM in debug mode. In order to obtain stack traces for leaks and errors you need to do the following:
19 | 1) Enable a detailed map file in Project Options under Linking -> Map File
20 | 2) Put the FastMM_FullDebugMode.dll (FastMM_FullDebugMode64.dll for 64-bit) in the same folder as the executable.
21 | 3) Either put the .map file in the same folder as the executable OR embed JCL debug info into the executable.}
22 | FastMM_EnterDebugMode;
23 |
24 | {We do not want any dialog boxes for errors or leaks.}
25 | FastMM_MessageBoxEvents := [];
26 |
27 | {We want all errors, memory leak details as well as leak summaries logged to a text file.}
28 | FastMM_LogToFileEvents := FastMM_LogToFileEvents + [mmetUnexpectedMemoryLeakDetail, mmetUnexpectedMemoryLeakSummary];
29 | end;
30 |
31 | procedure LeakMemory;
32 | begin
33 | TObject.Create;
34 | end;
35 |
36 | begin
37 | ConfigureFastMM;
38 |
39 | LeakMemory;
40 | end.
41 |
--------------------------------------------------------------------------------
/Demos/Debug Features/Log Leaks and Errors To File/LogLeaksAndErrorsToFile.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {25FDB4DD-EC21-44F3-AD97-83532E5AB40A}
4 | 19.5
5 | None
6 | True
7 | Debug
8 | Win32
9 | 3
10 | Console
11 | LogLeaksAndErrorsToFile.dpr
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Base
29 | true
30 |
31 |
32 | true
33 | Cfg_1
34 | true
35 | true
36 |
37 |
38 | true
39 | Base
40 | true
41 |
42 |
43 | false
44 | false
45 | false
46 | false
47 | false
48 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)
49 | LogLeaksAndErrorsToFile
50 | 1033
51 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
52 |
53 |
54 | soapserver;IndySystem;vclwinx;fmx;vclie;DbxCommonDriver;bindengine;vcldb;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;frxTee;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;IndyIPClient;dbxcds;vcledge;vclFireDAC;bindcompvclwinx;bindcomp;FireDACCommon;FmxTeeUI;QR5_R;IndyCore;RESTBackendComponents;bindcompfmx;PascalScript_Core_D;A407_R;bindcompdbx;frxADO;inetdb;rtl;FireDACMySQLDriver;FireDACSqliteDriver;DbxClientDriver;FireDACADSDriver;RESTComponents;soapmidas;DBXSqliteDriver;vcl;vclactnband;IndyIPServer;dsnapxml;fmxFireDAC;dbexpress;dsnapcon;adortl;TeeUI;Tee;Jcl;DBXMySQLDriver;CCL_R;frx;VclSmp;inet;frxe;vclimg;vcltouch;FireDACPgDriver;FireDAC;fmxase;inetdbxpress;xmlrtl;tethering;dbrtl;bindcompvcl;dsnap;fmxdae;TeeDB;CloudService;FireDACMSAccDriver;CustomIPTransport;fmxobj;bindcompvclsmp;FMXTee;JclDeveloperTools;frxDB;soaprtl;vcldsnap;DBXInterBaseDriver;FireDACIBDriver;$(DCC_UsePackage)
55 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
56 | Debug
57 | true
58 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
59 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
60 | 3
61 | C:\Temp5\(None)
62 | none
63 |
64 |
65 | soapserver;IndySystem;vclwinx;fmx;vclie;DbxCommonDriver;bindengine;vcldb;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;IndyIPClient;dbxcds;vcledge;vclFireDAC;bindcompvclwinx;bindcomp;FireDACCommon;FmxTeeUI;IndyCore;RESTBackendComponents;bindcompfmx;bindcompdbx;inetdb;rtl;FireDACMySQLDriver;FireDACSqliteDriver;DbxClientDriver;FireDACADSDriver;RESTComponents;soapmidas;DBXSqliteDriver;vcl;vclactnband;IndyIPServer;dsnapxml;fmxFireDAC;dbexpress;dsnapcon;adortl;TeeUI;Tee;DBXMySQLDriver;VclSmp;inet;vclimg;vcltouch;FireDACPgDriver;FireDAC;fmxase;inetdbxpress;xmlrtl;tethering;dbrtl;bindcompvcl;dsnap;fmxdae;TeeDB;CloudService;FireDACMSAccDriver;CustomIPTransport;fmxobj;bindcompvclsmp;FMXTee;soaprtl;vcldsnap;DBXInterBaseDriver;FireDACIBDriver;$(DCC_UsePackage)
66 | true
67 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
68 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
69 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
70 | Debug
71 | 3
72 | C:\Temp5\(None)
73 | none
74 |
75 |
76 | DEBUG;$(DCC_Define)
77 | true
78 | false
79 | true
80 | true
81 | true
82 | true
83 | true
84 |
85 |
86 | false
87 |
88 |
89 | false
90 | RELEASE;$(DCC_Define)
91 | 0
92 | 0
93 |
94 |
95 |
96 | MainSource
97 |
98 |
99 | Base
100 |
101 |
102 | Cfg_1
103 | Base
104 |
105 |
106 | Cfg_2
107 | Base
108 |
109 |
110 |
111 | Delphi.Personality.12
112 | Application
113 |
114 |
115 |
116 | LogLeaksAndErrorsToFile.dpr
117 |
118 |
119 |
120 | True
121 | True
122 |
123 |
124 | 12
125 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/Demos/Debug Features/Memory Corruption Detection/MemoryCorruptionDetectionDemo.dpr:
--------------------------------------------------------------------------------
1 | program MemoryCorruptionDetectionDemo;
2 |
3 | {$APPTYPE CONSOLE}
4 |
5 | uses
6 | {FastMM5 has to be the first unit in the DPR, otherwise FastMM5 cannot install itself.}
7 | FastMM5,
8 | System.SysUtils;
9 |
10 | procedure Test;
11 | var
12 | LPointer: PByte;
13 | begin
14 | {Allocate a 1 byte memory block.}
15 | GetMem(LPointer, 1);
16 |
17 | {Write beyond the end of the allocated memory block, thus corrupting the memory pool.}
18 | LPointer[1] := 0;
19 |
20 | {Now try to free the block. FastMM will detect that the block has been corrupted and display an error report. This
21 | error report will also be logged to a file in the same folder as the application.}
22 | FreeMem(LPointer);
23 | end;
24 |
25 | begin
26 | {Debug mode enables various consistency checks that will catch most memory corruption issues. Enabling debug mode
27 | will attempt to load the FastMM_FullDebugMode.dll library - make sure it is in the same folder, or on the path. If
28 | successful, and a map file for the application (or embedded jdbg info) is available, then crash reports will include
29 | unit and line information that will help with finding the cause of the error.}
30 | FastMM_EnterDebugMode;
31 |
32 | try
33 | Test;
34 | except
35 | on E: Exception do
36 | Writeln(E.ClassName, ': ', E.Message);
37 | end;
38 | end.
39 |
--------------------------------------------------------------------------------
/Demos/Debug Features/Memory Corruption Detection/MemoryCorruptionDetectionDemo.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {E87AE5B9-CF97-43D2-BC80-0734BF20C3A9}
4 | 19.5
5 | None
6 | True
7 | Debug
8 | Win32
9 | 3
10 | Console
11 | MemoryCorruptionDetectionDemo.dpr
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Base
29 | true
30 |
31 |
32 | true
33 | Cfg_1
34 | true
35 | true
36 |
37 |
38 | true
39 | Base
40 | true
41 |
42 |
43 | false
44 | false
45 | false
46 | false
47 | false
48 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)
49 | MemoryCorruptionDetectionDemo
50 | 3
51 | 7177
52 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
53 |
54 |
55 | vclwinx;DataSnapServer;fmx;emshosting;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;DBXMSSQLDriver;FireDACCommonODBC;emsclient;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;QR5_R;emsedge;bindcompfmx;DBXFirebirdDriver;A407_R;frxADO;inetdb;FireDACSqliteDriver;DbxClientDriver;FireDACASADriver;Tee;soapmidas;vclactnband;TeeUI;fmxFireDAC;dbexpress;Jcl;FireDACInfxDriver;DBXMySQLDriver;VclSmp;inet;DataSnapCommon;vcltouch;fmxase;DBXOdbcDriver;dbrtl;FireDACDBXDriver;FireDACOracleDriver;fmxdae;TeeDB;FireDACMSAccDriver;CustomIPTransport;FireDACMSSQLDriver;DataSnapIndy10ServerTransport;JclDeveloperTools;DataSnapConnectors;vcldsnap;DBXInterBaseDriver;FireDACMongoDBDriver;IndySystem;FireDACTDataDriver;vcldb;frxTee;vclFireDAC;bindcomp;FireDACCommon;DataSnapServerMidas;FireDACODBCDriver;emsserverresource;IndyCore;RESTBackendComponents;PascalScript_Core_D;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;DataSnapClient;DataSnapProviderClient;adortl;CCL_R;DBXSybaseASEDriver;DBXDb2Driver;frx;frxe;vclimg;DataSnapFireDAC;emsclientfiredac;FireDACPgDriver;FireDAC;FireDACDSDriver;inetdbxpress;xmlrtl;tethering;bindcompvcl;dsnap;CloudService;DBXSybaseASADriver;DBXOracleDriver;FireDACDb2Driver;DBXInformixDriver;fmxobj;bindcompvclsmp;DataSnapNativeClient;FMXTee;frxDB;DatasnapConnectorsFreePascal;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage)
56 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
57 | Debug
58 | 1033
59 | true
60 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
61 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
62 | (None)
63 | none
64 |
65 |
66 | vclwinx;DataSnapServer;fmx;emshosting;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;DBXMSSQLDriver;FireDACCommonODBC;emsclient;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;emsedge;bindcompfmx;DBXFirebirdDriver;inetdb;FireDACSqliteDriver;DbxClientDriver;FireDACASADriver;Tee;soapmidas;vclactnband;TeeUI;fmxFireDAC;dbexpress;FireDACInfxDriver;DBXMySQLDriver;VclSmp;inet;DataSnapCommon;vcltouch;fmxase;DBXOdbcDriver;dbrtl;FireDACDBXDriver;FireDACOracleDriver;fmxdae;TeeDB;FireDACMSAccDriver;CustomIPTransport;FireDACMSSQLDriver;DataSnapIndy10ServerTransport;DataSnapConnectors;vcldsnap;DBXInterBaseDriver;FireDACMongoDBDriver;IndySystem;FireDACTDataDriver;vcldb;vclFireDAC;bindcomp;FireDACCommon;DataSnapServerMidas;FireDACODBCDriver;emsserverresource;IndyCore;RESTBackendComponents;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;DataSnapClient;DataSnapProviderClient;adortl;DBXSybaseASEDriver;DBXDb2Driver;vclimg;DataSnapFireDAC;emsclientfiredac;FireDACPgDriver;FireDAC;FireDACDSDriver;inetdbxpress;xmlrtl;tethering;bindcompvcl;dsnap;CloudService;DBXSybaseASADriver;DBXOracleDriver;FireDACDb2Driver;DBXInformixDriver;fmxobj;bindcompvclsmp;DataSnapNativeClient;FMXTee;DatasnapConnectorsFreePascal;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage)
67 | true
68 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
69 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
70 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
71 | Debug
72 | 1033
73 | (None)
74 | none
75 |
76 |
77 | DEBUG;$(DCC_Define)
78 | true
79 | false
80 | true
81 | true
82 | true
83 | true
84 | true
85 |
86 |
87 | false
88 | 1033
89 | (None)
90 | none
91 |
92 |
93 | false
94 | RELEASE;$(DCC_Define)
95 | 0
96 | 0
97 |
98 |
99 |
100 | MainSource
101 |
102 |
103 | Base
104 |
105 |
106 | Cfg_1
107 | Base
108 |
109 |
110 | Cfg_2
111 | Base
112 |
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/Delay loaded DLL/FastMMInitSharing.pas:
--------------------------------------------------------------------------------
1 | {Important note: This has to be the first unit in the DPR, because memory managers cannot be switched once memory has
2 | been allocated, and the initialization sections of other units are likely to allocate memory.}
3 |
4 | unit FastMMInitSharing;
5 |
6 | interface
7 |
8 | uses
9 | FastMM5;
10 |
11 | implementation
12 |
13 | initialization
14 | {First try to share this memory manager. This will fail if another module is already sharing its memory manager. In
15 | case of the latter, try to use the memory manager shared by the other module.}
16 | if FastMM_ShareMemoryManager then
17 | begin
18 | {Try to load the debug support library (FastMM_FullDebugMode.dll, or FastMM_FullDebugMode64.dll under 64-bit). If
19 | it is available, then enter debug mode.}
20 | if FastMM_LoadDebugSupportLibrary then
21 | begin
22 | FastMM_EnterDebugMode;
23 | {In debug mode, also show the stack traces for memory leaks.}
24 | FastMM_MessageBoxEvents := FastMM_MessageBoxEvents + [mmetUnexpectedMemoryLeakDetail];
25 | end;
26 | end
27 | else
28 | begin
29 | {Another module is already sharing its memory manager, so try to use that.}
30 | FastMM_AttemptToUseSharedMemoryManager;
31 | end;
32 |
33 | end.
34 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/Delay loaded DLL/TestApp.dpr:
--------------------------------------------------------------------------------
1 | // JCL_DEBUG_EXPERT_INSERTJDBG ON
2 | program TestApp;
3 |
4 | {$APPTYPE CONSOLE}
5 |
6 | uses
7 | FastMMInitSharing,
8 | System.Classes;
9 |
10 | {Note that TestLibrary.dll is delay loaded, so it will be initialized after the main application. Consequently the
11 | library will be sharing the memory manager of the main application.}
12 | procedure LeakMemory; external 'TestLibrary' delayed;
13 |
14 | begin
15 | ReportMemoryLeaksOnShutdown := True;
16 |
17 | {Leak a TPersistent in the main application}
18 | TPersistent.Create;
19 | {Leak a TObject in the library}
20 | LeakMemory;
21 | end.
22 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/Delay loaded DLL/TestLibrary.dpr:
--------------------------------------------------------------------------------
1 | {This sample library exports a single call that will leak a TObject.}
2 |
3 | // JCL_DEBUG_EXPERT_INSERTJDBG ON
4 | library TestLibrary;
5 |
6 | uses
7 | FastMMInitSharing;
8 |
9 | procedure LeakMemory;
10 | begin
11 | TObject.Create;
12 | end;
13 |
14 | exports LeakMemory;
15 |
16 | begin
17 | end.
18 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/ShareMem/ShareMemDemo.dpr:
--------------------------------------------------------------------------------
1 | // JCL_DEBUG_EXPERT_INSERTJDBG ON
2 | program ShareMemDemo;
3 |
4 | {$APPTYPE CONSOLE}
5 |
6 | uses
7 | System.ShareMem,
8 | System.Classes;
9 |
10 | procedure LeakMemory; external 'TestLibrary';
11 |
12 | begin
13 | {Leak memory in the library}
14 | LeakMemory;
15 |
16 | {Leak memory in the main application.}
17 | TPersistent.Create;
18 |
19 | end.
20 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/ShareMem/ShareMemDemo.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {0F3B4294-351C-4BD5-8742-30FF9F553FA1}
4 | ShareMemDemo.dpr
5 | True
6 | Debug
7 | 3
8 | Console
9 | None
10 | 19.4
11 | Win64
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Base
29 | true
30 |
31 |
32 | true
33 | Cfg_1
34 | true
35 | true
36 |
37 |
38 | true
39 | Cfg_1
40 | true
41 | true
42 |
43 |
44 | true
45 | Base
46 | true
47 |
48 |
49 | true
50 | Cfg_2
51 | true
52 | true
53 |
54 |
55 | true
56 | Cfg_2
57 | true
58 | true
59 |
60 |
61 | false
62 | false
63 | false
64 | false
65 | false
66 | 00400000
67 | ShareMemDemo
68 | 7177
69 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=
70 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)
71 | $(BDS)\bin\delphi_PROJECTICON.ico
72 | $(BDS)\bin\delphi_PROJECTICNS.icns
73 | rtl;$(DCC_UsePackage)
74 | 3
75 | true
76 |
77 |
78 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
79 | Debug
80 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
81 | 1033
82 | (None)
83 | none
84 |
85 |
86 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
87 | Debug
88 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
89 | 1033
90 | (None)
91 | none
92 |
93 |
94 | RELEASE;$(DCC_Define)
95 | 0
96 | false
97 | 0
98 |
99 |
100 | 1033
101 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
102 |
103 |
104 | 1033
105 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
106 |
107 |
108 | DEBUG;$(DCC_Define)
109 | false
110 | true
111 | true
112 | true
113 |
114 |
115 | 1033
116 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
117 |
118 |
119 | 1033
120 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
121 |
122 |
123 |
124 | MainSource
125 |
126 |
127 | Base
128 |
129 |
130 | Cfg_1
131 | Base
132 |
133 |
134 | Cfg_2
135 | Base
136 |
137 |
138 |
139 | Delphi.Personality.12
140 |
141 |
142 |
143 |
144 | ShareMemDemo.dpr
145 |
146 |
147 | Microsoft Office 2000 Sample Automation Server Wrapper Components
148 | Microsoft Office XP Sample Automation Server Wrapper Components
149 |
150 |
151 |
152 | True
153 | True
154 |
155 |
156 | 12
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/ShareMem/ShareMemDemo.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/Demos/Memory Manager Sharing/ShareMem/ShareMemDemo.res
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/ShareMem/TestLibrary.dpr:
--------------------------------------------------------------------------------
1 | {This sample library exports a single call that will leak a TObject.}
2 |
3 | // JCL_DEBUG_EXPERT_INSERTJDBG ON
4 | library TestLibrary;
5 |
6 | uses
7 | System.ShareMem;
8 |
9 | procedure LeakMemory;
10 | begin
11 | TObject.Create;
12 | end;
13 |
14 | exports LeakMemory;
15 |
16 | begin
17 | end.
18 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/ShareMem/TestLibrary.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {AB6FEE68-0141-4E9F-99D5-EB10122B5946}
4 | TestLibrary.dpr
5 | True
6 | Debug
7 | 3
8 | Library
9 | None
10 | 19.4
11 | Win64
12 |
13 |
14 | true
15 |
16 |
17 | true
18 | Base
19 | true
20 |
21 |
22 | true
23 | Base
24 | true
25 |
26 |
27 | true
28 | Base
29 | true
30 |
31 |
32 | true
33 | Cfg_1
34 | true
35 | true
36 |
37 |
38 | true
39 | Cfg_1
40 | true
41 | true
42 |
43 |
44 | true
45 | Base
46 | true
47 |
48 |
49 | true
50 | Cfg_2
51 | true
52 | true
53 |
54 |
55 | true
56 | Cfg_2
57 | true
58 | true
59 |
60 |
61 | false
62 | false
63 | false
64 | false
65 | false
66 | 00400000
67 | true
68 | TestLibrary
69 | 7177
70 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=
71 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)
72 | rtl;$(DCC_UsePackage)
73 | 3
74 | true
75 |
76 |
77 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
78 | Debug
79 | true
80 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
81 | 1033
82 | ShareMemDemo.exe
83 | (None)
84 | none
85 |
86 |
87 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
88 | Debug
89 | true
90 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
91 | 1033
92 | ShareMemDemo.exe
93 | (None)
94 | none
95 |
96 |
97 | RELEASE;$(DCC_Define)
98 | 0
99 | false
100 | 0
101 |
102 |
103 | true
104 | 1033
105 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
106 |
107 |
108 | true
109 | 1033
110 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
111 |
112 |
113 | DEBUG;$(DCC_Define)
114 | false
115 | true
116 | true
117 | true
118 |
119 |
120 | true
121 | 1033
122 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
123 |
124 |
125 | true
126 | 1033
127 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
128 |
129 |
130 |
131 | MainSource
132 |
133 |
134 | Base
135 |
136 |
137 | Cfg_1
138 | Base
139 |
140 |
141 | Cfg_2
142 | Base
143 |
144 |
145 |
146 | Delphi.Personality.12
147 |
148 |
149 |
150 |
151 | TestLibrary.dpr
152 |
153 |
154 | Microsoft Office 2000 Sample Automation Server Wrapper Components
155 | Microsoft Office XP Sample Automation Server Wrapper Components
156 |
157 |
158 |
159 | True
160 | True
161 |
162 |
163 | 12
164 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/ShareMem/TestLibrary.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/Demos/Memory Manager Sharing/ShareMem/TestLibrary.res
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/Statically Linked DLL/FastMMInitSharing.pas:
--------------------------------------------------------------------------------
1 | {Important note: This has to be the first unit in the DPR, because memory managers cannot be switched once memory has
2 | been allocated, and the initialization sections of other units are likely to allocate memory.}
3 |
4 | unit FastMMInitSharing;
5 |
6 | interface
7 |
8 | uses
9 | FastMM5;
10 |
11 | implementation
12 |
13 | initialization
14 | {First try to share this memory manager. This will fail if another module is already sharing its memory manager. In
15 | case of the latter, try to use the memory manager shared by the other module.}
16 | if not FastMM_ShareMemoryManager then
17 | FastMM_AttemptToUseSharedMemoryManager;
18 | end.
19 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/Statically Linked DLL/TestApp.dpr:
--------------------------------------------------------------------------------
1 | program TestApp;
2 |
3 | {$APPTYPE CONSOLE}
4 |
5 | uses
6 | FastMMInitSharing,
7 | System.Classes;
8 |
9 | {Note that TestLibrary.dll is statically linked, so it will be initialized before the main application. This means the
10 | main application will actually be sharing the memory manager of the DLL. (If TestLibrary was loaded dynamically then
11 | it would be sharing the memory manager of the main application.)}
12 | procedure LeakMemory; external 'TestLibrary';
13 |
14 | begin
15 | {Leak a TPersistent in the main application}
16 | TPersistent.Create;
17 | {Leak a TObject in the library}
18 | LeakMemory;
19 | end.
20 |
--------------------------------------------------------------------------------
/Demos/Memory Manager Sharing/Statically Linked DLL/TestLibrary.dpr:
--------------------------------------------------------------------------------
1 | {This sample library exports a single call that will leak a TObject.}
2 |
3 | library TestLibrary;
4 |
5 | uses
6 | FastMMInitSharing;
7 |
8 | procedure LeakMemory;
9 | begin
10 | TObject.Create;
11 | end;
12 |
13 | exports LeakMemory;
14 |
15 | begin
16 | ReportMemoryLeaksOnShutdown := True;
17 | end.
18 |
--------------------------------------------------------------------------------
/Demos/Usage Tracker/DemoForm.dfm:
--------------------------------------------------------------------------------
1 | object fDemo: TfDemo
2 | Left = 199
3 | Top = 114
4 | BorderIcons = [biSystemMenu]
5 | BorderStyle = bsSingle
6 | Caption = 'Usage Tracker Demo'
7 | ClientHeight = 53
8 | ClientWidth = 239
9 | Color = clBtnFace
10 | Font.Charset = DEFAULT_CHARSET
11 | Font.Color = clWindowText
12 | Font.Height = -11
13 | Font.Name = 'MS Sans Serif'
14 | Font.Style = []
15 | Position = poScreenCenter
16 | TextHeight = 13
17 | object bShowTracker: TButton
18 | Left = 8
19 | Top = 8
20 | Width = 221
21 | Height = 37
22 | Caption = 'Show Usage Tracker'
23 | TabOrder = 0
24 | OnClick = bShowTrackerClick
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/Demos/Usage Tracker/DemoForm.pas:
--------------------------------------------------------------------------------
1 | unit DemoForm;
2 |
3 | interface
4 |
5 | uses
6 | Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, FastMMUsageTracker;
7 |
8 | type
9 | TfDemo = class(TForm)
10 | bShowTracker: TButton;
11 | procedure bShowTrackerClick(Sender: TObject);
12 | private
13 | public
14 | end;
15 |
16 | var
17 | fDemo: TfDemo;
18 |
19 | implementation
20 |
21 | {$R *.dfm}
22 |
23 | procedure TfDemo.bShowTrackerClick(Sender: TObject);
24 | begin
25 | ShowFastMMUsageTracker;
26 | end;
27 |
28 | end.
29 |
--------------------------------------------------------------------------------
/Demos/Usage Tracker/FastMMUsageTracker.dfm:
--------------------------------------------------------------------------------
1 | object fFastMMUsageTracker: TfFastMMUsageTracker
2 | Left = 460
3 | Top = 178
4 | BorderIcons = [biSystemMenu]
5 | BorderStyle = bsSingle
6 | Caption = 'FastMM Memory Usage Tracker'
7 | ClientHeight = 556
8 | ClientWidth = 553
9 | Color = clBtnFace
10 | Font.Charset = DEFAULT_CHARSET
11 | Font.Color = clWindowText
12 | Font.Height = -11
13 | Font.Name = 'MS Sans Serif'
14 | Font.Style = []
15 | Position = poScreenCenter
16 | OnClose = FormClose
17 | OnCreate = FormCreate
18 | TextHeight = 13
19 | object bClose: TBitBtn
20 | Left = 472
21 | Top = 524
22 | Width = 75
23 | Height = 25
24 | Caption = 'Close'
25 | Glyph.Data = {
26 | 76010000424D7601000000000000760000002800000020000000100000000100
27 | 04000000000000010000130B0000130B00001000000000000000000000000000
28 | 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
29 | FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333
30 | 3333333333FFFFF3333333333999993333333333F77777FFF333333999999999
31 | 3333333777333777FF3333993333339993333377FF3333377FF3399993333339
32 | 993337777FF3333377F3393999333333993337F777FF333337FF993399933333
33 | 399377F3777FF333377F993339993333399377F33777FF33377F993333999333
34 | 399377F333777FF3377F993333399933399377F3333777FF377F993333339993
35 | 399377FF3333777FF7733993333339993933373FF3333777F7F3399933333399
36 | 99333773FF3333777733339993333339933333773FFFFFF77333333999999999
37 | 3333333777333777333333333999993333333333377777333333}
38 | NumGlyphs = 2
39 | TabOrder = 0
40 | OnClick = bCloseClick
41 | end
42 | object bUpdate: TBitBtn
43 | Left = 392
44 | Top = 524
45 | Width = 75
46 | Height = 25
47 | Caption = 'Update'
48 | Glyph.Data = {
49 | 76010000424D7601000000000000760000002800000020000000100000000100
50 | 04000000000000010000120B0000120B00001000000000000000000000000000
51 | 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000
52 | FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00370777033333
53 | 3330337F3F7F33333F3787070003333707303F737773333373F7007703333330
54 | 700077337F3333373777887007333337007733F773F333337733700070333333
55 | 077037773733333F7F37703707333300080737F373333377737F003333333307
56 | 78087733FFF3337FFF7F33300033330008073F3777F33F777F73073070370733
57 | 078073F7F7FF73F37FF7700070007037007837773777F73377FF007777700730
58 | 70007733FFF77F37377707700077033707307F37773F7FFF7337080777070003
59 | 3330737F3F7F777F333778080707770333333F7F737F3F7F3333080787070003
60 | 33337F73FF737773333307800077033333337337773373333333}
61 | NumGlyphs = 2
62 | TabOrder = 1
63 | OnClick = bUpdateClick
64 | end
65 | object ChkAutoUpdate: TCheckBox
66 | Left = 280
67 | Top = 528
68 | Width = 97
69 | Height = 17
70 | Caption = 'Auto Update'
71 | TabOrder = 2
72 | OnClick = ChkAutoUpdateClick
73 | end
74 | object pcUsageTracker: TPageControl
75 | Left = 0
76 | Top = 0
77 | Width = 553
78 | Height = 521
79 | ActivePage = tsGeneralInformation
80 | Align = alTop
81 | TabOrder = 3
82 | object tsAllocation: TTabSheet
83 | Caption = 'FastMM4 Allocation'
84 | object sgBlockStatistics: TStringGrid
85 | Left = 4
86 | Top = 4
87 | Width = 533
88 | Height = 481
89 | DefaultColWidth = 83
90 | DefaultRowHeight = 17
91 | Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine]
92 | PopupMenu = smMM4Allocation
93 | ScrollBars = ssVertical
94 | TabOrder = 0
95 | OnDrawCell = sgBlockStatisticsDrawCell
96 | ColWidths = (
97 | 83
98 | 104
99 | 106
100 | 106
101 | 108)
102 | end
103 | end
104 | object tsVMGraph: TTabSheet
105 | Caption = 'VM Graph'
106 | ImageIndex = 1
107 | object Label1: TLabel
108 | Left = 8
109 | Top = 440
110 | Width = 38
111 | Height = 13
112 | Caption = 'Address'
113 | end
114 | object Label2: TLabel
115 | Left = 152
116 | Top = 440
117 | Width = 25
118 | Height = 13
119 | Caption = 'State'
120 | end
121 | object Label3: TLabel
122 | Left = 8
123 | Top = 468
124 | Width = 43
125 | Height = 13
126 | Caption = 'Exe/DLL'
127 | end
128 | object eAddress: TEdit
129 | Left = 60
130 | Top = 436
131 | Width = 81
132 | Height = 21
133 | Enabled = False
134 | TabOrder = 0
135 | Text = '$00000000'
136 | end
137 | object eState: TEdit
138 | Left = 184
139 | Top = 436
140 | Width = 105
141 | Height = 21
142 | Enabled = False
143 | TabOrder = 1
144 | Text = 'Unallocated'
145 | end
146 | object eDLLName: TEdit
147 | Left = 60
148 | Top = 464
149 | Width = 477
150 | Height = 21
151 | ReadOnly = True
152 | TabOrder = 2
153 | end
154 | object ChkSmallGraph: TCheckBox
155 | Left = 304
156 | Top = 436
157 | Width = 97
158 | Height = 21
159 | Caption = 'Small Map'
160 | Checked = True
161 | State = cbChecked
162 | TabOrder = 3
163 | OnClick = ChkSmallGraphClick
164 | end
165 | object dgMemoryMap: TDrawGrid
166 | Left = 4
167 | Top = 4
168 | Width = 533
169 | Height = 425
170 | ColCount = 64
171 | DefaultColWidth = 8
172 | DefaultRowHeight = 8
173 | FixedCols = 0
174 | RowCount = 1024
175 | FixedRows = 0
176 | GridLineWidth = 0
177 | Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine]
178 | ScrollBars = ssVertical
179 | TabOrder = 4
180 | OnDrawCell = dgMemoryMapDrawCell
181 | OnSelectCell = dgMemoryMapSelectCell
182 | end
183 | end
184 | object tsVMDump: TTabSheet
185 | Caption = 'VM Dump'
186 | ImageIndex = 2
187 | object sgVMDump: TStringGrid
188 | Left = 4
189 | Top = 4
190 | Width = 533
191 | Height = 481
192 | DefaultColWidth = 83
193 | DefaultRowHeight = 17
194 | FixedCols = 0
195 | Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine]
196 | PopupMenu = smVMDump
197 | ScrollBars = ssVertical
198 | TabOrder = 0
199 | OnDrawCell = sgVMDumpDrawCell
200 | OnMouseDown = sgVMDumpMouseDown
201 | OnMouseUp = sgVMDumpMouseUp
202 | ColWidths = (
203 | 83
204 | 96
205 | 60
206 | 58
207 | 209)
208 | end
209 | end
210 | object tsGeneralInformation: TTabSheet
211 | Caption = 'General Information'
212 | ImageIndex = 3
213 | object mVMStatistics: TMemo
214 | Left = 4
215 | Top = 4
216 | Width = 533
217 | Height = 481
218 | Font.Charset = ANSI_CHARSET
219 | Font.Color = clWindowText
220 | Font.Height = -11
221 | Font.Name = 'Courier New'
222 | Font.Style = []
223 | ParentFont = False
224 | PopupMenu = smGeneralInformation
225 | ReadOnly = True
226 | ScrollBars = ssVertical
227 | TabOrder = 0
228 | end
229 | end
230 | end
231 | object tTimer: TTimer
232 | Enabled = False
233 | Interval = 2000
234 | OnTimer = tTimerTimer
235 | Left = 128
236 | Top = 512
237 | end
238 | object smVMDump: TPopupMenu
239 | Left = 100
240 | Top = 512
241 | object miVMDumpCopyAlltoClipboard: TMenuItem
242 | Caption = '&Copy All to Clipboard'
243 | OnClick = miVMDumpCopyAlltoClipboardClick
244 | end
245 | end
246 | object smGeneralInformation: TPopupMenu
247 | Left = 68
248 | Top = 512
249 | object miGeneralInformationCopyAlltoClipboard: TMenuItem
250 | Caption = '&Copy All to Clipboard'
251 | OnClick = miGeneralInformationCopyAlltoClipboardClick
252 | end
253 | end
254 | object smMM4Allocation: TPopupMenu
255 | Left = 36
256 | Top = 512
257 | object siMM4AllocationCopyAlltoClipboard: TMenuItem
258 | Caption = '&Copy All to Clipboard'
259 | OnClick = siMM4AllocationCopyAlltoClipboardClick
260 | end
261 | end
262 | end
263 |
--------------------------------------------------------------------------------
/Demos/Usage Tracker/FastMMUsageTracker.pas:
--------------------------------------------------------------------------------
1 | (*
2 |
3 | Fast Memory Manager Usage Tracker 2.20
4 |
5 | Description:
6 |
7 | - Shows FastMM5 allocation usage
8 |
9 | - Shows VM Memory in graphical map
10 | - Free
11 | - Commit
12 | - Reserved
13 | - EXE (Red)
14 | - DLLs (Blue)
15 |
16 | - VM Dump of the whole process
17 | (2GB standard, 3GB with the /3G switch set, and 4GB under WoW64)
18 |
19 | - General Information
20 | - System memory usage
21 | - Process memory usage
22 | - 5 Largest contiguous free VM memory spaces
23 | - FastMM5 summary information
24 |
25 | Usage:
26 | - Add the FastMMUsageTracker unit
27 | - Add the ShowFastMMUsageTracker procedure to an event
28 | - FastMMUsageTracker form should not be autocreated
29 |
30 | Notes:
31 | - Consider setting the base adress of your BPLs & DLLs or use Microsoft's
32 | ReBase.exe to set third party BPLs and DLLs. Libraries that do not have to
33 | be relocated can be shared across processes, thus conserving system
34 | resources.
35 | - The first of the "Largest contiguous free VM memory spaces" gives you an
36 | indication of the largest single memory block that can be allocated.
37 |
38 | Change log:
39 |
40 | Version 2.10 (22 September 2009):
41 | - New usage tracker implemented by Hanspeter Widmer with many new features.
42 | (Thanks Hanspeter!);
43 | - Colour coding of changes in the allocation map added by Murray McGowan
44 | (red for an increase in usage, green for a decrease). (Thanks Murray!)
45 | Version 2.2 (2 April 2024):
46 | - Updated for FastMM5
47 | *)
48 |
49 | unit FastMMUsageTracker;
50 |
51 | interface
52 |
53 | uses
54 | Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Grids,
55 | Buttons, ComCtrls, Menus, FastMM5;
56 |
57 | type
58 | TChunkStatusEx = (
59 | {Items that correspond to the same entry in TChunkStatus}
60 | csExUnallocated,
61 | csExAllocated,
62 | csExReserved,
63 | csExSysAllocated,
64 | csExSysReserved,
65 | {TChunkStatusEx additional detail}
66 | csExSysExe,
67 | csExSysDLL);
68 |
69 | TMemoryMapEx = array[0..65535] of TChunkStatusEx;
70 |
71 | TfFastMMUsageTracker = class(TForm)
72 | tTimer: TTimer;
73 | bClose: TBitBtn;
74 | bUpdate: TBitBtn;
75 | ChkAutoUpdate: TCheckBox;
76 | smVMDump: TPopupMenu;
77 | smMM4Allocation: TPopupMenu;
78 | smGeneralInformation: TPopupMenu;
79 | miVMDumpCopyAlltoClipboard: TMenuItem;
80 | miGeneralInformationCopyAlltoClipboard: TMenuItem;
81 | siMM4AllocationCopyAlltoClipboard: TMenuItem;
82 | pcUsageTracker: TPageControl;
83 | tsAllocation: TTabSheet;
84 | tsVMGraph: TTabSheet;
85 | tsVMDump: TTabSheet;
86 | tsGeneralInformation: TTabSheet;
87 | mVMStatistics: TMemo;
88 | sgVMDump: TStringGrid;
89 | Label1: TLabel;
90 | Label2: TLabel;
91 | Label3: TLabel;
92 | eAddress: TEdit;
93 | eState: TEdit;
94 | eDLLName: TEdit;
95 | ChkSmallGraph: TCheckBox;
96 | sgBlockStatistics: TStringGrid;
97 | dgMemoryMap: TDrawGrid;
98 | procedure FormClose(Sender: TObject; var Action: TCloseAction);
99 | procedure tTimerTimer(Sender: TObject);
100 | procedure FormCreate(Sender: TObject);
101 | procedure bCloseClick(Sender: TObject);
102 | procedure dgMemoryMapDrawCell(Sender: TObject; ACol, ARow: Integer;
103 | Rect: TRect; State: TGridDrawState);
104 | procedure dgMemoryMapSelectCell(Sender: TObject; ACol, ARow: Integer;
105 | var CanSelect: Boolean);
106 | procedure bUpdateClick(Sender: TObject);
107 | procedure ChkAutoUpdateClick(Sender: TObject);
108 | procedure ChkSmallGraphClick(Sender: TObject);
109 | procedure sgVMDumpMouseDown(Sender: TObject; Button: TMouseButton;
110 | Shift: TShiftState; X, Y: Integer);
111 | procedure sgVMDumpMouseUp(Sender: TObject; Button: TMouseButton;
112 | Shift: TShiftState; X, Y: Integer);
113 | procedure sgVMDumpDrawCell(Sender: TObject; ACol, ARow: Integer;
114 | Rect: TRect; State: TGridDrawState);
115 | procedure miVMDumpCopyAlltoClipboardClick(Sender: TObject);
116 | procedure miGeneralInformationCopyAlltoClipboardClick(Sender: TObject);
117 | procedure siMM4AllocationCopyAlltoClipboardClick(Sender: TObject);
118 | procedure sgBlockStatisticsDrawCell(Sender: TObject; ACol,
119 | ARow: Integer; Rect: TRect; State: TGridDrawState);
120 | private
121 | {The current and previous memory manager states}
122 | FMemoryManagerState, FPrevMemoryManagerState: TFastMM_MemoryManagerState;
123 | FMemoryMapEx: TMemoryMapEx;
124 | AddressSpacePageCount: Integer;
125 | OR_VMDumpDownCell: TGridCoord;
126 | procedure HeaderClicked(AGrid: TStringgrid; const ACell: TGridCoord);
127 | procedure SortGrid(grid: TStringgrid; PB_Nummeric: Boolean; byColumn: Integer; ascending: Boolean);
128 | procedure UpdateGraphMetrics;
129 | public
130 | {Refreshes the display}
131 | procedure RefreshSnapShot;
132 | end;
133 |
134 | function ShowFastMMUsageTracker: TfFastMMUsageTracker;
135 |
136 | implementation
137 |
138 | uses
139 | Clipbrd, PsAPI;
140 |
141 | {$R *.dfm}
142 |
143 | const
144 | SystemBasicInformation = 0;
145 | SystemPerformanceInformation = 2;
146 | SystemTimeInformation = 3;
147 |
148 |
149 | type
150 | {To get access to protected methods}
151 | TLocalStringGrid = class(TStringGrid);
152 |
153 | TMemoryStatusEx = packed record
154 | dwLength: DWORD;
155 | dwMemoryLoad: DWORD;
156 | ullTotalPhys: Int64;
157 | ullAvailPhys: Int64;
158 | ullTotalPageFile: Int64;
159 | ullAvailPageFile: Int64;
160 | ullTotalVirtual: Int64;
161 | ullAvailVirtual: Int64;
162 | ullAvailExtendedVirtual: Int64;
163 | end;
164 | PMemoryStatusEx = ^TMemoryStatusEx;
165 | LPMEMORYSTATUSEX = PMemoryStatusEx;
166 |
167 | TP_GlobalMemoryStatusEx = function(
168 | var PR_MemStatusEx: TMemoryStatusEx): LongBool; stdcall;
169 |
170 | TSystem_Basic_Information = packed record
171 | dwUnknown1: DWORD;
172 | uKeMaximumIncrement: ULONG;
173 | uPageSize: ULONG;
174 | uMmNumberOfPhysicalPages: ULONG;
175 | uMmLowestPhysicalPage: ULONG;
176 | uMmHighestPhysicalPage: ULONG;
177 | uAllocationGranularity: ULONG;
178 | pLowestUserAddress: Pointer;
179 | pMmHighestUserAddress: Pointer;
180 | uKeActiveProcessors: ULONG;
181 | bKeNumberProcessors: Byte;
182 | bUnknown2: Byte;
183 | wUnknown3: Word;
184 | end;
185 |
186 | TSystem_Performance_Information = packed record
187 | liIdleTime: LARGE_INTEGER;
188 | dwSpare: array[0..75] of DWORD;
189 | end;
190 |
191 | TSystem_Time_Information = packed record
192 | liKeBootTime: LARGE_INTEGER;
193 | liKeSystemTime: LARGE_INTEGER;
194 | liExpTimeZoneBias: LARGE_INTEGER;
195 | uCurrentTimeZoneId: ULONG;
196 | dwReserved: DWORD;
197 | end;
198 |
199 | TP_NtQuerySystemInformation = function(InfoClass: DWORD; Buffer: Pointer;
200 | BufSize: DWORD; ReturnSize: PCardinal): DWORD; stdcall;
201 |
202 | var
203 | MP_GlobalMemoryStatusEx: TP_GlobalMemoryStatusEx = nil;
204 | MP_NtQuerySystemInformation: TP_NtQuerySystemInformation = nil;
205 |
206 | //-----------------------------------------------------------------------------
207 | // Various Global Procedures
208 | //-----------------------------------------------------------------------------
209 |
210 | function ShowFastMMUsageTracker: TfFastMMUsageTracker;
211 | begin
212 | Application.CreateForm(TfFastMMUsageTracker, Result);
213 | if Assigned(Result) then
214 | begin
215 | Result.RefreshSnapShot;
216 | Result.Show;
217 | end;
218 | end;
219 |
220 | function CardinalToStringFormatted(const ACardinal: Cardinal): string;
221 | begin
222 | Result := FormatFloat('#,##0', ACardinal);
223 | end;
224 |
225 | function Int64ToStringFormatted(const AInt64: Int64): string;
226 | begin
227 | Result := FormatFloat('#,##0', AInt64);
228 | end;
229 |
230 | function CardinalToKStringFormatted(const ACardinal: Cardinal): string;
231 | begin
232 | Result := FormatFloat('#,##0', ACardinal div 1024) + 'K';
233 | end;
234 |
235 | function Int64ToKStringFormatted(const AInt64: Int64): string;
236 | begin
237 | Result := FormatFloat('#,##0', AInt64 div 1024) + 'K';
238 | end;
239 |
240 | procedure CopyGridContentsToClipBoard(AStringGrid: TStringGrid);
241 | const
242 | TAB = Chr(VK_TAB);
243 | CRLF = #13#10;
244 | var
245 | LI_r, LI_c: Integer;
246 | LS_S: string;
247 | begin
248 | LS_S := '';
249 | for LI_r := 0 to AStringGrid.RowCount - 1 do
250 | begin
251 | for LI_c := 0 to AStringGrid.ColCount - 1 do
252 | begin
253 | LS_S := LS_S + AStringGrid.Cells[LI_c, LI_r];
254 | if LI_c < AStringGrid.ColCount - 1 then
255 | LS_S := LS_S + TAB;
256 | end;
257 | if LI_r < AStringGrid.RowCount - 1 then
258 | LS_S := LS_S + CRLF;
259 | end;
260 | ClipBoard.SetTextBuf(PChar(LS_S));
261 | end;
262 |
263 | function LocSort(P1, P2: Pointer): Integer;
264 | begin
265 | if NativeUInt(P1) = NativeUInt(P2) then
266 | Result := 0
267 | else
268 | begin
269 | if NativeUInt(P1) > NativeUInt(P2) then
270 | Result := -1
271 | else
272 | Result := 1;
273 | end;
274 | end;
275 |
276 | //-----------------------------------------------------------------------------
277 | // Form TfFastMMUsageTracker
278 | //-----------------------------------------------------------------------------
279 |
280 | procedure TfFastMMUsageTracker.FormCreate(Sender: TObject);
281 | var
282 | LR_SystemInfo: TSystemInfo;
283 | begin
284 | pcUsageTracker.ActivePage := tsAllocation;
285 | GetSystemInfo(LR_SystemInfo);
286 | {Get the number of address space pages}
287 | if (Cardinal(LR_SystemInfo.lpMaximumApplicationAddress) and $80000000) = 0 then
288 | AddressSpacePageCount := 32768
289 | else
290 | AddressSpacePageCount := 65536;
291 | {Update the graph metricx}
292 | UpdateGraphMetrics;
293 | {Set up the StringGrid columns}
294 | with sgBlockStatistics do
295 | begin
296 | Cells[0, 0] := 'Block Size';
297 | Cells[1, 0] := '# Live Pointers';
298 | Cells[2, 0] := 'Live Size';
299 | Cells[3, 0] := 'Used Space';
300 | Cells[4, 0] := 'Efficiency';
301 | end;
302 | with sgVMDump do
303 | begin
304 | Cells[0, 0] := 'VM Block';
305 | Cells[1, 0] := 'Size';
306 | Cells[2, 0] := 'Type';
307 | Cells[3, 0] := 'State';
308 | Cells[4, 0] := 'EXE/DLL';
309 | end;
310 | end;
311 |
312 | procedure TfFastMMUsageTracker.FormClose(Sender: TObject; var Action: TCloseAction);
313 | begin
314 | Action := caFree;
315 | end;
316 |
317 | procedure TfFastMMUsageTracker.SortGrid(grid: TStringgrid; PB_Nummeric: Boolean; byColumn: Integer; ascending: Boolean);
318 |
319 | function CompareNumeric(const S1, S2: string): Integer;
320 | var
321 | LVal1, LVal2: Integer;
322 | begin
323 | begin
324 | LVal1 := StrToInt(S1);
325 | LVal2 := StrToInt(S2);
326 | if LVal1 = LVal2 then
327 | begin
328 | Result := 0;
329 | end
330 | else
331 | begin
332 | if LVal1 > LVal2 then
333 | Result := 1
334 | else
335 | Result := -1;
336 | end;
337 | end;
338 | end;
339 |
340 | procedure ExchangeGridRows(i, j: Integer);
341 | var
342 | k: Integer;
343 | begin
344 | for k := 0 to Grid.ColCount - 1 do
345 | Grid.Cols[k].Exchange(i, j);
346 | end;
347 |
348 | procedure QuickSortNummeric(L, R: Integer);
349 | var
350 | I, J: Integer;
351 | P: string;
352 | begin
353 | repeat
354 | I := L;
355 | J := R;
356 | P := Grid.Cells[byColumn, (L + R) shr 1];
357 | repeat
358 | while CompareNumeric(Grid.Cells[byColumn, I], P) < 0 do
359 | Inc(I);
360 | while CompareNumeric(Grid.Cells[byColumn, J], P) > 0 do
361 | Dec(J);
362 | if I <= J then
363 | begin
364 | if I <> J then
365 | ExchangeGridRows(I, J);
366 | Inc(I);
367 | Dec(J);
368 | end;
369 | until I > J;
370 | if L < J then
371 | QuickSortNummeric(L, J);
372 | L := I;
373 | until I >= R;
374 | end;
375 |
376 | procedure QuickSortString(L, R: Integer);
377 | var
378 | I, J: Integer;
379 | P: string;
380 | begin
381 | repeat
382 | I := L;
383 | J := R;
384 | P := Grid.Cells[byColumn, (L + R) shr 1];
385 | repeat
386 | while CompareText(Grid.Cells[byColumn, I], P) < 0 do
387 | Inc(I);
388 | while CompareText(Grid.Cells[byColumn, J], P) > 0 do
389 | Dec(J);
390 | if I <= J then
391 | begin
392 | if I <> J then
393 | ExchangeGridRows(I, J);
394 | Inc(I);
395 | Dec(J);
396 | end;
397 | until I > J;
398 | if L < J then
399 | QuickSortString(L, J);
400 | L := I;
401 | until I >= R;
402 | end;
403 |
404 | procedure InvertGrid;
405 | var
406 | i, j: Integer;
407 | begin
408 | i := Grid.Fixedrows;
409 | j := Grid.Rowcount - 1;
410 | while i < j do
411 | begin
412 | ExchangeGridRows(I, J);
413 | Inc(i);
414 | Dec(j);
415 | end;
416 | end;
417 |
418 | begin
419 | Screen.Cursor := crHourglass;
420 | Grid.Perform(WM_SETREDRAW, 0, 0);
421 | try
422 | if PB_Nummeric then
423 | QuickSortNummeric(Grid.FixedRows, Grid.Rowcount - 1)
424 | else
425 | QuickSortString(Grid.FixedRows, Grid.Rowcount - 1);
426 | if not Ascending then
427 | InvertGrid;
428 | finally
429 | Grid.Perform(WM_SETREDRAW, 1, 0);
430 | Grid.Refresh;
431 | Screen.Cursor := crDefault;
432 | end;
433 | end;
434 |
435 |
436 | procedure TfFastMMUsageTracker.HeaderClicked(AGrid: TStringgrid; const ACell: TGridCoord);
437 | var
438 | i: Integer;
439 | LNumericSort: Boolean;
440 | begin
441 | // The header cell stores a flag in the Objects property that signals the
442 | // current sort order of the grid column. A value of 0 shows no sort marker,
443 | // 1 means sorted ascending, -1 sorted descending
444 | // clear markers
445 | for i := AGrid.FixedCols to AGrid.ColCount - 1 do
446 | begin
447 | if Assigned(AGrid.Objects[i, 0]) and (i <> ACell.x) then
448 | begin
449 | AGrid.Objects[i, 0] := nil;
450 | TLocalStringGrid(AGrid).InvalidateCell(i, 0);
451 | end;
452 | end;
453 | // Sort grid on new column. If grid is currently sorted ascending on this
454 | // column we invert the sort direction, otherwise we sort it ascending.
455 | if ACell.X = 1 then
456 | LNumericSort := True
457 | else
458 | LNumericSort := False;
459 | if Integer(AGrid.Objects[ACell.x, ACell.y]) = 1 then
460 | begin
461 | SortGrid(AGrid, LNumericSort, ACell.x, False);
462 | AGrid.Objects[ACell.x, 0] := Pointer(-1);
463 | end
464 | else
465 | begin
466 | SortGrid(AGrid, LNumericSort, ACell.x, True);
467 | AGrid.Objects[ACell.x, 0] := Pointer(1);
468 | end;
469 | TLocalStringGrid(AGrid).InvalidateCell(ACell.x, ACell.y);
470 | end;
471 |
472 | procedure TfFastMMUsageTracker.UpdateGraphMetrics;
473 | begin
474 | if ChkSmallGraph.Checked then
475 | begin
476 | dgMemoryMap.DefaultColWidth := 4;
477 | dgMemoryMap.ColCount := 128;
478 | end
479 | else
480 | begin
481 | dgMemoryMap.DefaultColWidth := 8;
482 | dgMemoryMap.ColCount := 64;
483 | end;
484 | dgMemoryMap.DefaultRowHeight := dgMemoryMap.DefaultColWidth;
485 | dgMemoryMap.RowCount := AddressSpacePageCount div dgMemoryMap.ColCount;
486 | end;
487 |
488 | procedure TfFastMMUsageTracker.RefreshSnapShot;
489 | var
490 | LP_FreeVMList: TList;
491 | LU_MEM_FREE: SIZE_T;
492 | LU_MEM_COMMIT: SIZE_T;
493 | LU_MEM_RESERVE: SIZE_T;
494 | LAllocatedSize, LTotalBlocks, LTotalAllocated, LTotalReserved,
495 | LPrevAllocatedSize, LPrevTotalBlocks, LPrevTotalAllocated, LPrevTotalReserved: NativeUInt;
496 |
497 | procedure UpdateVMGraph(var AMemoryMap: TMemoryMapEx);
498 | var
499 | LInd, LIndTop, I1: Integer;
500 | LChunkState: TChunkStatusEx;
501 | LMBI: TMemoryBasicInformation;
502 | LA_Char: array[0..MAX_PATH] of Char;
503 | begin
504 | LInd := 0;
505 | repeat
506 | {If the chunk is not allocated by this MM, what is its status?}
507 | if AMemoryMap[LInd] = csExSysAllocated then
508 | begin
509 | {Get all the reserved memory blocks and windows allocated memory blocks, etc.}
510 | VirtualQuery(Pointer(LInd * 65536), LMBI, SizeOf(LMBI));
511 | if LMBI.State = MEM_COMMIT then
512 | begin
513 | if (GetModuleFileName(DWord(LMBI.AllocationBase), LA_Char, MAX_PATH) <> 0) then
514 | begin
515 | if DWord(LMBI.AllocationBase) = SysInit.HInstance then
516 | LChunkState := csExSysExe
517 | else
518 | LChunkState := csExSysDLL;
519 | end
520 | else
521 | begin
522 | LChunkState := csExSysAllocated;
523 | end;
524 | if LMBI.RegionSize > 65536 then
525 | begin
526 | LIndTop := (Cardinal(LMBI.BaseAddress) + Cardinal(LMBI.RegionSize)) div 65536;
527 | // Fill up multiple tables
528 | for I1 := LInd to LIndTop do
529 | AMemoryMap[I1] := LChunkState;
530 | LInd := LIndTop;
531 | end
532 | else
533 | begin
534 | AMemoryMap[LInd] := LChunkState;
535 | end;
536 | end
537 | end;
538 | Inc(LInd);
539 | until LInd >= AddressSpacePageCount;
540 | end;
541 |
542 | procedure UpdateVMDump;
543 | var
544 | LP_Base: PByte;
545 | LR_Info: TMemoryBasicInformation;
546 | LU_rv: SIZE_T;
547 | LI_I: Integer;
548 | LA_Char: array[0..MAX_PATH] of Char;
549 | begin
550 | LP_Base := nil;
551 | LU_rv := VirtualQuery(LP_Base, LR_Info, sizeof(LR_Info));
552 | LI_I := 1;
553 | while LU_rv = sizeof(LR_Info) do
554 | begin
555 | with sgVMDump do
556 | begin
557 | Cells[0, LI_I] := IntToHex(Integer(LR_Info.BaseAddress), 8);
558 | Cells[1, LI_I] := IntToStr(LR_Info.RegionSize);
559 | Cells[3, LI_I] := IntToHex(Integer(LR_Info.Protect), 8);
560 | case LR_Info.State of
561 |
562 | MEM_Commit:
563 | begin
564 | LU_MEM_COMMIT := LU_MEM_COMMIT + LR_Info.RegionSize;
565 | if (GetModuleFileName(dword(LR_Info.AllocationBase), LA_Char, MAX_PATH) <> 0) then
566 | begin
567 | if DWord(LR_Info.AllocationBase) = SysInit.HInstance then
568 | Cells[2, LI_I] := 'Exe'
569 | else
570 | Cells[2, LI_I] := 'DLL';
571 | Cells[4, LI_I] := ExtractFileName(LA_Char);
572 | end
573 | else
574 | begin
575 | Cells[4, LI_I] := '';
576 | Cells[2, LI_I] := 'Commited';
577 | end;
578 | end;
579 |
580 | MEM_RESERVE:
581 | begin
582 | LU_MEM_RESERVE := LU_MEM_RESERVE + LR_Info.RegionSize;
583 | Cells[2, LI_I] := 'Reserved';
584 | Cells[4, LI_I] := '';
585 | end;
586 |
587 | MEM_FREE:
588 | begin
589 | LP_FreeVMList.Add(Pointer(LR_Info.RegionSize));
590 | LU_MEM_FREE := LU_MEM_FREE + Lr_Info.RegionSize;
591 | Cells[2, LI_I] := 'Free';
592 | Cells[4, LI_I] := '';
593 | end;
594 | end;
595 |
596 | Inc(LP_Base, LR_Info.RegionSize);
597 | LU_rv := VirtualQuery(LP_Base, LR_Info, sizeof(LR_Info));
598 | Inc(LI_I);
599 | end;
600 | end;
601 |
602 | sgVMDump.RowCount := LI_I;
603 | end;
604 |
605 | procedure UpdateFastMMData;
606 | var
607 | LInd: Integer;
608 | LPrevSBState, LSBState: ^TSmallBlockTypeState;
609 |
610 | procedure UpdateBlockStatistics(c, r, current, prev: Integer);
611 | var
612 | s : string;
613 | begin
614 | s := IntToStr(current);
615 | if current > prev then
616 | s := s + ' (+' + IntToStr(current - prev) + ')'
617 | else if current < prev then
618 | s := s + ' (-' + IntToStr(prev - current) + ')';
619 | sgBlockStatistics.Cells[c, r] := s;
620 | sgBlockStatistics.Objects[c, r] := Pointer(current - prev);
621 | end;
622 |
623 | begin
624 | {Set up the row count}
625 | sgBlockStatistics.RowCount := FMemoryManagerState.SmallBlockTypeCount + 4;
626 | sgBlockStatistics.Cells[0, FMemoryManagerState.SmallBlockTypeCount + 1] := 'Medium Blocks';
627 | sgBlockStatistics.Cells[0, FMemoryManagerState.SmallBlockTypeCount + 2] := 'Large Blocks';
628 | sgBlockStatistics.Cells[0, FMemoryManagerState.SmallBlockTypeCount + 3] := 'Overall';
629 | for LInd := 0 to FMemoryManagerState.SmallBlockTypeCount - 1 do
630 | begin
631 | sgBlockStatistics.Cells[0, LInd + 1] :=
632 | IntToStr(FMemoryManagerState.SmallBlockTypeStates[LInd].InternalBlockSize)
633 | + '(' + IntToStr(FMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize) + ')';
634 | end;
635 | {Set the texts inside the results string grid}
636 | for LInd := 0 to FMemoryManagerState.SmallBlockTypeCount - 1 do
637 | begin
638 | LPrevSBState := @FPrevMemoryManagerState.SmallBlockTypeStates[LInd];
639 | LSBState := @FMemoryManagerState.SmallBlockTypeStates[LInd];
640 | UpdateBlockStatistics(1, LInd + 1, LSBState.AllocatedBlockCount, LPrevSBState.AllocatedBlockCount);
641 | Inc(LTotalBlocks, LSBState.AllocatedBlockCount);
642 | Inc(LPrevTotalBlocks, LPrevSBState.AllocatedBlockCount);
643 | LAllocatedSize := LSBState.AllocatedBlockCount * LSBState.UseableBlockSize;
644 | LPrevAllocatedSize := LPrevSBState.AllocatedBlockCount * LPrevSBState.UseableBlockSize;
645 | UpdateBlockStatistics(2, LInd + 1, LAllocatedSize, LPrevAllocatedSize);
646 | Inc(LTotalAllocated, LAllocatedSize);
647 | Inc(LPrevTotalAllocated, LPrevAllocatedSize);
648 | UpdateBlockStatistics(3, LInd + 1, LSBState.ReservedAddressSpace, LPrevSBState.ReservedAddressSpace);
649 | Inc(LTotalReserved, LSBState.ReservedAddressSpace);
650 | Inc(LPrevTotalReserved, LPrevSBState.ReservedAddressSpace);
651 | if LSBState.ReservedAddressSpace > 0 then
652 | sgBlockStatistics.Cells[4, LInd + 1] := FormatFloat('0.##%', LAllocatedSize / LSBState.ReservedAddressSpace * 100)
653 | else
654 | sgBlockStatistics.Cells[4, LInd + 1] := 'N/A';
655 | end;
656 | {-----------Medium blocks---------}
657 | LInd := FMemoryManagerState.SmallBlockTypeCount + 1;
658 | UpdateBlockStatistics(1, LInd, FMemoryManagerState.AllocatedMediumBlockCount, FPrevMemoryManagerState.AllocatedMediumBlockCount);
659 | Inc(LTotalBlocks, FMemoryManagerState.AllocatedMediumBlockCount);
660 | Inc(LPrevTotalBlocks, FPrevMemoryManagerState.AllocatedMediumBlockCount);
661 | UpdateBlockStatistics(2, LInd, FMemoryManagerState.TotalAllocatedMediumBlockSize, FPrevMemoryManagerState.TotalAllocatedMediumBlockSize);
662 | Inc(LTotalAllocated, FMemoryManagerState.TotalAllocatedMediumBlockSize);
663 | Inc(LPrevTotalAllocated, FPrevMemoryManagerState.TotalAllocatedMediumBlockSize);
664 | UpdateBlockStatistics(3, LInd, FMemoryManagerState.ReservedMediumBlockAddressSpace, FPrevMemoryManagerState.ReservedMediumBlockAddressSpace);
665 | Inc(LTotalReserved, FMemoryManagerState.ReservedMediumBlockAddressSpace);
666 | Inc(LPrevTotalReserved, FPrevMemoryManagerState.ReservedMediumBlockAddressSpace);
667 | if FMemoryManagerState.ReservedMediumBlockAddressSpace > 0 then
668 | sgBlockStatistics.Cells[4, LInd] := FormatFloat('0.##%', FMemoryManagerState.TotalAllocatedMediumBlockSize / FMemoryManagerState.ReservedMediumBlockAddressSpace * 100)
669 | else
670 | sgBlockStatistics.Cells[4, LInd] := 'N/A';
671 | {----------Large blocks----------}
672 | LInd := FMemoryManagerState.SmallBlockTypeCount + 2;
673 | UpdateBlockStatistics(1, LInd, FMemoryManagerState.AllocatedLargeBlockCount, FPrevMemoryManagerState.AllocatedLargeBlockCount);
674 | Inc(LTotalBlocks, FMemoryManagerState.AllocatedLargeBlockCount);
675 | Inc(LPrevTotalBlocks, FPrevMemoryManagerState.AllocatedLargeBlockCount);
676 | UpdateBlockStatistics(2, LInd, FMemoryManagerState.TotalAllocatedLargeBlockSize, FPrevMemoryManagerState.TotalAllocatedLargeBlockSize);
677 | Inc(LTotalAllocated, FMemoryManagerState.TotalAllocatedLargeBlockSize);
678 | Inc(LPrevTotalAllocated, FPrevMemoryManagerState.TotalAllocatedLargeBlockSize);
679 | UpdateBlockStatistics(3, LInd, FMemoryManagerState.ReservedLargeBlockAddressSpace, FPrevMemoryManagerState.ReservedLargeBlockAddressSpace);
680 | Inc(LTotalReserved, FMemoryManagerState.ReservedLargeBlockAddressSpace);
681 | Inc(LPrevTotalReserved, FPrevMemoryManagerState.ReservedLargeBlockAddressSpace);
682 | if FMemoryManagerState.ReservedLargeBlockAddressSpace > 0 then
683 | sgBlockStatistics.Cells[4, LInd] := FormatFloat('0.##%', FMemoryManagerState.TotalAllocatedLargeBlockSize / FMemoryManagerState.ReservedLargeBlockAddressSpace * 100)
684 | else
685 | sgBlockStatistics.Cells[4, LInd] := 'N/A';
686 | {-----------Overall--------------}
687 | LInd := FMemoryManagerState.SmallBlockTypeCount + 3;
688 | UpdateBlockStatistics(1, Lind, LTotalBlocks, LPrevTotalBlocks);
689 | UpdateBlockStatistics(2, Lind, LTotalAllocated, LPrevTotalAllocated);
690 | UpdateBlockStatistics(3, Lind, LTotalReserved, LPrevTotalReserved);
691 | if LTotalReserved > 0 then
692 | sgBlockStatistics.Cells[4, LInd] := FormatFloat('0.##%', LTotalAllocated / LTotalReserved * 100)
693 | else
694 | sgBlockStatistics.Cells[4, LInd] := 'N/A';
695 | end;
696 |
697 | procedure UpdateStatisticsData;
698 | const
699 | CI_MaxFreeBlocksList = 9;
700 |
701 | var
702 | LR_SystemInfo: TSystemInfo;
703 | LR_GlobalMemoryStatus: TMemoryStatus;
704 | LR_GlobalMemoryStatusEx: TMemoryStatusEx;
705 | LR_ProcessMemoryCounters: TProcessMemoryCounters;
706 | LR_SysBaseInfo: TSystem_Basic_Information;
707 | LU_MinQuota: {$if CompilerVersion >= 23}NativeUInt{$else}Cardinal{$ifend};
708 | LU_MaxQuota: {$if CompilerVersion >= 23}NativeUInt{$else}Cardinal{$ifend};
709 | LI_I: Integer;
710 | LI_Max: Integer;
711 | begin
712 | mVMStatistics.Lines.BeginUpdate;
713 | try
714 | mVMStatistics.Clear;
715 |
716 | LU_MinQuota := 0;
717 | LU_MaxQuota := 0;
718 |
719 | if Assigned(MP_GlobalMemoryStatusEx) then
720 | begin
721 | ZeroMemory(@LR_GlobalMemoryStatusEx, SizeOf(TMemoryStatusEx));
722 | LR_GlobalMemoryStatusEx.dwLength := SizeOf(TMemoryStatusEx);
723 |
724 | if not MP_GlobalMemoryStatusEx(LR_GlobalMemoryStatusEx) then
725 | begin
726 | mVMStatistics.Lines.Add('GlobalMemoryStatusEx err: ' + SysErrorMessage(GetLastError));
727 | end;
728 | end
729 | else
730 | begin
731 | LR_GlobalMemoryStatus.dwLength := SizeOf(TMemoryStatus);
732 | GlobalMemoryStatus(LR_GlobalMemoryStatus);
733 | end;
734 |
735 | LP_FreeVMList.SortList(LocSort);
736 |
737 | GetProcessWorkingSetSize(GetCurrentProcess, LU_MinQuota, LU_MaxQuota);
738 | GetSystemInfo(LR_SystemInfo);
739 |
740 | with mVMStatistics.Lines do
741 | begin
742 | Add('System Info:');
743 | Add('------------');
744 |
745 | Add('Processor Count = ' + IntToStr(LR_SystemInfo.dwNumberOfProcessors));
746 | Add('Allocation Granularity = ' + IntToStr(LR_SystemInfo.dwAllocationGranularity));
747 |
748 | if Assigned(MP_GlobalMemoryStatusEx) then
749 | begin
750 | with LR_GlobalMemoryStatusEx do
751 | begin
752 | Add('Available Physical Memory = ' + Int64ToKStringFormatted(ullAvailPhys));
753 | Add('Total Physical Memory = ' + Int64ToKStringFormatted(ullTotalPhys));
754 | Add('Available Virtual Memory = ' + Int64ToKStringFormatted(ullAvailVirtual));
755 | Add('Total Virtual Memory = ' + Int64ToKStringFormatted(ullTotalVirtual));
756 | Add('Total Virtual Extended Memory = ' + Int64ToKStringFormatted(ullAvailExtendedVirtual));
757 | end;
758 | end
759 |
760 | else
761 | begin
762 | with LR_GlobalMemoryStatus do
763 | begin
764 | Add('Available Physical Memory = ' + Int64ToKStringFormatted(dwAvailPhys));
765 | Add('Total Physical Memory = ' + Int64ToKStringFormatted(dwTotalPhys));
766 | Add('Available Virtual Memory = ' + Int64ToKStringFormatted(dwAvailVirtual));
767 | Add('Total Virtual Memory = ' + Int64ToKStringFormatted(dwTotalVirtual));
768 | end;
769 | end;
770 |
771 | if Assigned(MP_NtQuerySystemInformation) then
772 | begin
773 | if MP_NtQuerySystemInformation(SystemBasicInformation, @LR_SysBaseInfo, SizeOf(LR_SysBaseInfo), nil) = 0 then
774 | begin
775 | with LR_SysBaseInfo do begin
776 | Add('Maximum Increment = ' + CardinalToKStringFormatted(uKeMaximumIncrement));
777 | Add('Page Size = ' + CardinalToKStringFormatted(uPageSize));
778 | Add('Number of Physical Pages = ' + CardinalToKStringFormatted(uMmNumberOfPhysicalPages));
779 | Add('Lowest Physical Page = ' + CardinalToStringFormatted(uMmLowestPhysicalPage));
780 | Add('Highest Physical Page = ' + CardinalToKStringFormatted(uMmHighestPhysicalPage));
781 | end;
782 | end;
783 | end;
784 |
785 | // same as GetProcessMemoryInfo & NtQuerySystemInformation (SystemBasicInformation
786 |
787 | // The working set is the amount of memory physically mapped to the process context at a given
788 | // time. Memory in the paged pool is system memory that can be transferred to the paging file
789 | // on disk (paged) when it is not being used. Memory in the nonpaged pool is system memory
790 | // that cannot be paged to disk as long as the corresponding objects are allocated. The pagefile
791 | // usage represents how much memory is set aside for the process in the system paging file.
792 | // When memory usage is too high, the virtual memory manager pages selected memory to disk.
793 | // When a thread needs a page that is not in memory, the memory manager reloads it from the
794 | // paging file.
795 |
796 |
797 | if GetProcessMemoryInfo(GetCurrentProcess, @LR_ProcessMemoryCounters, SizeOf(LR_ProcessMemoryCounters)) then
798 | begin
799 | with LR_ProcessMemoryCounters do
800 | begin
801 | Add('Page Fault Count = ' + CardinalToKStringFormatted(PageFaultCount));
802 | Add('Peak Working Set Size = ' + Int64ToKStringFormatted(PeakWorkingSetSize));
803 | Add('Working Set Size = ' + Int64ToKStringFormatted(WorkingSetSize));
804 | Add('Quota Peak Paged Pool Usage = ' + Int64ToKStringFormatted(QuotaPeakPagedPoolUsage));
805 | Add('Quota Paged Pool Usage = ' + Int64ToKStringFormatted(QuotaPagedPoolUsage));
806 | Add('Quota Peak Non-Paged Pool Usage = ' + Int64ToKStringFormatted(QuotaPeakNonPagedPoolUsage));
807 | Add('Quota Non-Paged Pool Usage = ' + Int64ToKStringFormatted(QuotaNonPagedPoolUsage));
808 | Add('Pagefile Usage = ' + Int64ToKStringFormatted(PagefileUsage));
809 | Add('Peak Pagefile Usage = ' + Int64ToKStringFormatted(PeakPagefileUsage));
810 | end;
811 | end;
812 |
813 | Add('');
814 | Add('Process Info: PID (' + IntToStr(GetCurrentProcessId) + ')');
815 | Add('------------------------');
816 | Add('Minimum Address = ' + Int64ToKStringFormatted(NativeUInt(LR_SystemInfo.lpMinimumApplicationAddress)));
817 | Add('Maximum VM Address = ' + Int64ToKStringFormatted(NativeUInt(LR_SystemInfo.lpMaximumApplicationAddress)));
818 | Add('Page Protection & Commit Size = ' + IntToStr(LR_SystemInfo.dWPageSize));
819 | Add('');
820 | Add('Quota info:');
821 | Add('-----------');
822 | Add('Minimum Quota = ' + Int64ToKStringFormatted(LU_MinQuota));
823 | Add('Maximum Quota = ' + Int64ToKStringFormatted(LU_MaxQuota));
824 | Add('');
825 | Add('VM Info:');
826 | Add('--------');
827 | Add('Total Free = ' + Int64ToKStringFormatted(LU_MEM_FREE));
828 | Add('Total Reserve = ' + Int64ToKStringFormatted(LU_MEM_RESERVE));
829 | Add('Total Commit = ' + Int64ToKStringFormatted(LU_MEM_COMMIT));
830 |
831 | if LP_FreeVMList.Count > CI_MaxFreeBlocksList then
832 | LI_Max := CI_MaxFreeBlocksList - 1
833 | else
834 | LI_Max := LP_FreeVMList.Count - 1;
835 |
836 | for LI_I := 0 to LI_Max do
837 | begin
838 | Add('Largest Free Block ' + IntToStr(LI_I + 1) + '. = ' + Int64ToKStringFormatted(NativeUInt(LP_FreeVMList.List[LI_I])));
839 | end;
840 |
841 | Add('');
842 | Add('FastMM Info:');
843 | Add('-------------');
844 | Add('Total Blocks = ' + Int64ToKStringFormatted(LTotalBlocks));
845 | Add('Total Allocated = ' + Int64ToKStringFormatted(LTotalAllocated));
846 | Add('Total Reserved = ' + Int64ToKStringFormatted(LTotalReserved));
847 | end;
848 |
849 | finally
850 | mVMStatistics.Lines.EndUpdate;
851 | end;
852 | end;
853 |
854 | var
855 | Save_Cursor: TCursor;
856 | begin
857 | if SizeOf(TMemoryMap) <> SizeOf(TMemoryMapEx) then
858 | begin
859 | Showmessage('Internal implementation error');
860 | Exit;
861 | end;
862 |
863 | LU_MEM_FREE := 0;
864 | LU_MEM_COMMIT := 0;
865 | LU_MEM_RESERVE := 0;
866 |
867 | LTotalBlocks := 0;
868 | LTotalAllocated := 0;
869 | LTotalReserved := 0;
870 |
871 | LPrevTotalBlocks := 0;
872 | LPrevTotalAllocated := 0;
873 | LPrevTotalReserved := 0;
874 |
875 | // Set hourglass cursor
876 | Save_Cursor := Screen.Cursor;
877 | Screen.Cursor := crHourGlass;
878 | LP_FreeVMList := TList.Create;
879 | try
880 | // retrieve FastMM info
881 |
882 | FastMM_GetMemoryManagerState(FMemoryManagerState);
883 | FastMM_GetMemoryMap(TMemoryMap(FMemoryMapEx));
884 |
885 | // Update FastMM Graph with EXE & DLL locations
886 | UpdateVMGraph(FMemoryMapEx);
887 |
888 | // VM dump
889 | UpdateVMDump;
890 |
891 | // FastMM data
892 | UpdateFastMMData;
893 |
894 | // General Information
895 | UpdateStatisticsData;
896 |
897 | // Screen updates
898 | dgMemoryMap.Invalidate;
899 |
900 | FPrevMemoryManagerState := FMemoryManagerState;
901 | finally
902 | FreeAndNil(LP_FreeVMList);
903 | Screen.Cursor := Save_Cursor;
904 | end;
905 | end;
906 |
907 | procedure TfFastMMUsageTracker.sgBlockStatisticsDrawCell(Sender: TObject;
908 | ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
909 | var
910 | d: integer;
911 | y: integer;
912 | s: string;
913 | LOldColour, LColour: TColor;
914 | begin
915 | d := Integer(sgBlockStatistics.Objects[ACol, ARow]);
916 | if d <> 0 then
917 | begin
918 | LOldColour := sgBlockStatistics.Canvas.Brush.Color;
919 | if d < 0 then
920 | LColour := clLime
921 | else
922 | LColour := clRed;
923 | sgBlockStatistics.Canvas.Brush.Color := LColour;
924 | sgBlockStatistics.Canvas.Font.Color := clWindowText;
925 | s := sgBlockStatistics.Cells[ACol, ARow];
926 | y := sgBlockStatistics.Canvas.TextHeight(s);
927 | y := ((Rect.Bottom - Rect.Top) - y) div 2;
928 | sgBlockStatistics.Canvas.TextRect(Rect, Rect.Left + 2, Rect.top + y, s);
929 | sgBlockStatistics.Canvas.Brush.Color := LOldColour;
930 | end;
931 | end;
932 |
933 | procedure TfFastMMUsageTracker.tTimerTimer(Sender: TObject);
934 | begin
935 | tTimer.Enabled := False;
936 | try
937 | RefreshSnapShot;
938 | finally
939 | tTimer.Enabled := True;
940 | end;
941 | end;
942 |
943 | procedure TfFastMMUsageTracker.bCloseClick(Sender: TObject);
944 | begin
945 | Close;
946 | end;
947 |
948 | procedure TfFastMMUsageTracker.dgMemoryMapDrawCell(Sender: TObject; ACol,
949 | ARow: Integer; Rect: TRect; State: TGridDrawState);
950 | var
951 | LChunkIndex: integer;
952 | LChunkColour: TColor;
953 | begin
954 | {Get the chunk index}
955 | LChunkIndex := ARow * dgMemoryMap.ColCount + ACol;
956 |
957 | {Get the correct colour}
958 | case FMemoryMapEx[LChunkIndex] of
959 |
960 | csExAllocated:
961 | begin
962 | LChunkColour := $9090FF;
963 | end;
964 |
965 | csExReserved:
966 | begin
967 | LChunkColour := $90F090;
968 | end;
969 |
970 | csExSysAllocated:
971 | begin
972 | LChunkColour := $707070;
973 | end;
974 |
975 | csExSysExe:
976 | begin
977 | LChunkColour := clRed;
978 | end;
979 |
980 | csExSysDLL:
981 | begin
982 | LChunkColour := clBlue;
983 | end;
984 |
985 | csExSysReserved:
986 | begin
987 | LChunkColour := $C0C0C0;
988 | end
989 |
990 | else
991 | begin
992 | {ExUnallocated}
993 | LChunkColour := $FFFFFF;
994 | end;
995 | end;
996 |
997 | {Draw the chunk background}
998 | dgMemoryMap.Canvas.Brush.Color := LChunkColour;
999 |
1000 | if State = [] then
1001 | dgMemoryMap.Canvas.FillRect(Rect)
1002 | else
1003 | dgMemoryMap.Canvas.Rectangle(Rect);
1004 | end;
1005 |
1006 | procedure TfFastMMUsageTracker.dgMemoryMapSelectCell(Sender: TObject; ACol,
1007 | ARow: Integer; var CanSelect: Boolean);
1008 | var
1009 | LChunkIndex: Cardinal;
1010 | LMBI: TMemoryBasicInformation;
1011 | LA_Char: array[0..MAX_PATH] of char;
1012 | begin
1013 | eDLLName.Text := '';
1014 | LChunkIndex := ARow * dgMemoryMap.ColCount + ACol;
1015 | eAddress.Text := Format('$%0.8x', [LChunkIndex shl 16]);
1016 |
1017 | case FMemoryMapEx[LChunkIndex] of
1018 |
1019 | csExAllocated:
1020 | begin
1021 | eState.Text := 'FastMM Allocated';
1022 | end;
1023 |
1024 | csExReserved:
1025 | begin
1026 | eState.Text := 'FastMM Reserved';
1027 | end;
1028 |
1029 | csExSysAllocated:
1030 | begin
1031 | eState.Text := 'System Allocated';
1032 | end;
1033 |
1034 | csExSysExe:
1035 | begin
1036 | eState.Text := 'System Exe';
1037 | VirtualQuery(Pointer(LChunkIndex shl 16), LMBI, SizeOf(LMBI));
1038 | if (GetModuleFileName(dword(LMBI.AllocationBase), LA_Char, MAX_PATH) <> 0) then
1039 | begin
1040 | eDLLName.Text := LA_Char;
1041 | end;
1042 | end;
1043 |
1044 | csExSysDLL:
1045 | begin
1046 | eState.Text := 'System/User DLL';
1047 | VirtualQuery(Pointer(LChunkIndex shl 16), LMBI, SizeOf(LMBI));
1048 | if (GetModuleFileName(dword(LMBI.AllocationBase), LA_Char, MAX_PATH) <> 0) then
1049 | begin
1050 | eDLLName.Text := LA_Char;
1051 | end;
1052 | end;
1053 |
1054 | csExSysReserved:
1055 | begin
1056 | eState.Text := 'System Reserved';
1057 | end
1058 |
1059 | else
1060 | begin
1061 | {ExUnallocated}
1062 | eState.Text := 'Free';
1063 | end;
1064 | end;
1065 | end;
1066 |
1067 | procedure TfFastMMUsageTracker.bUpdateClick(Sender: TObject);
1068 | begin
1069 | RefreshSnapShot;
1070 | end;
1071 |
1072 | procedure TfFastMMUsageTracker.ChkAutoUpdateClick(Sender: TObject);
1073 | begin
1074 | tTimer.Enabled := ChkAutoUpdate.Checked;
1075 | end;
1076 |
1077 | procedure TfFastMMUsageTracker.ChkSmallGraphClick(Sender: TObject);
1078 | begin
1079 | UpdateGraphMetrics;
1080 | dgMemoryMap.Invalidate;
1081 | dgMemoryMap.SetFocus;
1082 | end;
1083 |
1084 | procedure TfFastMMUsageTracker.sgVMDumpMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
1085 | begin
1086 | if (Button = mbLeft) and (Shift = [ssLeft]) then
1087 | begin
1088 | (Sender as TStringgrid).MouseToCell(X, Y, OR_VMDumpDownCell.X, OR_VMDumpDownCell.Y);
1089 | end
1090 | else
1091 | begin
1092 | OR_VMDumpDownCell.X := 0;
1093 | OR_VMDumpDownCell.Y := 0;
1094 | end;
1095 | end;
1096 |
1097 | procedure TfFastMMUsageTracker.sgVMDumpMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
1098 | var
1099 | p: TGridCoord;
1100 | LGrid: TStringgrid;
1101 | begin
1102 | LGrid := Sender as TStringGrid;
1103 | if (Button = mbLeft) and (Shift = []) then
1104 | begin
1105 | LGrid.MouseToCell(X, Y, p.X, p.Y);
1106 | if CompareMem(@p, @OR_VMDumpDownCell, sizeof(p))
1107 | and (p.Y < LGrid.FixedRows)
1108 | and (p.X >= LGrid.FixedCols) then
1109 | begin
1110 | HeaderClicked(LGrid, p);
1111 | end;
1112 | end;
1113 | OR_VMDumpDownCell.X := 0;
1114 | OR_VMDumpDownCell.Y := 0;
1115 | end;
1116 |
1117 | procedure TfFastMMUsageTracker.sgVMDumpDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
1118 | var
1119 | LGrid: TStringgrid;
1120 | LMarker: Char;
1121 | begin
1122 | LGrid := Sender as TStringgrid;
1123 | // paint the sort marker on header columns
1124 | if (ACol >= LGrid.FixedCols) and (aRow = 0) then
1125 | begin
1126 | if Assigned(LGrid.Objects[aCol, aRow]) then
1127 | begin
1128 | if Integer(LGrid.Objects[aCol, aRow]) > 0 then
1129 | LMarker := 't' // up wedge in Marlett font
1130 | else
1131 | LMarker := 'u'; // down wedge in Marlett font
1132 | with LGrid.canvas do
1133 | begin
1134 | Font.Name := 'Marlett';
1135 | Font.Charset := SYMBOL_CHARSET;
1136 | Font.Size := 12;
1137 | TextOut(Rect.Right - TextWidth(LMarker), Rect.Top, LMarker);
1138 | Font := LGrid.font;
1139 | end;
1140 | end;
1141 | end;
1142 | end;
1143 |
1144 | procedure TfFastMMUsageTracker.siMM4AllocationCopyAlltoClipboardClick(Sender: TObject);
1145 | begin
1146 | CopyGridContentsToClipBoard(sgBlockStatistics);
1147 | end;
1148 |
1149 | procedure TfFastMMUsageTracker.miVMDumpCopyAlltoClipboardClick(Sender: TObject);
1150 | begin
1151 | CopyGridContentsToClipBoard(sgVMDump);
1152 | end;
1153 |
1154 | procedure TfFastMMUsageTracker.miGeneralInformationCopyAlltoClipboardClick(Sender: TObject);
1155 | begin
1156 | with mVMStatistics do
1157 | begin
1158 | Lines.BeginUpdate;
1159 | try
1160 | SelectAll;
1161 | CopyToClipboard;
1162 | SelStart := 0;
1163 | finally
1164 | Lines.EndUpdate;
1165 | end;
1166 | end;
1167 | end;
1168 |
1169 | procedure ModuleInit;
1170 | begin
1171 | if Win32Platform = VER_PLATFORM_WIN32_NT then
1172 | begin
1173 | MP_GlobalMemoryStatusEx := TP_GlobalMemoryStatusEx(
1174 | GetProcAddress(GetModuleHandle(kernel32), 'GlobalMemoryStatusEx'));
1175 | MP_NtQuerySystemInformation := TP_NtQuerySystemInformation(
1176 | GetProcAddress(GetModuleHandle('ntdll.dll'), 'NtQuerySystemInformation'));
1177 | end;
1178 | end;
1179 |
1180 | initialization
1181 | ModuleInit;
1182 |
1183 | end.
1184 |
--------------------------------------------------------------------------------
/Demos/Usage Tracker/UsageTrackerDemo.dpr:
--------------------------------------------------------------------------------
1 | program UsageTrackerDemo;
2 |
3 | uses
4 | FastMM5,
5 | Forms,
6 | DemoForm in 'DemoForm.pas' {fDemo};
7 |
8 | {$R *.res}
9 |
10 | {Enable large address space support for this demo}
11 | {$SetPEFlags $20}
12 |
13 | begin
14 | Application.Initialize;
15 | Application.CreateForm(TfDemo, fDemo);
16 | Application.Run;
17 | end.
18 |
--------------------------------------------------------------------------------
/Demos/Usage Tracker/UsageTrackerDemo.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {2d29cca4-0633-47dd-b826-c21a24d53d83}
4 | UsageTrackerDemo.dpr
5 | Debug
6 | DCC32
7 | UsageTrackerDemo.exe
8 | VCL
9 | 19.5
10 | True
11 | Debug
12 | Win32
13 | 3
14 | Application
15 |
16 |
17 | true
18 |
19 |
20 | true
21 | Base
22 | true
23 |
24 |
25 | true
26 | Base
27 | true
28 |
29 |
30 | true
31 | Base
32 | true
33 |
34 |
35 | true
36 | Cfg_1
37 | true
38 | true
39 |
40 |
41 | true
42 | Cfg_1
43 | true
44 | true
45 |
46 |
47 | true
48 | Base
49 | true
50 |
51 |
52 | true
53 | Cfg_2
54 | true
55 | true
56 |
57 |
58 | true
59 | Cfg_2
60 | true
61 | true
62 |
63 |
64 | UsageTrackerDemo
65 | Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace)
66 | 7177
67 | CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=
68 |
69 |
70 | System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
71 | Debug
72 | true
73 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
74 | 1033
75 | $(BDS)\bin\default_app.manifest
76 | $(BDS)\bin\delphi_PROJECTICON.ico
77 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
78 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
79 | none
80 |
81 |
82 | $(BDS)\bin\delphi_PROJECTICON.ico
83 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
84 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
85 | System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
86 | Debug
87 | true
88 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
89 | 1033
90 | $(BDS)\bin\default_app.manifest
91 | none
92 |
93 |
94 | 7.0
95 | 0
96 | False
97 | 0
98 | RELEASE;$(DCC_Define)
99 | ..\..;$(DCC_UnitSearchPath)
100 |
101 |
102 | PerMonitorV2
103 |
104 |
105 | PerMonitorV2
106 |
107 |
108 | 7.0
109 | DEBUG;$(DCC_Define)
110 | ..\..;$(DCC_UnitSearchPath)
111 |
112 |
113 | PerMonitorV2
114 |
115 |
116 | PerMonitorV2
117 | true
118 | 1033
119 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
120 |
121 |
122 | Delphi.Personality.12
123 | VCLApplication
124 |
125 |
126 |
127 | False
128 | True
129 | False
130 |
131 |
132 | False
133 | False
134 | 1
135 | 0
136 | 0
137 | 0
138 | False
139 | False
140 | False
141 | False
142 | False
143 | 7177
144 | 1252
145 |
146 |
147 |
148 |
149 | 1.0.0.0
150 |
151 |
152 |
153 |
154 |
155 | 1.0.0.0
156 |
157 |
158 |
159 | Microsoft Office 2000 Sample Automation Server Wrapper Components
160 | Microsoft Office XP Sample Automation Server Wrapper Components
161 |
162 |
163 | UsageTrackerDemo.dpr
164 |
165 |
166 |
167 | True
168 | True
169 |
170 |
171 | 12
172 |
173 |
174 |
175 |
176 | MainSource
177 |
178 |
179 |
180 |
181 |
182 | Base
183 |
184 |
185 | Cfg_1
186 | Base
187 |
188 |
189 | Cfg_2
190 | Base
191 |
192 |
193 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/FullDebugMode DLL/FastMM_FullDebugMode.dpr:
--------------------------------------------------------------------------------
1 | {
2 |
3 | Fast Memory Manager: FullDebugMode Support DLL 1.64
4 |
5 | Description:
6 | Support DLL for FastMM. With this DLL available, FastMM will report debug info (unit name, line numbers, etc.) for
7 | stack traces.
8 |
9 | Usage:
10 | 1) To compile you will need the JCL library (http://sourceforge.net/projects/jcl/)
11 | 2) Place in the same location as the replacement borlndmm.dll or your application's executable module.
12 |
13 | Change log:
14 | Version 1.00 (9 July 2005):
15 | - Initial release.
16 | Version 1.01 (13 July 2005):
17 | - Added the option to use madExcept instead of the JCL Debug library. (Thanks to Martin Aignesberger.)
18 | Version 1.02 (30 September 2005):
19 | - Changed options to display detail for addresses inside libraries as well.
20 | Version 1.03 (13 October 2005):
21 | - Added a raw stack trace procedure that implements raw stack traces.
22 | Version 1.10 (14 October 2005):
23 | - Improved the program logic behind the skipping of stack levels to cause less incorrect entries in raw stack traces.
24 | (Thanks to Craig Peterson.)
25 | Version 1.20 (17 October 2005):
26 | - Improved support for madExcept stack traces. (Thanks to Mathias Rauen.)
27 | Version 1.30 (26 October 2005):
28 | - Changed name to FastMM_FullDebugMode to reflect the fact that there is now a static dependency on this DLL for
29 | FullDebugMode. The static dependency solves a DLL unload order issue. (Thanks to Bart van der Werf.)
30 | Version 1.40 (31 October 2005):
31 | - Added support for EurekaLog. (Thanks to Fabio Dell'Aria.)
32 | Version 1.42 (23 June 2006):
33 | - Fixed a bug in the RawStackTraces code that may have caused an A/V in some rare circumstances. (Thanks to Primoz
34 | Gabrijelcic.)
35 | Version 1.44 (16 November 2006):
36 | - Changed the RawStackTraces code to prevent it from modifying the Windows "GetLastError" error code. (Thanks to
37 | Primoz Gabrijelcic.)
38 | Version 1.50 (14 August 2008):
39 | - Added support for Delphi 2009. (Thanks to Mark Edington.)
40 | Version 1.60 (5 May 2009):
41 | - Improved the code used to identify call instructions in the stack trace code. (Thanks to the JCL team.)
42 | Version 1.61 (5 September 2010):
43 | - Recompiled using the latest JCL in order to fix a possible crash on shutdown when the executable contains no debug
44 | information. (Thanks to Hanspeter Widmer.)
45 | Version 1.62 (19 July 2012):
46 | - Added a workaround for QC 107209 (Thanks to David Heffernan.)
47 | Version 1.63 (14 September 2013):
48 | - Added support for OSX (Thanks to Sebastian Zierer)
49 | Version 1.64 (27 February 2021)
50 | - Implemented a return address information cache that greatly speeds up the conversion of many similar stack traces
51 | to text.
52 | Version 1.65 (10 July 2023)
53 | - Made LogStackTrace thread safe.
54 |
55 | }
56 |
57 | {$IFDEF MSWINDOWS}
58 | {--------------------Start of options block-------------------------}
59 |
60 | {Select the stack tracing library to use. The JCL, madExcept and EurekaLog are supported. Only one can be used at a
61 | time.}
62 | {$define JCLDebug}
63 | {.$define madExcept}
64 | {.$define EurekaLog_Legacy}
65 | {.$define EurekaLog_V7}
66 |
67 | {--------------------End of options block-------------------------}
68 | {$ENDIF}
69 |
70 | // JCL_DEBUG_EXPERT_INSERTJDBG OFF
71 | library FastMM_FullDebugMode;
72 |
73 | uses
74 | {$ifdef JCLDebug}JCLDebug,{$endif}
75 | {$ifdef madExcept}madStackTrace,{$endif}
76 | {$ifdef EurekaLog_Legacy}ExceptionLog,{$endif}
77 | {$ifdef EurekaLog_V7}EFastMM4Support,{$endif}
78 | System.SysUtils, {$IFDEF MACOS}Posix.Base, SBMapFiles {$ELSE} Winapi.Windows {$ENDIF};
79 |
80 | {$R *.res}
81 |
82 | {$StackFrames on}
83 | {$warn Symbol_Platform off}
84 |
85 | {The name of the 64-bit DLL has a '64' at the end.}
86 | {$if SizeOf(Pointer) = 8}
87 | {$LIBSUFFIX '64'}
88 | {$ifend}
89 |
90 | {--------------------------Return Address Info Cache --------------------------}
91 |
92 | const
93 | CReturnAddressCacheSize = 4096;
94 | {FastMM assumes a maximum of 256 characters per stack trace entry. The address text and line break are in addition to
95 | the info text.}
96 | CMaxInfoTextLength = 224;
97 |
98 | type
99 | {Return address info cache: Maintains the source information for up to CReturnAddressCacheSize return addresses in
100 | a binary search tree.}
101 |
102 | PReturnAddressInfo = ^TReturnAddressInfo;
103 | TReturnAddressInfo = record
104 | ParentEntry: PReturnAddressInfo;
105 | ChildEntries: array[0..1] of PReturnAddressInfo;
106 | ReturnAddress: NativeUInt;
107 | InfoTextLength: Integer;
108 | InfoText: array[0..CMaxInfoTextLength - 1] of AnsiChar;
109 | end;
110 |
111 | TReturnAddressInfoCache = record
112 | {Entry 0 is the root of the tree.}
113 | Entries: array[0..CReturnAddressCacheSize] of TReturnAddressInfo;
114 | NextNewEntryIndex: Integer;
115 | function AddEntry(AReturnAddress: NativeUInt; const AReturnAddressInfoText: AnsiString): PReturnAddressInfo;
116 | procedure DeleteEntry(AEntry: PReturnAddressInfo);
117 | function FindEntry(AReturnAddress: NativeUInt): PReturnAddressInfo;
118 | end;
119 |
120 | function TReturnAddressInfoCache.AddEntry(AReturnAddress: NativeUInt; const AReturnAddressInfoText: AnsiString): PReturnAddressInfo;
121 | var
122 | LParentItem, LChildItem: PReturnAddressInfo;
123 | LAddressBits: NativeUInt;
124 | LChildIndex: Integer;
125 | begin
126 | {Get the address of the entry to reuse. (Entry 0 is the tree root.)}
127 | if NextNewEntryIndex = High(Entries) then
128 | NextNewEntryIndex := 0;
129 | Inc(NextNewEntryIndex);
130 |
131 | Result := @Entries[NextNewEntryIndex];
132 |
133 | {Delete it if it is already in use}
134 | DeleteEntry(Result);
135 |
136 | {Step down the tree until an open slot is found in the required direction.}
137 | LParentItem := @Entries[0];
138 | LAddressBits := AReturnAddress;
139 | while True do
140 | begin
141 | {Get the current child in the appropriate direction.}
142 | LChildItem := LParentItem.ChildEntries[LAddressBits and 1];
143 | {No child -> This slot is available.}
144 | if LChildItem = nil then
145 | Break;
146 | {Traverse further down the tree.}
147 | LParentItem := LChildItem;
148 | LAddressBits := LAddressBits shr 1;
149 | end;
150 | LChildIndex := LAddressBits and 1;
151 |
152 | {Insert the node into the tree}
153 | LParentItem.ChildEntries[LChildIndex] := Result;
154 | Result.ParentEntry := LParentItem;
155 |
156 | {Set the info text for the item.}
157 | Result.ReturnAddress := AReturnAddress;
158 | Result.InfoTextLength := Length(AReturnAddressInfoText);
159 | if Result.InfoTextLength > CMaxInfoTextLength then
160 | Result.InfoTextLength := CMaxInfoTextLength;
161 | System.Move(Pointer(AReturnAddressInfoText)^, Result.InfoText, Result.InfoTextLength * SizeOf(AnsiChar));
162 | end;
163 |
164 | procedure TReturnAddressInfoCache.DeleteEntry(AEntry: PReturnAddressInfo);
165 | var
166 | LRemovedItemChildIndex, LMovedItemChildIndex: Integer;
167 | LMovedItem, LChildItem: PReturnAddressInfo;
168 | begin
169 | {Is this entry currentlty in the tree?}
170 | if AEntry.ParentEntry = nil then
171 | Exit;
172 |
173 | LRemovedItemChildIndex := Ord(AEntry.ParentEntry.ChildEntries[1] = AEntry);
174 |
175 | {Does this item have children of its own?}
176 | if (NativeInt(AEntry.ChildEntries[0]) or NativeInt(AEntry.ChildEntries[1])) <> 0 then
177 | begin
178 | {It has children: We need to traverse child items until we find a leaf item and then move it into this item's
179 | position in the search tree.}
180 | LMovedItem := AEntry;
181 |
182 | while True do
183 | begin
184 | LChildItem := LMovedItem.ChildEntries[0]; //try left then right
185 | if LChildItem = nil then
186 | begin
187 | LChildItem := LMovedItem.ChildEntries[1];
188 | if LChildItem = nil then
189 | Break;
190 | end;
191 | LMovedItem := LChildItem;
192 | end;
193 |
194 | {Disconnect the moved item from its current parent item.}
195 | LMovedItemChildIndex := Ord(LMovedItem.ParentEntry.ChildEntries[1] = LMovedItem);
196 | LMovedItem.ParentEntry.ChildEntries[LMovedItemChildIndex] := nil;
197 |
198 | {Set the new parent for the moved item}
199 | AEntry.ParentEntry.ChildEntries[LRemovedItemChildIndex] := LMovedItem;
200 | LMovedItem.ParentEntry := AEntry.ParentEntry;
201 |
202 | {Set the new left child for the moved item}
203 | LChildItem := AEntry.ChildEntries[0];
204 | if LChildItem <> nil then
205 | begin
206 | LMovedItem.ChildEntries[0] := LChildItem;
207 | LChildItem.ParentEntry := LMovedItem;
208 | AEntry.ChildEntries[0] := nil;
209 | end;
210 |
211 | {Set the new right child for the moved item}
212 | LChildItem := AEntry.ChildEntries[1];
213 | if LChildItem <> nil then
214 | begin
215 | LMovedItem.ChildEntries[1] := LChildItem;
216 | LChildItem.ParentEntry := LMovedItem;
217 | AEntry.ChildEntries[1] := nil;
218 | end;
219 |
220 | end
221 | else
222 | begin
223 | {The deleted item is a leaf item: Remove it from the tree directly.}
224 | AEntry.ParentEntry.ChildEntries[LRemovedItemChildIndex] := nil;
225 | end;
226 | {Reset the parent for the removed item.}
227 | AEntry.ParentEntry := nil;
228 | end;
229 |
230 | function TReturnAddressInfoCache.FindEntry(AReturnAddress: NativeUInt): PReturnAddressInfo;
231 | var
232 | LAddressBits: NativeUInt;
233 | LParentItem: PReturnAddressInfo;
234 | begin
235 | LAddressBits := AReturnAddress;
236 | LParentItem := @Entries[0];
237 | {Step down the tree until the item is found or there is no child item in the required direction.}
238 | while True do
239 | begin
240 | {Get the child item in the required direction.}
241 | Result := LParentItem.ChildEntries[LAddressBits and 1];
242 | {If there is no child, or the child's key value matches the search key value then we're done.}
243 | if (Result = nil)
244 | or (Result.ReturnAddress = AReturnAddress) then
245 | begin
246 | Exit;
247 | end;
248 | {The child key value is not a match -> Move down the tree.}
249 | LParentItem := Result;
250 | LAddressBits := LAddressBits shr 1;
251 | end;
252 | end;
253 |
254 | {--------------------------Stack Tracing Subroutines--------------------------}
255 |
256 | procedure GetStackRange(var AStackBaseAddress, ACurrentStackPointer: NativeUInt);
257 | asm
258 | {$if SizeOf(Pointer) = 8}
259 | mov rax, gs:[abs 8]
260 | mov [rcx], rax
261 | mov [rdx], rbp
262 | {$else}
263 | mov ecx, fs:[4]
264 | mov [eax], ecx
265 | mov [edx], ebp
266 | {$ifend}
267 | end;
268 |
269 | {--------------------------Frame Based Stack Tracing--------------------------}
270 |
271 | {$if SizeOf(Pointer) = 8}
272 |
273 | function CaptureStackBackTrace(FramesToSkip, FramesToCapture: DWORD;
274 | BackTrace: Pointer; BackTraceHash: PDWORD): Word;
275 | external kernel32 name 'RtlCaptureStackBackTrace';
276 |
277 | {We use the Windows API to do frame based stack tracing under 64-bit.}
278 | procedure GetFrameBasedStackTrace(AReturnAddresses: PNativeUInt;
279 | AMaxDepth, ASkipFrames: Cardinal);
280 | begin
281 | CaptureStackBackTrace(ASkipFrames, AMaxDepth, AReturnAddresses, nil);
282 | end;
283 |
284 | {$else}
285 |
286 | {Dumps the call stack trace to the given address. Fills the list with the addresses where the called addresses can be
287 | found. This is the fast stack frame based tracing routine.}
288 | procedure GetFrameBasedStackTrace(AReturnAddresses: PNativeUInt; AMaxDepth, ASkipFrames: Cardinal);
289 | var
290 | LStackTop, LStackBottom, LCurrentFrame: NativeUInt;
291 | begin
292 | {Get the call stack top and current bottom}
293 | GetStackRange(LStackTop, LStackBottom);
294 | Dec(LStackTop, SizeOf(Pointer) - 1);
295 | {Get the current frame start}
296 | LCurrentFrame := LStackBottom;
297 | {Fill the call stack}
298 | while (AMaxDepth > 0)
299 | and (LCurrentFrame >= LStackBottom)
300 | and (LCurrentFrame < LStackTop) do
301 | begin
302 | {Ignore the requested number of levels}
303 | if ASkipFrames = 0 then
304 | begin
305 | AReturnAddresses^ := PNativeUInt(LCurrentFrame + SizeOf(Pointer))^;
306 | Inc(AReturnAddresses);
307 | Dec(AMaxDepth);
308 | end
309 | else
310 | Dec(ASkipFrames);
311 | {Get the next frame}
312 | LCurrentFrame := PNativeUInt(LCurrentFrame)^;
313 | end;
314 | {Clear the remaining entries}
315 | while AMaxDepth > 0 do
316 | begin
317 | AReturnAddresses^ := 0;
318 | Inc(AReturnAddresses);
319 | Dec(AMaxDepth);
320 | end;
321 | end;
322 | {$ifend}
323 |
324 | {-----------------------------Raw Stack Tracing-----------------------------}
325 |
326 | const
327 | {Hexadecimal characters}
328 | HexTable: array[0..15] of AnsiChar = '0123456789ABCDEF';
329 |
330 | type
331 | {The state of a memory page. Used by the raw stack tracing mechanism to determine whether an address is a valid call
332 | site or not.}
333 | TMemoryPageAccess = (mpaUnknown, mpaNotExecutable, mpaExecutable);
334 |
335 | var
336 | {There are a total of 1M x 4K pages in the (low) 4GB address space}
337 | MemoryPageAccessMap: array[0..1024 * 1024 - 1] of TMemoryPageAccess;
338 |
339 | {$IFDEF MSWINDOWS}
340 | {Updates the memory page access map. Currently only supports the low 4GB of address space.}
341 | procedure UpdateMemoryPageAccessMap(AAddress: NativeUInt);
342 | var
343 | LMemInfo: TMemoryBasicInformation;
344 | LAccess: TMemoryPageAccess;
345 | LStartPage, LPageCount: NativeUInt;
346 | begin
347 | {Query the page}
348 | if VirtualQuery(Pointer(AAddress), LMemInfo, SizeOf(LMemInfo)) <> 0 then
349 | begin
350 | {Get access type}
351 | if (LMemInfo.State = MEM_COMMIT)
352 | and (LMemInfo.Protect and (PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY or PAGE_EXECUTE) <> 0)
353 | and (LMemInfo.Protect and PAGE_GUARD = 0) then
354 | begin
355 | LAccess := mpaExecutable
356 | end
357 | else
358 | LAccess := mpaNotExecutable;
359 | {Update the map}
360 | LStartPage := NativeUInt(LMemInfo.BaseAddress) div 4096;
361 | LPageCount := LMemInfo.RegionSize div 4096;
362 | if LStartPage < NativeUInt(Length(MemoryPageAccessMap)) then
363 | begin
364 | if (LStartPage + LPageCount) >= NativeUInt(Length(MemoryPageAccessMap)) then
365 | LPageCount := NativeUInt(Length(MemoryPageAccessMap)) - LStartPage;
366 | FillChar(MemoryPageAccessMap[LStartPage], LPageCount, Ord(LAccess));
367 | end;
368 | end
369 | else
370 | begin
371 | {Invalid address}
372 | MemoryPageAccessMap[AAddress div 4096] := mpaNotExecutable;
373 | end;
374 | end;
375 | {$ENDIF}
376 |
377 | {Thread-safe version that avoids the global variable Default8087CW.}
378 | procedure Set8087CW(ANewCW: Word);
379 | var
380 | L8087CW: Word;
381 | asm
382 | mov L8087CW, ANewCW
383 | fnclex
384 | fldcw L8087CW
385 | end;
386 |
387 | {$if CompilerVersion > 22}
388 | {Thread-safe version that avoids the global variable DefaultMXCSR.}
389 | procedure SetMXCSR(ANewMXCSR: Cardinal);
390 | var
391 | LMXCSR: Cardinal;
392 | asm
393 | {$if SizeOf(Pointer) <> 8}
394 | cmp System.TestSSE, 0
395 | je @exit
396 | {$ifend}
397 | {Remove the flag bits}
398 | and ANewMXCSR, $ffc0
399 | mov LMXCSR, ANewMXCSR
400 | ldmxcsr LMXCSR
401 | @exit:
402 | end;
403 | {$ifend}
404 |
405 | {$IFDEF MSWINDOWS}
406 | {Returns true if the return address is a valid call site. This function is only safe to call while exceptions are
407 | being handled.}
408 | function IsValidCallSite(AReturnAddress: NativeUInt): boolean;
409 | var
410 | LCallAddress: NativeUInt;
411 | LCode8Back, LCode4Back, LTemp: Cardinal;
412 | LOld8087CW: Word;
413 | LOldMXCSR: Cardinal;
414 | begin
415 | {We assume (for now) that all code will execute within the first 4GB of address space.}
416 | if (AReturnAddress > $ffff) {$if SizeOf(Pointer) = 8}and (AReturnAddress <= $ffffffff){$endif} then
417 | begin
418 | {The call address is up to 8 bytes before the return address}
419 | LCallAddress := AReturnAddress - 8;
420 | {Update the page map}
421 | if MemoryPageAccessMap[LCallAddress div 4096] = mpaUnknown then
422 | UpdateMemoryPageAccessMap(LCallAddress);
423 | {Check the page access}
424 | if (MemoryPageAccessMap[LCallAddress div 4096] = mpaExecutable)
425 | and (MemoryPageAccessMap[(LCallAddress + 8) div 4096] = mpaExecutable) then
426 | begin
427 | {Try to determine what kind of call it is (if any), more or less in order of frequency of occurrence. (Code below
428 | taken from the Jedi Code Library (jcl.sourceforge.net).) We need to retrieve the current floating point control
429 | registers, since any external exception will reset it to the DLL defaults which may not otherwise correspond to
430 | the defaults of the main application (QC 107198).}
431 | LOld8087CW := Get8087CW;
432 | LOldMXCSR := GetMXCSR;
433 | try
434 | {5 bytes, CALL NEAR REL32}
435 | if PByteArray(LCallAddress)[3] = $E8 then
436 | begin
437 | Result := True;
438 | Exit;
439 | end;
440 | {Get the 4 bytes before the return address}
441 | LCode4Back := PCardinal(LCallAddress + 4)^;
442 | {2 byte call?}
443 | LTemp := LCode4Back and $F8FF0000;
444 | {2 bytes, CALL NEAR EAX}
445 | if LTemp = $D0FF0000 then
446 | begin
447 | Result := True;
448 | Exit;
449 | end;
450 | {2 bytes, CALL NEAR [EAX]}
451 | if LTemp = $10FF0000 then
452 | begin
453 | LTemp := LCode4Back - LTemp;
454 | if (LTemp <> $04000000) and (LTemp <> $05000000) then
455 | begin
456 | Result := True;
457 | Exit;
458 | end;
459 | end;
460 | {3 bytes, CALL NEAR [EAX+EAX*i]}
461 | if (LCode4Back and $00FFFF00) = $0014FF00 then
462 | begin
463 | Result := True;
464 | Exit;
465 | end;
466 | {3 bytes, CALL NEAR [EAX+$12]}
467 | if ((LCode4Back and $00F8FF00) = $0050FF00)
468 | and ((LCode4Back and $00070000) <> $00040000) then
469 | begin
470 | Result := True;
471 | Exit;
472 | end;
473 | {4 bytes, CALL NEAR [EAX+EAX+$12]}
474 | if Word(LCode4Back) = $54FF then
475 | begin
476 | Result := True;
477 | Exit;
478 | end;
479 | {6 bytes, CALL NEAR [$12345678]}
480 | LCode8Back := PCardinal(LCallAddress)^;
481 | if (LCode8Back and $FFFF0000) = $15FF0000 then
482 | begin
483 | Result := True;
484 | Exit;
485 | end;
486 | {6 bytes, CALL NEAR [EAX+$12345678]}
487 | if ((LCode8Back and $F8FF0000) = $90FF0000)
488 | and ((LCode8Back and $07000000) <> $04000000) then
489 | begin
490 | Result := True;
491 | Exit;
492 | end;
493 | {7 bytes, CALL NEAR [EAX+EAX+$1234567]}
494 | if (LCode8Back and $00FFFF00) = $0094FF00 then
495 | begin
496 | Result := True;
497 | Exit;
498 | end;
499 | {7 bytes, CALL FAR $1234:12345678}
500 | if (LCode8Back and $0000FF00) = $00009A00 then
501 | begin
502 | Result := True;
503 | Exit;
504 | end;
505 | {Not a valid call site}
506 | Result := False;
507 | except
508 | {The access has changed}
509 | UpdateMemoryPageAccessMap(LCallAddress);
510 | {The RTL sets the FPU control words to the default values if an external exception occurs. Reset their values
511 | here to the values on entry to this call.}
512 | Set8087CW(LOld8087CW);
513 | SetMXCSR(LOldMXCSR);
514 | {Not executable}
515 | Result := False;
516 | end;
517 | end
518 | else
519 | Result := False;
520 | end
521 | else
522 | Result := False;
523 | end;
524 | {$ENDIF}
525 |
526 | {Dumps the call stack trace to the given address. Fills the list with the addresses where the called addresses can be
527 | found. This is the "raw" stack tracing routine.}
528 |
529 | {$IFDEF MACOS}
530 | function backtrace(result: PNativeUInt; size: Integer): Integer; cdecl; external libc name '_backtrace';
531 | function _NSGetExecutablePath(buf: PAnsiChar; BufSize: PCardinal): Integer; cdecl; external libc name '__NSGetExecutablePath';
532 | {$ENDIF}
533 |
534 | procedure GetRawStackTrace(AReturnAddresses: PNativeUInt; AMaxDepth, ASkipFrames: Cardinal);
535 | var
536 | LStackTop, LStackBottom, LCurrentFrame, LNextFrame, LReturnAddress,
537 | LStackAddress: NativeUInt;
538 | LLastOSError: Cardinal;
539 |
540 | {$IFDEF MACOS}
541 | StackLog: PNativeUInt; //array[0..10] of Pointer;
542 | Cnt: Integer;
543 | I: Integer;
544 | {$ENDIF}
545 | begin
546 | {$IFDEF MACOS}
547 | {$POINTERMATH ON}
548 | Cnt := AMaxDepth + ASkipFrames;
549 |
550 | GetMem(StackLog, SizeOf(Pointer) * Cnt);
551 | try
552 | Cnt := backtrace(StackLog, Cnt);
553 |
554 | for I := ASkipFrames to Cnt - 1 do
555 | begin
556 | // writeln('Stack: ', inttohex(NativeUInt(stacklog[I]), 8));
557 | AReturnAddresses[I - ASkipFrames] := StackLog[I];
558 | end;
559 |
560 | finally
561 | FreeMem(StackLog);
562 | end;
563 | {$POINTERMATH OFF}
564 | {$ENDIF}
565 | {Are exceptions being handled? Can only do a raw stack trace if the possible access violations are going to be
566 | handled.}
567 | {$IFDEF MSWINDOWS}
568 | if Assigned(ExceptObjProc) then
569 | begin
570 | {Save the last Windows error code}
571 | LLastOSError := GetLastError;
572 | {Get the call stack top and current bottom}
573 | GetStackRange(LStackTop, LStackBottom);
574 | Dec(LStackTop, SizeOf(Pointer) - 1);
575 | {Get the current frame start}
576 | LCurrentFrame := LStackBottom;
577 | {Fill the call stack}
578 | while (AMaxDepth > 0)
579 | and (LCurrentFrame < LStackTop) do
580 | begin
581 | {Get the next frame}
582 | LNextFrame := PNativeUInt(LCurrentFrame)^;
583 | {Is it a valid stack frame address?}
584 | if (LNextFrame < LStackTop)
585 | and (LNextFrame > LCurrentFrame) then
586 | begin
587 | {The pointer to the next stack frame appears valid: Get the return address of the current frame}
588 | LReturnAddress := PNativeUInt(LCurrentFrame + SizeOf(Pointer))^;
589 | {Does this appear to be a valid return address}
590 | if (LReturnAddress > $ffff) {$if SizeOf(Pointer) = 8}and (LReturnAddress <= $ffffffff){$endif} then
591 | begin
592 | {Is the map for this return address incorrect? It may be unknown or marked as non-executable because a library
593 | was previously not yet loaded, or perhaps this is not a valid stack frame.}
594 | if MemoryPageAccessMap[(LReturnAddress - 8) div 4096] <> mpaExecutable then
595 | UpdateMemoryPageAccessMap(LReturnAddress - 8);
596 | {Is this return address actually valid?}
597 | if IsValidCallSite(LReturnAddress) then
598 | begin
599 | {Ignore the requested number of levels}
600 | if ASkipFrames = 0 then
601 | begin
602 | AReturnAddresses^ := LReturnAddress;
603 | Inc(AReturnAddresses);
604 | Dec(AMaxDepth);
605 | end;
606 | end
607 | else
608 | begin
609 | {If the return address is invalid it implies this stack frame is invalid after all.}
610 | LNextFrame := LStackTop;
611 | end;
612 | end
613 | else
614 | begin
615 | {The return address is bad - this is not a valid stack frame}
616 | LNextFrame := LStackTop;
617 | end;
618 | end
619 | else
620 | begin
621 | {This is not a valid stack frame}
622 | LNextFrame := LStackTop;
623 | end;
624 | {Do not check intermediate entries if there are still frames to skip}
625 | if ASkipFrames <> 0 then
626 | begin
627 | Dec(ASkipFrames);
628 | end
629 | else
630 | begin
631 | {Check all stack entries up to the next stack frame}
632 | LStackAddress := LCurrentFrame + 2 * SizeOf(Pointer);
633 | while (AMaxDepth > 0)
634 | and (LStackAddress < LNextFrame) do
635 | begin
636 | {Get the return address}
637 | LReturnAddress := PNativeUInt(LStackAddress)^;
638 | {Is this a valid call site?}
639 | if IsValidCallSite(LReturnAddress) then
640 | begin
641 | AReturnAddresses^ := LReturnAddress;
642 | Inc(AReturnAddresses);
643 | Dec(AMaxDepth);
644 | end;
645 | {Check the next stack address}
646 | Inc(LStackAddress, SizeOf(Pointer));
647 | end;
648 | end;
649 | {Do the next stack frame}
650 | LCurrentFrame := LNextFrame;
651 | end;
652 | {Clear the remaining entries}
653 | while AMaxDepth > 0 do
654 | begin
655 | AReturnAddresses^ := 0;
656 | Inc(AReturnAddresses);
657 | Dec(AMaxDepth);
658 | end;
659 | {Restore the last Windows error code, since a VirtualQuery call may have modified it.}
660 | SetLastError(LLastOSError);
661 | end
662 | else
663 | begin
664 | {Exception handling is not available - do a frame based stack trace}
665 | GetFrameBasedStackTrace(AReturnAddresses, AMaxDepth, ASkipFrames);
666 | end;
667 | {$ENDIF}
668 | end;
669 |
670 | {-----------------------------Stack Trace Logging----------------------------}
671 |
672 | {Gets the textual representation of the stack trace into ABuffer and returns a pointer to the position just after the
673 | last character.}
674 | {$ifdef JCLDebug}
675 | {Converts an unsigned integer to a hexadecimal string at the buffer location, returning the new buffer position.}
676 | function NativeUIntToHexBuf(ANum: NativeUInt; APBuffer: PAnsiChar): PAnsiChar;
677 | const
678 | MaxDigits = 16;
679 | var
680 | LDigitBuffer: array[0..MaxDigits - 1] of AnsiChar;
681 | LCount: Cardinal;
682 | LDigit: NativeUInt;
683 | begin
684 | {Generate the digits in the local buffer}
685 | LCount := 0;
686 | repeat
687 | LDigit := ANum;
688 | ANum := ANum div 16;
689 | LDigit := LDigit - ANum * 16;
690 | Inc(LCount);
691 | LDigitBuffer[MaxDigits - LCount] := HexTable[LDigit];
692 | until ANum = 0;
693 | {Add leading zeros}
694 | while LCount < SizeOf(NativeUInt) * 2 do
695 | begin
696 | Inc(LCount);
697 | LDigitBuffer[MaxDigits - LCount] := '0';
698 | end;
699 | {Copy the digits to the output buffer and advance it}
700 | System.Move(LDigitBuffer[MaxDigits - LCount], APBuffer^, LCount);
701 | Result := APBuffer + LCount;
702 | end;
703 |
704 | {Subroutine used by LogStackTrace}
705 | procedure AppendInfoToString(var AString: string; const AInfo: string);
706 | begin
707 | if AInfo <> '' then
708 | AString := Format('%s[%s]', [AString, AInfo]);
709 | end;
710 |
711 | var
712 | LReturnAddressInfoCache: TReturnAddressInfoCache;
713 | LLogStackTrace_Locked: Integer; //0 = unlocked, 1 = locked
714 |
715 | function LogStackTrace(AReturnAddresses: PNativeUInt; AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
716 | var
717 | LInd: Cardinal;
718 | LAddress: NativeUInt;
719 | LInfo: TJCLLocationInfo;
720 | LTempStr: string;
721 | P: PChar;
722 | LLocationCacheInitialized: Boolean;
723 | LPInfo: PReturnAddressInfo;
724 | begin
725 | LLocationCacheInitialized := False;
726 |
727 | Result := ABuffer;
728 |
729 | {This routine is protected by a lock - only one thread can be inside it at any given time.}
730 | while AtomicCmpExchange(LLogStackTrace_Locked, 1, 0) <> 0 do
731 | Winapi.Windows.SwitchToThread;
732 |
733 | try
734 | for LInd := 0 to AMaxDepth - 1 do
735 | begin
736 | LAddress := AReturnAddresses^;
737 | if LAddress = 0 then
738 | Exit;
739 | Result^ := #13;
740 | Inc(Result);
741 | Result^ := #10;
742 | Inc(Result);
743 | Result := NativeUIntToHexBuf(LAddress, Result);
744 |
745 | {If the info for the return address is not yet in the cache, add it.}
746 | LPInfo := LReturnAddressInfoCache.FindEntry(LAddress);
747 | if LPInfo = nil then
748 | begin
749 | if not LLocationCacheInitialized then
750 | begin
751 | {$if declared(BeginGetLocationInfoCache)} // available depending on the JCL's version
752 | BeginGetLocationInfoCache;
753 | {$endif}
754 | LLocationCacheInitialized := True;
755 | end;
756 | {Get location info for the caller (at least one byte before the return address).}
757 | GetLocationInfo(Pointer(LAddress - 1), LInfo);
758 | {Build the result string}
759 | LTempStr := ' ';
760 | AppendInfoToString(LTempStr, LInfo.SourceName);
761 | AppendInfoToString(LTempStr, LInfo.UnitName);
762 |
763 | {Remove UnitName from ProcedureName, no need to output it twice}
764 | P := PChar(LInfo.ProcedureName);
765 | if (StrLComp(P, PChar(LInfo.UnitName), Length(LInfo.UnitName)) = 0) and (P[Length(LInfo.UnitName)] = '.') then
766 | AppendInfoToString(LTempStr, Copy(LInfo.ProcedureName, Length(LInfo.UnitName) + 2))
767 | else
768 | AppendInfoToString(LTempStr, LInfo.ProcedureName);
769 |
770 | if LInfo.LineNumber <> 0 then
771 | AppendInfoToString(LTempStr, IntToStr(LInfo.LineNumber));
772 |
773 | LPInfo := LReturnAddressInfoCache.AddEntry(LAddress, AnsiString(LTempStr));
774 | end;
775 |
776 | System.Move(LPInfo.InfoText, Result^, LPInfo.InfoTextLength);
777 | Inc(Result, LPInfo.InfoTextLength);
778 |
779 | Inc(AReturnAddresses);
780 | end;
781 | finally
782 | if LLocationCacheInitialized then
783 | begin
784 | {$if declared(BeginGetLocationInfoCache)} // available depending on the JCL's version
785 | EndGetLocationInfoCache;
786 | {$endif}
787 | end;
788 |
789 | LLogStackTrace_Locked := 0;
790 | end;
791 | end;
792 | {$endif}
793 |
794 | {$ifdef madExcept}
795 | function LogStackTrace(AReturnAddresses: PNativeUInt;
796 | AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
797 | begin
798 | {Needs madExcept 2.7i or madExcept 3.0a or a newer build}
799 | Result := madStackTrace.FastMM_LogStackTrace(
800 | AReturnAddresses,
801 | AMaxDepth,
802 | ABuffer,
803 | {madExcept stack trace fine tuning}
804 | false, //hide items which have no line number information?
805 | true, //show relative address offset to procedure entrypoint?
806 | true, //show relative line number offset to procedure entry point?
807 | false //skip special noise reduction processing?
808 | );
809 | end;
810 | {$endif}
811 |
812 | {$ifdef EurekaLog_Legacy}
813 | function LogStackTrace(AReturnAddresses: PNativeUInt; AMaxDepth: Cardinal;
814 | ABuffer: PAnsiChar): PAnsiChar;
815 | begin
816 | {Needs EurekaLog 5 or 6}
817 | Result := ExceptionLog.FastMM_LogStackTrace(
818 | AReturnAddresses, AMaxDepth, ABuffer,
819 | {EurekaLog stack trace fine tuning}
820 | False, // Show the DLLs functions call. <--|
821 | // |-- See the note below!
822 | False, // Show the BPLs functions call. <--|
823 | True // Show relative line no. offset to procedure start point.
824 | );
825 | // NOTE:
826 | // -----
827 | // With these values set both to "False", EurekaLog try to returns the best
828 | // call-stack available.
829 | //
830 | // To do this EurekaLog execute the following points:
831 | // --------------------------------------------------
832 | // 1)...try to fill all call-stack items using only debug data with line no.
833 | // 2)...if remains some empty call-stack items from the previous process (1),
834 | // EurekaLog try to fill these with the BPLs functions calls;
835 | // 3)...if remains some empty call-stack items from the previous process (2),
836 | // EurekaLog try to fill these with the DLLs functions calls;
837 | end;
838 | {$endif}
839 |
840 | {$ifdef EurekaLog_V7}
841 | function LogStackTrace(AReturnAddresses: PNativeUInt; AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
842 | begin
843 | {Needs EurekaLog 7 or later}
844 | Result := EFastMM4Support.FastMM_LogStackTrace(PPointer(AReturnAddresses), AMaxDepth, ABuffer, 10 * 256,
845 | [ddUnit, ddProcedure, ddSourceCode], True, False, False, True, False, True);
846 | end;
847 | {$endif}
848 |
849 | {$IFDEF MACOS}
850 |
851 | {Appends the source text to the destination and returns the new destination position}
852 | function AppendStringToBuffer(const ASource, ADestination: PAnsiChar; ACount: Cardinal): PAnsiChar;
853 | begin
854 | System.Move(ASource^, ADestination^, ACount);
855 | Result := Pointer(PByte(ADestination) + ACount);
856 | end;
857 |
858 | var
859 | MapFile: TSBMapFile;
860 |
861 | function LogStackTrace(AReturnAddresses: PNativeUInt; AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
862 | var
863 | s1: AnsiString;
864 | I: Integer;
865 | FileName: array[0..255] of AnsiChar;
866 | Len: Cardinal;
867 | begin
868 | {$POINTERMATH ON}
869 | // writelN('LogStackTrace');
870 | // for I := 0 to AMaxDepth - 1 do
871 | // Writeln(IntToHex(AReturnAddresses[I], 8));
872 |
873 | // s1 := IntToHex(Integer(AReturnAddresses[0]), 8);
874 | // result := ABuffer;
875 | // Move(pointer(s1)^, result^, Length(s1));
876 | // inc(result, Length(s1));
877 |
878 | if MapFile = nil then
879 | begin
880 | MapFile := TSBMapFile.Create;
881 | Len := Length(FileName);
882 | _NSGetExecutablePath(@FileName[0], @Len);
883 | if FileExists(ChangeFileExt(FileName, '.map')) then
884 | MapFile.LoadFromFile(ChangeFileExt(FileName, '.map'));
885 | end;
886 |
887 | Result := ABuffer;
888 |
889 | s1 := #13#10;
890 | Result := AppendStringToBuffer(PAnsiChar(s1), Result, Length(s1));
891 |
892 | for I := 0 to AMaxDepth - 1 do
893 | begin
894 | s1 := IntToHex(AReturnAddresses[I], 8);
895 | s1 := s1 + ' ' + MapFile.GetFunctionName(AReturnAddresses[I]) + #13#10;
896 | Result := AppendStringToBuffer(PAnsiChar(s1), Result, Length(s1));
897 | end;
898 |
899 | {$POINTERMATH OFF}
900 | end;
901 | {$ENDIF}
902 |
903 | {-----------------------------Exported Functions----------------------------}
904 |
905 | exports
906 | GetFrameBasedStackTrace,
907 | GetRawStackTrace,
908 | LogStackTrace;
909 |
910 | begin
911 | {$ifdef JCLDebug}
912 | JclStackTrackingOptions := JclStackTrackingOptions + [stAllModules];
913 | {$endif}
914 |
915 | end.
916 |
--------------------------------------------------------------------------------
/FullDebugMode DLL/FastMM_FullDebugMode.dproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {990612ba-64b5-4560-bc82-798c7cdf11d3}
4 | FastMM_FullDebugMode.dpr
5 | Debug
6 | DCC32
7 | FastMM_FullDebugMode.dll
8 | None
9 | 19.5
10 | True
11 | Debug
12 | Win32
13 | 3
14 | Library
15 |
16 |
17 | true
18 |
19 |
20 | true
21 | Base
22 | true
23 |
24 |
25 | true
26 | Base
27 | true
28 |
29 |
30 | true
31 | Base
32 | true
33 |
34 |
35 | true
36 | Cfg_1
37 | true
38 | true
39 |
40 |
41 | true
42 | Cfg_1
43 | true
44 | true
45 |
46 |
47 | true
48 | Base
49 | true
50 |
51 |
52 | true
53 | Cfg_2
54 | true
55 | true
56 |
57 |
58 | true
59 | Cfg_2
60 | true
61 | true
62 |
63 |
64 | None
65 | true
66 | 65
67 | System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace)
68 | 8
69 | true
70 | 1033
71 | CompanyName=Pierre le Riche;FileDescription=FastMM Debug Support Library;FileVersion=1.65.0.8;InternalName=;LegalCopyright=(c) 2004-2023 Pierre le Riche;LegalTrademarks=;OriginalFilename=FastMM_FullDebugMode.dll;ProductName=FastMM Debug Support Library;ProductVersion=1.65;Comments=
72 | FastMM_FullDebugMode
73 |
74 |
75 | FullDebugMode_DLL_TestApp.exe
76 | System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
77 | CompanyName=Pierre le Riche;FileVersion=1.65.0.8;InternalName=;LegalCopyright=(c) 2004-2023 Pierre le Riche;LegalTrademarks=;OriginalFilename=FastMM_FullDebugMode.dll;ProductVersion=1.65;Comments=;ProgramID=;FileDescription=FastMM Debug Support Library;ProductName=FastMM Debug Support Library
78 | none
79 |
80 |
81 | C:\Projects\CIMSO\Components\Current\PSD40\FastMM4\FullDebugMode DLL\FullDebugMode_DLL_TestApp.exe
82 | System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
83 | CompanyName=Pierre le Riche;FileVersion=1.65.0.8;InternalName=;LegalCopyright=(c) 2004-2023 Pierre le Riche;LegalTrademarks=;OriginalFilename=FastMM_FullDebugMode64.dll;ProductVersion=1.65;Comments=;ProgramID=;FileDescription=FastMM Debug Support Library;ProductName=FastMM Debug Support Library
84 | none
85 |
86 |
87 | 7.0
88 | 0
89 | False
90 | 0
91 | 3
92 | RELEASE;$(DCC_Define)
93 |
94 |
95 | CompanyName=Pierre le Riche;FileVersion=1.65.0.8;InternalName=;LegalCopyright=(c) 2004-2023 Pierre le Riche;LegalTrademarks=;OriginalFilename=FastMM_FullDebugMode.dll;ProductVersion=1.65;Comments=;ProgramID=;FileDescription=FastMM Debug Support Library;ProductName=FastMM Debug Support Library
96 |
97 |
98 | CompanyName=Pierre le Riche;FileVersion=1.65.0.8;InternalName=;LegalCopyright=(c) 2004-2023 Pierre le Riche;LegalTrademarks=;OriginalFilename=FastMM_FullDebugMode64.dll;ProductVersion=1.65;Comments=;ProgramID=;FileDescription=FastMM Debug Support Library;ProductName=FastMM Debug Support Library
99 |
100 |
101 | 7.0
102 | 3
103 | DEBUG;$(DCC_Define)
104 |
105 |
106 | CompanyName=Pierre le Riche;FileVersion=1.65.0.8;InternalName=;LegalCopyright=(c) 2004-2023 Pierre le Riche;LegalTrademarks=;OriginalFilename=FastMM_FullDebugMode.dll;ProductVersion=1.65;Comments=;ProgramID=;FileDescription=FastMM Debug Support Library;ProductName=FastMM Debug Support Library
107 |
108 |
109 | CompanyName=Pierre le Riche;FileVersion=1.65.0.8;InternalName=;LegalCopyright=(c) 2004-2023 Pierre le Riche;LegalTrademarks=;OriginalFilename=FastMM_FullDebugMode64.dll;ProductVersion=1.65;Comments=;ProgramID=;FileDescription=FastMM Debug Support Library;ProductName=FastMM Debug Support Library
110 | true
111 |
112 |
113 | Delphi.Personality.12
114 | VCLApplication
115 |
116 |
117 |
118 | False
119 | True
120 | False
121 |
122 |
123 | True
124 | False
125 | 1
126 | 61
127 | 0
128 | 6
129 | False
130 | False
131 | False
132 | False
133 | False
134 | 7177
135 | 1252
136 |
137 |
138 | PSD / Pierre le Riche
139 | FastMM FullDebugMode Support DLL
140 | 1.61.0.6
141 |
142 | (c) Professional Software Development
143 | Licence: MPL 1.1
144 | FastMM_FullDebugMode.dll
145 | FastMM FullDebugMode Support DLL
146 | 1.60
147 |
148 |
149 |
150 | FastMM_FullDebugMode.dpr
151 |
152 |
153 |
154 | True
155 | True
156 |
157 |
158 | 12
159 |
160 |
161 |
162 |
163 | MainSource
164 |
165 |
166 | Base
167 |
168 |
169 | Cfg_1
170 | Base
171 |
172 |
173 | Cfg_2
174 | Base
175 |
176 |
177 |
178 |
179 |
180 |
181 |
--------------------------------------------------------------------------------
/FullDebugMode DLL/Precompiled/FastMM_FullDebugMode.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/FullDebugMode DLL/Precompiled/FastMM_FullDebugMode.dll
--------------------------------------------------------------------------------
/FullDebugMode DLL/Precompiled/FastMM_FullDebugMode64.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/FullDebugMode DLL/Precompiled/FastMM_FullDebugMode64.dll
--------------------------------------------------------------------------------
/Images/FastMM5-social.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/Images/FastMM5-social.png
--------------------------------------------------------------------------------
/Images/FastMM5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/Images/FastMM5.jpg
--------------------------------------------------------------------------------
/Images/FastMM5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/Images/FastMM5.png
--------------------------------------------------------------------------------
/Images/FastMMv5-square.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/Images/FastMMv5-square.jpg
--------------------------------------------------------------------------------
/Images/FastMMv5-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pleriche/FastMM5/c80e84e48190d0d2ab1b184e17c50e79b5e3dcbd/Images/FastMMv5-square.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FastMM5
2 | FastMM is a fast replacement memory manager for Embarcadero Delphi applications that scales well across multiple threads and CPU cores, is not prone to memory fragmentation, and supports shared memory without the use of external .DLL files.
3 |
4 | 
5 |
6 | Version 5 is a complete rewrite of FastMM. It is designed from the ground up to simultaneously keep the strengths and address the shortcomings of version 4.992:
7 | * Multithreaded scaling across multiple CPU cores is massively improved, without memory usage blowout. It can be configured to scale close to linearly for any number of CPU cores.
8 | * In the Fastcode memory manager benchmark tool FastMM 5 scores 15% higher than FastMM 4.992 on the single threaded benchmarks, and 30% higher on the multithreaded benchmarks. (I7-8700K CPU, EnableMMX and AssumeMultithreaded options enabled.)
9 | * It is fully configurable runtime. There is no need to change conditional defines and recompile to change options. (It is however backward compatible with many of the version 4 conditional defines.)
10 | * Debug mode uses the same debug support library as version 4 (FastMM_FullDebugMode.dll) by default, but custom stack trace routines are also supported. Call FastMM_EnterDebugMode to switch to debug mode ("FullDebugMode") and call FastMM_ExitDebugMode to return to performance mode. Calls may be nested, in which case debug mode will be exited after the last FastMM_ExitDebugMode call.
11 | * Supports 8, 16, 32 or 64 byte alignment of all blocks. Call FastMM_EnterMinimumAddressAlignment to request a minimum block alignment, and FastMM_ExitMinimumAddressAlignment to rescind a prior request. Calls may be nested, in which case the coarsest alignment request will be in effect.
12 | * All event notifications (errors, memory leak messages, etc.) may be routed to the debugger (via OutputDebugString), a log file, the screen or any combination of the three. Messages are built using templates containing mail-merge tokens. Templates may be changed runtime to facilitate different layouts and/or translation into any language. Templates fully support Unicode, and the log file may be configured to be written in UTF-8 or UTF-16 format, with or without a BOM.
13 | * It may be configured runtime to favour speed, memory usage efficiency or a blend of the two via the FastMM_SetOptimizationStrategy call.
14 |
15 | Homepage: https://github.com/pleriche/FastMM5
16 |
17 | ### Developed by
18 | Pierre le Riche, copyright 2004 - 2025, all rights reserved
19 |
20 | ### Sponsored by
21 | [gs-soft AG](https://www.gs-soft.com/)
22 |
23 | ### Licence
24 | FastMM 5 is dual-licensed. You may choose to use it under the restrictions of the GPL v3 licence at no cost to you, or you may purchase a commercial licence. A commercial licence grants you the right to use FastMM5 in your own applications, royalty free, and without any requirement to disclose your source code nor any modifications to FastMM to any other party. A commercial licence lasts into perpetuity, and entitles you to all future updates, free of charge. A commercial licence is sold per developer developing applications that use FastMM, as follows:
25 |
26 |
Number Of Developers
Price (USD)
27 |
1 developer
$99
28 |
2 developers
$189
29 |
3 developers
$269
30 |
4 developers
$339
31 |
5 developers
$399
32 |
More than 5 developers
$399 + $50 per developer from the 6th onwards
33 |
Site licence (unlimited number of developers affiliated with the owner of the licence, i.e. employees, co-workers, interns and contractors)
$999
34 |
35 |
36 | Please send an e-mail to fastmm@leriche.org to request an invoice before or after payment is made. Payment may be made via PayPal at https://www.paypal.me/fastmm (paypal@leriche.org), or via bank transfer. Bank details will be provided on the invoice.
37 |
38 | Support (via e-mail) is available for users with a commercial licence. Enhancement requests submitted by users with a commercial licence will be prioritized.
39 |
40 | ### Usage Instructions
41 | Add FastMM5.pas as the first unit in your project's DPR file. It will install itself automatically during startup, replacing the default memory manager.
42 |
43 | In order to share the memory manager between the main application and libraries call FastMM_AttemptToUseSharedMemoryManager (in order to use the memory manager of another module in the process) or FastMM_ShareMemoryManager (to share the memory manager instance of the current module with other modules). It is important to share the memory manager between modules where memory allocated in the one module may be freed by the other.
44 |
45 | If the application requires memory alignment greater than the default, call FastMM_EnterMinimumAddressAlignment and once the greater alignment is no longer required call FastMM_ExitMinimumAddressAlignment. Calls may be nested. The coarsest memory alignment requested takes precedence.
46 |
47 | At the cost of performance and increased memory usage FastMM can log additional metadata together with every block. In order to enable this mode call FastMM_EnterDebugMode and to exit debug mode call FastMM_ExitDebugMode. Calls may be nested in which case debug mode will be active as long as the number of FastMM_EnterDebugMode calls exceed the number of FastMM_ExitDebugMode calls. In debug mode freed memory blocks will be filled with the byte pattern $808080... so that usage of a freed memory block or object, as well as corruption of the block header and/or footer will likely be detected. If the debug support library, FastMM_FullDebugMode.dll, is available and the application has not specified its own handlers for FastMM_GetStackTrace and FastMM_ConvertStackTraceToText then the support library will be loaded during the first call to FastMM_EnterDebugMode.
48 |
49 | Events (memory leaks, errors, etc.) may be logged to file, displayed on-screen, passed to the debugger or any combination of the three. Specify how each event should be handled via the FastMM_LogToFileEvents, FastMM_MessageBoxEvents and FastMM_OutputDebugStringEvents variables. The default event log filename will be built from the application filepath, but may be overridden via FastMM_SetEventLogFilename. Messages are built from templates that may be changed/translated by the application.
50 |
51 | The optimization strategy of the memory manager may be tuned via FastMM_SetOptimizationStrategy. It can be set to favour performance, low memory usage, or a blend of both. The default strategy is to blend the performance and low memory usage goals.
52 |
53 | The default configuration should scale close to linearly up to between 8 and 16 threads, so for most applications there should be no need to tweak any performance settings. Beyond 16 threads you may consider increasing the number of arenas (CFastMM_...BlockArenaCount), but inspect the thread contention counts first (FastMM_...BlockThreadContentionCount), before assuming that it is necessary.
54 |
55 |
56 | ### The following conditional defines are supported
57 | * FastMM_FullDebugMode (or FullDebugMode) - If defined then FastMM_EnterDebugMode will be called on startup so that the memory manager starts up in debug mode. If FullDebugMode is defined then the FastMM_DebugLibraryStaticDependency define is also implied.
58 | * FastMM_FullDebugModeWhenDLLAvailable (or FullDebugModeWhenDLLAvailable) - If defined an attempt will be made to load the debug support library during startup. If successful then FastMM_EnterDebugMode will be called so that the memory manager starts up in debug mode.
59 | * FastMM_DebugLibraryStaticDependency - If defined there will be a static dependency on the debug support library, FastMM_FullDebugMode.dll (32-bit) or FastMM_FullDebugMode64.dll (64-bit). If FastMM_EnterDebugMode will be called in the startup code while and the memory manager will also be shared between an application and libraries, then it may be necessary to enable this define in order to avoid DLL unload order issues during application shutdown (typically manifesting as an access violation when attempting to report on memory leaks during shutdown). It is a longstanding issue with Windows that it is not always able to unload DLLs in the correct order on application shutdown when DLLs are loaded dynamically during startup. Note that while enabling this define will introduce a static dependency on the debug support library, it does not actually enter debug mode by default - FastMM_EnterDebugMode must still be called to enter debug mode, and FastMM_ExitDebugMode can be called to exit debug mode at any time.
60 | * FastMM_ClearLogFileOnStartup (or ClearLogFileOnStartup) - When defined FastMM_DeleteEventLogFile will be called during startup, deleting the event log file (if it exists).
61 | * FastMM_Align16Bytes (or Align16Bytes) - When defined FastMM_EnterMinimumAddressAlignment(maa16Bytes) will be called during startup, forcing a minimum of 16 byte alignment for memory blocks. Note that this has no effect under 64 bit, since 16 bytes is already the minimum alignment.
62 | * FastMM_5Arenas, FastMM_6Arenas .. FastMM_16Arenas - Increases the number of arenas from the default values. See the notes for the CFastMM_SmallBlockArenaCount constant for guidance on the appropriate number of arenas.
63 | * FastMM_DisableAutomaticInstall - Disables automatic installation of FastMM as the memory manager. If defined then FastMM_Initialize should be called from application code in order to install FastMM, and FastMM_Finalize to uninstall and perform the leak check (if enabled), etc.
64 | * FastMM_EnableMemoryLeakReporting (or EnableMemoryLeakReporting) - If defined then the memory leak summary and detail will be added to the set of events logged to file (FastMM_LogToFileEvents) and the leak summary will be added to the set of events displayed on-screen (FastMM_MessageBoxEvents).
65 | * FastMM_RequireDebuggerPresenceForLeakReporting (or RequireDebuggerPresenceForLeakReporting) - Used in conjunction with EnableMemoryLeakReporting - if the application is not running under the debugger then the EnableMemoryLeakReporting define is ignored.
66 | * FastMM_NoMessageBoxes (or NoMessageBoxes) - Clears the set of events that will cause a message box to be displayed (FastMM_MessageBoxEvents) on startup.
67 | * FastMM_ShareMM (or ShareMM) - If defined then FastMM_ShareMemoryManager will be called during startup, sharing the memory manager of the module if the memory manager of another module is not already being shared.
68 | * FastMM_ShareMMIfLibrary (or ShareMMIfLibrary) - If defined and the module is not a library then the ShareMM define is disabled.
69 | * FastMM_AttemptToUseSharedMM (or AttemptToUseSharedMM) - If defined FastMM_AttemptToUseSharedMemoryManager will be called during startup, switching to using the memory manager shared by another module (if there is a shared memory manager).
70 | * FastMM_NeverUninstall (or NeverUninstall) - Sets the FastMM_NeverUninstall global variable to True. Use this if any leaked pointers should remain valid after this unit is finalized.
71 | * PurePascal - The assembly language code paths are disabled, and only the Pascal code paths are used. This is normally used for debugging purposes only.
72 |
73 | ### Supported Compilers
74 | Delphi XE3 and later
75 |
76 | ### Supported Platforms
77 | Windows, 32-bit and 64-bit
78 |
79 | ### Change Log
80 | ##### Version 5.00
81 | * First non-beta release of FastMM 5.
82 |
83 | ##### Version 5.01
84 | * Enhancement: Log a stack trace for the virtual method call that lead to a "virtual method call on freed object" error
85 |
86 | ##### Version 5.02
87 | * Backward compatibility improvement: If ReportMemoryLeaksOnShutdown = True then mmetUnexpectedMemoryLeakSummary will automatically be included in FastMM_MessageBoxEvents, and the the leak summary will thus be displayed on shutdown.
88 | * FastMM in debug mode will now catch all TObject virtual method calls on a freed object. Previously it only caught some commonly used ones.
89 | * Increase the number of virtual methods supported by TFastMM_FreedObject to 75. (There are some classes in the RTL that have more than 30 virtual methods, e.g. TStringList).
90 | * Add a lock timeout for FastMM_LogStateToFile and FastMM_WalkBlocks. Some severe memory corruption crashes may leave an arena locked, in which case it was previously not possible to walk blocks or dump the memory manager state to file in the crash handler.
91 | * Add backward compatibility support for the ClearLogFileOnStartup v4 define.
92 |
93 | ##### Version 5.03
94 | * Add runtime support for configuring the number of entries in the debug block allocation and free stack traces (FastMM_SetDebugModeStackTraceEntryCount)
95 | * Enhancements to BorlndMM.dll: Support for a "DEBUG" build configuration as well as additional exports
96 | * Additional demo applications
97 | * Add a FastMM_NeverUninstall boolean variable. It will be set to True on startup if "NeverUninstall" is defined (for backward compatibility with FastMM4). This is useful in the rare situation where live pointers are expected to remain valid after the FastMM unit is finalized.
98 | * Change FastMM_ScanDebugBlocksForCorruption to a function that always returns a boolean result of True. This allows it to be used in a debug watch, thus scanning blocks every time the debugger stops on a breakpoint, etc.
99 | * Expose DebugLibrary_GetRawStackTrace and DebugLibrary_GetFrameBasedStackTrace in order to allow runtime switching between raw and frame based stack traces.
100 | * Add support for a new conditional define "FastMM_DisableAutomaticInstall". When defined FastMM will not be installed automatically, and instead the application should call FastMM_Initialize to initialize and install FastMM, and finally FastMM_Finalize to uninstall it. This allows the application runtime control over whether to use FastMM or not. Note that FastMM_Initialize has to be called very early in the unit initialization sequence, before any memory is allocated through the default memory manager. Practically this means it has to be called from either the first or second (after FastMM5.pas) unit in your project DPR.
101 | * Add a new boolean configuration variable: FastMM_DebugMode_ScanForCorruptionBeforeEveryOperation. When this variable is True and debug mode is enabled, all debug blocks will be checked for corruption on entry to any memory manager operation (i.e. GetMem, FreeMem, AllocMem and ReallocMem). It is analogous to the v4 FullDebugModeScanMemoryPoolBeforeEveryOperation option. Note that this comes with an extreme performance penalty.
102 | * Add the FastMM_5Arenas through FastMM_16Arenas defines in order to allow control of the number of arenas through conditional defines instead of requiring editing of the FastMM5.pas source file. As a rule of thumb, FastMM performs optimally if the number of arenas is between 0.5x to 1x the number of threads that are expected to call the memory manager simultaneously.
103 | * Expose the FastMM_DetectStringData and FastMM_DetectClassInstance functions, which are used to determine whether a pointer potentially points to string data or a class instance. These may be useful inside the FastMM_WalkBlocks callback in order to collect more detailed statistics about the memory pool content.
104 | * Add support for a "FastMM_DebugLibraryStaticDependency" define, which is automatically defined if the legacy "FullDebugMode" option is defined. When "FastMM_DebugLibraryStaticDependency" is defined the application will have a static dependency on the debug support library. This prevents the premature unloading of the debug support library (and crash on shutdown) when the memory manager is shared between the main application and a statically linked library.
105 | * Add FastMM_GetCurrentMemoryUsage, FastMM_SetMemoryUsageLimit and FastMM_GetMemoryUsageLimit calls. FastMM_GetCurrentMemoryUsage returns the number of bytes of address space that is currently either committed or reserved by FastMM. This includes the total used by the heap, as well as all internal management structures. FastMM_SetMemoryUsageLimit allows the application to specify a maximum amount of memory that may be allocated through FastMM. An attempt to allocate more than this amount will fail and lead to an "Out of Memory" exception. Note that after the first failure the maximum amount of memory that may be allocated is slightly increased in order to allow the application to allocate some additional memory in subsequent attempts. This is to allow for a graceful shutdown. Specify 0 for no limit. FastMM_GetMemoryUsageLimit returns the current limit in effect. 0 = no limit (the default).
106 |
107 | ##### Version 5.04
108 | * Implement a return address info cache for the LogStackTrace call in FastMM_FullDebugMode. This greatly speeds up logging of memory leak detail to file when there are many leaks with the same (or similar) stack traces.
109 | * Avoid opening and reopening the event log file multiple times when logging leak detail. This improves performance significantly when logging multiple memory leaks.
110 | * Ensure that the event log file is closed before showing any dialogs, so the user can access it while the dialog is displayed.
111 | * Implement several 32-bit SSE2 move routines (64-bit already used SSE2)
112 | * Make the static dependency on the FastMM_FullDebugMode library optional when FastMM_FullDebugMode is defined. When FastMM_DebugLibraryDynamicLoading (or LoadDebugDLLDynamically) is defined then the DLL will be loaded dynamically.
113 |
114 | ##### Version 5.05
115 | * Add the FastMM_AllocateTopDown (Boolean, default False) option. When True, allocates all memory from the top of the address space downward. This is useful to catch bad pointer typecasts in 64-bit code, where pointers would otherwise often fit in a 32-bit variable. Note that this comes with a performance impact in the other of O(n^2), where n is the number of chunks obtained from the OS.
116 | * Add support for Eurekalog 7 in FastMM_FullDebugMode.
117 | * Add FastMM_BeginEraseFreedBlockContent and FastMM_EndEraseFreedBlockContent calls. These calls enable/disable the erasure of the content of freed blocks. Calls may be nested, in which case erasure is only disabled when the number of FastMM_EndEraseFreedBlockContent calls equal the number of FastMM_BeginEraseFreedBlockContent calls. When enabled the content of all freed blocks is filled with the debug pattern $80808080 before being returned to the memory pool. This is useful for security purposes, and may also help catch "use after free" programming errors (like debug mode, but at reduced CPU cost).
118 | * Add support for the FastMM_IncludeLegacyOptionsFile define. If defined the legacy FastMM4Options.inc will be included (and the version 4 options will be translated to the equivalent v5 options).
119 | * Add the FastMM_NoDebugInfo option. If defined then debug info will not be emitted for FastMM5.pas, stopping the debugger from stepping into it.
120 | * Fix a race condition in FastMM_ScanDebugBlocksForCorruption that could erroneously report a debug block that is in the process of being freed by another thread as corrupted.
121 |
122 | ##### Version 5.06
123 | * Add lock timeout parameters to FastMM_ScanDebugBlocksForCorruption, FastMM_GetHeapStatus and FastMM_GetUsageSummary, with a default of 50ms. This prevents excessive wait times in these routines if another thread that holds a lock on a memory manager resource is currently suspended.
124 | * Make the LogStackTrace routine in FastMM_FullDebugMode thread safe.
125 | * Add FastMM_RequireIDEPresenceForLeakReporting like FastMM4 had to enable leak reporting in libraries.
126 | * Add an allocation group filter to FastMM_WalkBlocks and FastMM_LogStateToFile
127 | * Under 64-bit memory blocks are always at least 16 byte aligned, and under 32-bit they are always at least 8 byte aligned. Add a check in the debug FreeMem and ReallocMem routines to catch potentially bad pointers that break these rules.
128 | * Add a debug mode demo showing how to configure FastMM to detect and report memory corruption errors
129 | * Handle a potential race condition in FastMM_DetectClassInstance: If another thread frees a block while it is being evaluated as a potential class then an A/V could occur. This indirectly affects other functionality, like FastMM_LogStateToFile.
130 | * Fix several warnings with the options enabled to warn of potential data loss with implicit integer type conversions. These warnings were all false alarms, but now that the type conversions have been made explicit the warnings will no longer be emitted.
131 | * Add an option to sort the output of FastMM_LogStateToFile either in descending total memory usage (the default) or alphabetically.
132 | * Add the current timestamp to the FastMM_LogStateToFile output, and add an option not to truncate the file.
133 | * Change the parameters of FastMM_LogStateToFile from string to PWideChar to allow the calling code to avoid allocating strings (which may add clutter to the report). Reorder the parameters so that the order is more logical.
134 | * Add support to FastMM_LogStateToFile for logging a differential state between two allocation group ranges.
135 | * Include the relative allocation time when logging memory block details.
136 | * Report thread IDs in decimal (was previously hexadecimal).
137 | * Change allocation timestamps in crash logs to absolute times
138 | * Include milliseconds in block logging timestamps
139 | * Add FastMM_GetMemoryManagerState and FastMM_GetMemoryMap calls for backward compatibility with FastMM4
140 | * Port the usage tracker demo to FastMM5
141 | * Fix to large block thread contention handling: Previously large block thread contentions were not logged, and the thread would also not yield to other threads as intended.
142 | * Add support for changing the virtual method table offsets used to correctly identify objects in e.g. leak reports. This is useful when compiling a borlndmm.dll that will be used with an application that was compiled with a different version of Delphi where the VMT offsets differ.
143 | * Use the $90909090 debug fill pattern for allocated debug blocks in order to differentiate between errors involving uninitialized allocated memory and errors involving freed memory. Previously allocated debug blocks were initialized with the same $80808080 pattern as freed debug blocks.
144 | * Add FastMM_BeginEraseAllocatedBlockContent and FastMM_EndEraseAllocatedBlockContent. When enabled this will fill newly allocated blocks with the debug pattern, helping to catch bugs involving the use of uninitialized memory. This is a subset of the debug mode functionality, at a substantially reduced CPU cost.
145 | * Add a sanity check on the class name pointer in FastMM_DetectClassInstance as an additional safety net in order to prevent blocks from being misidentified as class instances in leak and state reports
146 | * Fix the failure to correctly report class names in leak reports, etc. when the class name pointer is not aligned to SizeOf(Pointer).
147 | * Add FastMM_DebugBreakAllocationNumber: Allows triggering of a break point in the debugger if the block with the specified allocation number is allocated.
148 |
--------------------------------------------------------------------------------