2006-04-15 03:44:05 -04:00
|
|
|
|
(*
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Fast Memory Manager 4.84
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
|
|
|
|
Description:
|
|
|
|
|
A fast replacement memory manager for Borland Delphi Win32 applications that
|
|
|
|
|
scales well under multi-threaded usage, is not prone to memory fragmentation,
|
|
|
|
|
and supports shared memory without the use of external .DLL files.
|
|
|
|
|
|
|
|
|
|
Homepage:
|
|
|
|
|
http://fastmm.sourceforge.net
|
|
|
|
|
|
|
|
|
|
Advantages:
|
|
|
|
|
- Fast
|
|
|
|
|
- Low overhead. FastMM is designed for an average of 5% and maximum of 10%
|
|
|
|
|
overhead per block.
|
|
|
|
|
- Supports up to 3GB of user mode address space under Windows 32-bit and 4GB
|
|
|
|
|
under Windows 64-bit. Add the "$SetPEFlags $20" option (in curly braces)
|
|
|
|
|
to your .dpr to enable this.
|
|
|
|
|
- Highly aligned memory blocks. Can be configured for either 8-byte or 16-byte
|
|
|
|
|
alignment.
|
|
|
|
|
- Good scaling under multi-threaded applications
|
|
|
|
|
- Intelligent reallocations. Avoids slow memory move operations through
|
|
|
|
|
not performing unneccesary downsizes and by having a minimum percentage
|
|
|
|
|
block size growth factor when an in-place block upsize is not possible.
|
|
|
|
|
- Resistant to address space fragmentation
|
|
|
|
|
- No external DLL required when sharing memory between the application and
|
|
|
|
|
external libraries (provided both use this memory manager)
|
|
|
|
|
- Optionally reports memory leaks on program shutdown. (This check can be set
|
|
|
|
|
to be performed only if Delphi is currently running on the machine, so end
|
|
|
|
|
users won't be bothered by the error message.)
|
|
|
|
|
- Supports Delphi 4 (or later), C++ Builder 5 (or later), Kylix 3.
|
|
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
|
Delphi:
|
|
|
|
|
Place this unit as the very first unit under the "uses" section in your
|
|
|
|
|
project's .dpr file. When sharing memory between an application and a DLL
|
|
|
|
|
(e.g. when passing a long string or dynamic array to a DLL function), both the
|
|
|
|
|
main application and the DLL must be compiled using this memory manager (with
|
|
|
|
|
the required conditional defines set). There are some conditional defines
|
|
|
|
|
(inside FastMM4Options.inc) that may be used to tweak the memory manager. To
|
|
|
|
|
enable support for a user mode address space greater than 2GB you will have to
|
|
|
|
|
use the EditBin* tool to set the LARGE_ADDRESS_AWARE flag in the EXE header.
|
|
|
|
|
This informs Windows x64 or Windows 32-bit (with the /3GB option set) that the
|
2008-07-25 09:23:00 -04:00
|
|
|
|
application supports an address space larger than 2GB (up to 4GB). In Delphi 6
|
2006-04-15 03:44:05 -04:00
|
|
|
|
and later you can also specify this flag through the compiler directive
|
|
|
|
|
{$SetPEFlags $20}
|
|
|
|
|
*The EditBin tool ships with the MS Visual C compiler.
|
|
|
|
|
C++ Builder 6:
|
|
|
|
|
Refer to the instructions inside FastMM4BCB.cpp.
|
|
|
|
|
|
|
|
|
|
License:
|
|
|
|
|
This work is copyright Professional Software Development / Pierre le Riche. It
|
|
|
|
|
is released under a dual license, and you may choose to use it under either the
|
|
|
|
|
Mozilla Public License 1.1 (MPL 1.1, available from
|
|
|
|
|
http://www.mozilla.org/MPL/MPL-1.1.html) or the GNU Lesser General Public
|
|
|
|
|
License 2.1 (LGPL 2.1, available from
|
|
|
|
|
http://www.opensource.org/licenses/lgpl-license.php). If you find FastMM useful
|
|
|
|
|
or you would like to support further development, a donation would be much
|
|
|
|
|
appreciated. My banking details are:
|
|
|
|
|
Country: South Africa
|
|
|
|
|
Bank: ABSA Bank Ltd
|
|
|
|
|
Branch: Somerset West
|
|
|
|
|
Branch Code: 334-712
|
|
|
|
|
Account Name: PSD (Distribution)
|
|
|
|
|
Account No.: 4041827693
|
|
|
|
|
Swift Code: ABSAZAJJ
|
|
|
|
|
My PayPal account is:
|
|
|
|
|
bof@psd.co.za
|
|
|
|
|
|
|
|
|
|
Contact Details:
|
|
|
|
|
My contact details are shown below if you would like to get in touch with me.
|
|
|
|
|
If you use this memory manager I would like to hear from you: please e-mail me
|
|
|
|
|
your comments - good and bad.
|
|
|
|
|
Snailmail:
|
|
|
|
|
PO Box 2514
|
|
|
|
|
Somerset West
|
|
|
|
|
7129
|
|
|
|
|
South Africa
|
|
|
|
|
E-mail:
|
|
|
|
|
plr@psd.co.za
|
|
|
|
|
|
|
|
|
|
Support:
|
|
|
|
|
If you have trouble using FastMM, you are welcome to drop me an e-mail at the
|
|
|
|
|
address above, or you may post your questions in the BASM newsgroup on the
|
|
|
|
|
Borland news server (which is where I hang out quite frequently).
|
|
|
|
|
|
|
|
|
|
Disclaimer:
|
|
|
|
|
FastMM has been tested extensively with both single and multithreaded
|
|
|
|
|
applications on various hardware platforms, but unfortunately I am not in a
|
|
|
|
|
position to make any guarantees. Use it at your own risk.
|
|
|
|
|
|
|
|
|
|
Acknowledgements (for version 4):
|
|
|
|
|
- Eric Grange for his RecyclerMM on which the earlier versions of FastMM were
|
|
|
|
|
based. RecyclerMM was what inspired me to try and write my own memory
|
|
|
|
|
manager back in early 2004.
|
2008-07-25 09:23:00 -04:00
|
|
|
|
- Primoz Gabrijelcic for helping to track down various bugs.
|
2006-04-15 03:44:05 -04:00
|
|
|
|
- Dennis Christensen for his tireless efforts with the Fastcode project:
|
|
|
|
|
helping to develop, optimize and debug the growing Fastcode library.
|
2008-07-25 09:23:00 -04:00
|
|
|
|
- JiYuan Xie for implementing the leak reporting code for C++ Builder.
|
2006-04-15 03:44:05 -04:00
|
|
|
|
- Pierre Y. for his suggestions regarding the extension of the memory leak
|
|
|
|
|
checking options.
|
2008-07-25 09:23:00 -04:00
|
|
|
|
- Hanspeter Widmer for his suggestion to have an option to display install and
|
|
|
|
|
uninstall debug messages and moving options to a separate file, as well as
|
|
|
|
|
the new usage tracker.
|
2006-04-15 03:44:05 -04:00
|
|
|
|
- Anders Isaksson and Greg for finding and identifying the "DelphiIsRunning"
|
|
|
|
|
bug under Delphi 5.
|
2008-07-25 09:23:00 -04:00
|
|
|
|
- Francois Malan for various suggestions and bug reports.
|
2006-04-15 03:44:05 -04:00
|
|
|
|
- Craig Peterson for helping me identify the cache associativity issues that
|
|
|
|
|
could arise due to medium blocks always being an exact multiple of 256 bytes.
|
|
|
|
|
Also for various other bug reports and enhancement suggestions.
|
|
|
|
|
- Jarek Karciarz, Vladimir Ulchenko (Vavan) and Bob Gonder for their help in
|
|
|
|
|
implementing the BCB support.
|
|
|
|
|
- Ben Taylor for his suggestion to display the object class of all memory
|
|
|
|
|
leaks.
|
|
|
|
|
- Jean Marc Eber and Vincent Mahon (the Memcheck guys) for the call stack
|
|
|
|
|
trace code and also the method used to catch virtual method calls on freed
|
|
|
|
|
objects.
|
|
|
|
|
- Nahan Hyn for the suggestion to be able to enable or disable memory leak
|
|
|
|
|
reporting through a global variable (the "ManualLeakReportingControl"
|
|
|
|
|
option.)
|
|
|
|
|
- Leonel Togniolli for various suggestions with regard to enhancing the bug
|
|
|
|
|
tracking features of FastMM and other helpful advice.
|
|
|
|
|
- Joe Bain and Leonel Togniolli for the workaround to QC#10922 affecting
|
|
|
|
|
compilation under Delphi 2005.
|
|
|
|
|
- Robert Marquardt for the suggestion to make localisation of FastMM easier by
|
|
|
|
|
having all string constants together.
|
|
|
|
|
- Simon Kissel and Fikret Hasovic for their help in implementing Kylix support.
|
|
|
|
|
- Matthias Thoma, Petr Vones, Robert Rossmair and the rest of the JCL team for
|
|
|
|
|
their debug info library used in the debug info support DLL and also the
|
|
|
|
|
code used to check for a valid call site in the "raw" stack trace code.
|
|
|
|
|
- Andreas Hausladen for the suggestion to use an external DLL to enable the
|
|
|
|
|
reporting of debug information.
|
|
|
|
|
- Alexander Tabakov for various good suggestions regarding the debugging
|
|
|
|
|
facilities of FastMM.
|
|
|
|
|
- M. Skloff for some useful suggestions and bringing to my attention some
|
|
|
|
|
compiler warnings.
|
|
|
|
|
- Martin Aignesberger for the code to use madExcept instead of the JCL library
|
|
|
|
|
inside the debug info support DLL.
|
|
|
|
|
- Diederik and Dennis Passmore for the suggestion to be able to register
|
|
|
|
|
expected leaks.
|
|
|
|
|
- Dario Tiraboschi and Mark Gebauer for pointing out the problems that occur
|
|
|
|
|
when range checking and complete boolean evaluation is turned on.
|
|
|
|
|
- Arthur Hoornweg for notifying me of the image base being incorrect for
|
|
|
|
|
borlndmm.dll.
|
|
|
|
|
- Theo Carr-Brion and Hanspeter Widmer for finding the false alarm error
|
|
|
|
|
message "Block Header Has Been Corrupted" bug in FullDebugMode.
|
|
|
|
|
- Danny Heijl for reporting the compiler error in "release" mode.
|
|
|
|
|
- Omar Zelaya for reporting the BCB support regression bug.
|
|
|
|
|
- Dan Miser for various good suggestions, e.g. not logging expected leaks to
|
|
|
|
|
file, enhancements the stack trace and messagebox functionality, etc.
|
|
|
|
|
- Arjen de Ruijter for fixing the bug in GetMemoryLeakType that caused it
|
|
|
|
|
to not properly detect expected leaks registered by class when in
|
|
|
|
|
"FullDebugMode".
|
|
|
|
|
- Aleksander Oven for reporting the installation problem when trying to use
|
|
|
|
|
FastMM in an application together with libraries that all use runtime
|
|
|
|
|
packages.
|
|
|
|
|
- Kristofer Skaug for reporting the bug that sometimes causes the leak report
|
|
|
|
|
to be shown, even when all the leaks have been registered as expected leaks.
|
|
|
|
|
Also for some useful enhancement suggestions.
|
|
|
|
|
- G<EFBFBD>nther Schoch for the "RequireDebuggerPresenceForLeakReporting" option.
|
|
|
|
|
- Jan Schl<EFBFBD>ter for the "ForceMMX" option.
|
|
|
|
|
- Hallvard Vassbotn for various good enhancement suggestions.
|
|
|
|
|
- Mark Edington for some good suggestions and bug reports.
|
|
|
|
|
- Paul Ishenin for reporting the compilation error when the NoMessageBoxes
|
|
|
|
|
option is set and also the missing call stack entries issue when "raw" stack
|
|
|
|
|
traces are enabled, as well as for the Russian translation.
|
|
|
|
|
- Cristian Nicola for reporting the compilation bug when the
|
|
|
|
|
CatchUseOfFreedInterfaces option was enabled (4.40).
|
|
|
|
|
- Mathias Rauen (madshi) for improving the support for madExcept in the debug
|
|
|
|
|
info support DLL.
|
|
|
|
|
- Roddy Pratt for the BCB5 support code.
|
2008-07-25 09:23:00 -04:00
|
|
|
|
- Rene Mihula for the Czech translation and the suggestion to have dynamic
|
|
|
|
|
loading of the FullDebugMode DLL as an option.
|
2006-04-15 03:44:05 -04:00
|
|
|
|
- Artur Redzko for the Polish translation.
|
|
|
|
|
- Bart van der Werf for helping me solve the DLL unload order problem when
|
2008-07-25 09:23:00 -04:00
|
|
|
|
using the debug mode borlndmm.dll library, as well as various other
|
|
|
|
|
suggestions.
|
2006-04-15 03:44:05 -04:00
|
|
|
|
- JRG ("The Delphi Guy") for the Spanish translation.
|
|
|
|
|
- Justus Janssen for Delphi 4 support.
|
|
|
|
|
- Vadim Lopushansky and Charles Vinal for reporting the Delphi 5 compile error
|
|
|
|
|
in version 4.50.
|
|
|
|
|
- Johni Jeferson Capeletto for the Brazilian Portuguese translation.
|
|
|
|
|
- Kurt Fitzner for reporting the BCb6 compiler error in 4.52.
|
|
|
|
|
- Michal Niklas for reporting the Kylix compiler error in 4.54.
|
|
|
|
|
- Thomas Speck and Uwe Queisser for German translations.
|
|
|
|
|
- Zaenal Mutaqin for the Indonesian translation.
|
|
|
|
|
- Carlos Macao for the Portuguese translation.
|
|
|
|
|
- Michael Winter for catching the performance issue when reallocating certain
|
|
|
|
|
block sizes.
|
|
|
|
|
- dzmitry[li] for the Belarussian translation.
|
|
|
|
|
- Marcelo Montenegro for the updated Spanish translation.
|
|
|
|
|
- Jud Cole for finding and reporting the bug which may trigger a read access
|
|
|
|
|
violation when upsizing certain small block sizes together with the
|
|
|
|
|
"UseCustomVariableSizeMoveRoutines" option.
|
|
|
|
|
- Zdenek Vasku for reporting and fixing the memory manager sharing bug
|
|
|
|
|
affecting Windows 95/98/Me.
|
2008-07-25 09:23:00 -04:00
|
|
|
|
- RB Winston for suggesting the improvement to GExperts "backup" support.
|
|
|
|
|
- Thomas Schulz for reporting the bug affecting large address space support
|
|
|
|
|
under FullDebugMode, as well as the recursive call bug when attempting to
|
|
|
|
|
report memory leaks when EnableMemoryLeakReporting is disabled.
|
|
|
|
|
- Luigi Sandon for the Italian translation.
|
|
|
|
|
- Werner Bochtler for various suggestions and bug reports.
|
|
|
|
|
- Markus Beth for suggesting the "NeverSleepOnThreadContention" option.
|
|
|
|
|
- JiYuan Xie for the Simplified Chinese translation.
|
|
|
|
|
- Andrey Shtukaturov for the updated Russian translation, as well as the
|
|
|
|
|
Ukrainian translation.
|
|
|
|
|
- Dimitry Timokhov for finding two elusive bugs in the memory leak class
|
|
|
|
|
detection code.
|
|
|
|
|
- Paulo Moreno for fixing the AllocMem bug in FullDebugMode that prevented
|
|
|
|
|
large blocks from being cleared.
|
|
|
|
|
- Vladimir Bochkarev for the suggestion to remove some unnecessary code if the
|
|
|
|
|
MM sharing mechanism is disabled.
|
|
|
|
|
- Loris Luise for the version constant suggestion.
|
|
|
|
|
- J.W. de Bokx for the MessageBox bugfix.
|
|
|
|
|
- Igor Lindunen for reporting the bug that caused the Align16Bytes option to
|
|
|
|
|
not work in FullDebugMode.
|
|
|
|
|
- Ionut Muntean for the Romanian translation.
|
|
|
|
|
- Florent Ouchet for the French translation.
|
|
|
|
|
- Marcus M<EFBFBD>nnig for the ScanMemoryPoolForCorruptions suggestion and the
|
|
|
|
|
suggestion to have the option to scan the memory pool before every
|
|
|
|
|
operation when in FullDebugMode.
|
|
|
|
|
- Craig Peterson for the SuppressMessageBoxes suggestion.
|
|
|
|
|
- Everyone who have made donations. Thanks!
|
2006-04-15 03:44:05 -04:00
|
|
|
|
- Any other Fastcoders or supporters that I have forgotten, and also everyone
|
|
|
|
|
that helped with the older versions.
|
|
|
|
|
|
|
|
|
|
Change log:
|
|
|
|
|
Version 1.00 (28 June 2004):
|
|
|
|
|
- First version (called PSDMemoryManager). Based on RecyclerMM (free block
|
|
|
|
|
stack approach) by Eric Grange.
|
|
|
|
|
Version 2.00 (3 November 2004):
|
|
|
|
|
- Complete redesign and rewrite from scratch. Name changed to FastMM to
|
|
|
|
|
reflect this fact. Uses a linked-list approach. Is faster, has less memory
|
|
|
|
|
overhead, and will now catch most bad pointers on FreeMem calls.
|
|
|
|
|
Version 3.00 (1 March 2005):
|
|
|
|
|
- Another rewrite. Reduced the memory overhead by: (a) not having a separate
|
|
|
|
|
memory area for the linked list of free blocks (uses space inside free
|
|
|
|
|
blocks themselves) (b) batch managers are allocated as part of chunks (c)
|
|
|
|
|
block size lookup table size reduced. This should make FastMM more CPU
|
|
|
|
|
cache friendly.
|
|
|
|
|
Version 4.00 (7 June 2005):
|
|
|
|
|
- Yet another rewrite. FastMM4 is in fact three memory managers in one: Small
|
|
|
|
|
blocks (up to a few KB) are managed through the binning model in the same
|
|
|
|
|
way as previous versions, medium blocks (from a few KB up to approximately
|
|
|
|
|
256K) are allocated in a linked-list fashion, and large blocks are grabbed
|
|
|
|
|
directly from the system through VirtualAlloc. This 3-layered design allows
|
|
|
|
|
very fast operation with the most frequently used block sizes (small
|
|
|
|
|
blocks), while also minimizing fragmentation and imparting significant
|
|
|
|
|
overhead savings with blocks larger than a few KB.
|
|
|
|
|
Version 4.01 (8 June 2005):
|
|
|
|
|
- Added the options "RequireDebugInfoForLeakReporting" and
|
|
|
|
|
"RequireIDEPresenceForLeakReporting" as suggested by Pierre Y.
|
|
|
|
|
- Fixed the "DelphiIsRunning" function not working under Delphi 5, and
|
|
|
|
|
consequently no leak checking. (Reported by Anders Isaksson and Greg.)
|
|
|
|
|
Version 4.02 (8 June 2005):
|
|
|
|
|
- Fixed the compilation error when both the "AssumeMultiThreaded" and
|
|
|
|
|
"CheckHeapForCorruption options were set. (Reported by Francois Malan.)
|
|
|
|
|
Version 4.03 (9 June 2005):
|
|
|
|
|
- Added descriptive error messages when FastMM4 cannot be installed because
|
|
|
|
|
another MM has already been installed or memory has already been allocated.
|
|
|
|
|
Version 4.04 (13 June 2005):
|
|
|
|
|
- Added a small fixed offset to the size of medium blocks (previously always
|
|
|
|
|
exact multiples of 256 bytes). This makes performance problems due to CPU
|
|
|
|
|
cache associativity limitations much less likely. (Reported by Craig
|
|
|
|
|
Peterson.)
|
|
|
|
|
Version 4.05 (17 June 2005):
|
|
|
|
|
- Added the Align16Bytes option. Disable this option to drop the 16 byte
|
|
|
|
|
alignment restriction and reduce alignment to 8 bytes for the smallest
|
|
|
|
|
block sizes. Disabling Align16Bytes should lower memory consumption at the
|
|
|
|
|
cost of complicating the use of aligned SSE move instructions. (Suggested
|
|
|
|
|
by Craig Peterson.)
|
|
|
|
|
- Added a support unit for C++ Builder 6 - Add FastMM4BCB.cpp and
|
|
|
|
|
FastMM4.pas to your BCB project to use FastMM instead of the RTL MM. Memory
|
|
|
|
|
leak checking is not supported because (unfortunately) once an MM is
|
|
|
|
|
installed under BCB you cannot uninstall it... at least not without
|
|
|
|
|
modifying the RTL code in exit.c or patching the RTL code runtime. (Thanks
|
|
|
|
|
to Jarek Karciarz, Vladimir Ulchenko and Bob Gonder.)
|
|
|
|
|
Version 4.06 (22 June 2005):
|
|
|
|
|
- Displays the class of all leaked objects on the memory leak report and also
|
|
|
|
|
tries to identify leaked long strings. Previously it only displayed the
|
|
|
|
|
sizes of all leaked blocks. (Suggested by Ben Taylor.)
|
|
|
|
|
- Added support for displaying the sizes of medium and large block memory
|
|
|
|
|
leaks. Previously it only displayed details for small block leaks.
|
|
|
|
|
Version 4.07 (22 June 2005):
|
|
|
|
|
- Fixed the detection of the class of leaked objects not working under
|
|
|
|
|
Windows 98/Me.
|
|
|
|
|
Version 4.08 (27 June 2005):
|
|
|
|
|
- Added a BorlndMM.dpr project to allow you to build a borlndmm.dll that uses
|
|
|
|
|
FastMM4 instead of the default memory manager. You may replace the old
|
|
|
|
|
DLL in the Delphi \Bin directory to make the IDE use this memory manager
|
|
|
|
|
instead.
|
|
|
|
|
Version 4.09 (30 June 2005):
|
|
|
|
|
- Included a patch fix for the bug affecting replacement borlndmm.dll files
|
|
|
|
|
with Delphi 2005 (QC#14007). Compile the patch, close Delphi, and run it
|
|
|
|
|
once to patch your vclide90.bpl. You will now be able to use the
|
|
|
|
|
replacement borlndmm.dll to speed up the Delphi 2005 IDE as well.
|
|
|
|
|
Version 4.10 (7 July 2005):
|
|
|
|
|
- Due to QC#14070 ("Delphi IDE attempts to free memory after the shutdown
|
|
|
|
|
code of borlndmm.dll has been called"), FastMM cannot be uninstalled
|
|
|
|
|
safely when used inside a replacement borlndmm.dll for the IDE. Added a
|
|
|
|
|
conditional define "NeverUninstall" for this purpose.
|
|
|
|
|
- Added the "FullDebugMode" option to pad all blocks with a header and footer
|
|
|
|
|
to help you catch memory overwrite bugs in your applications. All blocks
|
|
|
|
|
returned to freemem are also zeroed out to help catch bugs involving the
|
|
|
|
|
use of previously freed blocks. Also catches attempts at calling virtual
|
|
|
|
|
methods of freed objects provided the block in question has not been reused
|
|
|
|
|
since the object was freed. Displays stack traces on error to aid debugging.
|
|
|
|
|
- Added the "LogErrorsToFile" option to log all errors to a text file in the
|
|
|
|
|
same folder as the application.
|
|
|
|
|
- Added the "ManualLeakReportingControl" option (suggested by Nahan Hyn) to
|
|
|
|
|
enable control over whether the memory leak report should be done or not
|
|
|
|
|
via a global variable.
|
|
|
|
|
Version 4.11 (7 July 2005):
|
|
|
|
|
- Fixed a compilation error under Delphi 2005 due to QC#10922. (Thanks to Joe
|
|
|
|
|
Bain and Leonel Togniolli.)
|
|
|
|
|
- Fixed leaked object classes not displaying in the leak report in
|
|
|
|
|
"FullDebugMode".
|
|
|
|
|
Version 4.12 (8 July 2005):
|
|
|
|
|
- Moved all the string constants to one place to make it easier to do
|
|
|
|
|
translations into other languages. (Thanks to Robert Marquardt.)
|
|
|
|
|
- Added support for Kylix. Some functionality is currently missing: No
|
|
|
|
|
support for detecting the object class on leaks and also no MM sharing.
|
|
|
|
|
(Thanks to Simon Kissel and Fikret Hasovic).
|
|
|
|
|
Version 4.13 (11 July 2005):
|
|
|
|
|
- Added the FastMM_DebugInfo.dll support library to display debug info for
|
|
|
|
|
stack traces.
|
|
|
|
|
- Stack traces for the memory leak report is now logged to the log file in
|
|
|
|
|
"FullDebugMode".
|
|
|
|
|
Version 4.14 (14 July 2005):
|
|
|
|
|
- Fixed string leaks not being detected as such in "FullDebugMode". (Thanks
|
|
|
|
|
to Leonel Togniolli.)
|
|
|
|
|
- Fixed the compilation error in "FullDebugMode" when "LogErrorsToFile" is
|
|
|
|
|
not set. (Thanks to Leonel Togniolli.)
|
|
|
|
|
- Added a "Release" option to allow the grouping of various options and to
|
|
|
|
|
make it easier to make debug and release builds. (Thanks to Alexander
|
|
|
|
|
Tabakov.)
|
|
|
|
|
- Added a "HideMemoryLeakHintMessage" option to not display the hint below
|
|
|
|
|
the memory leak message. (Thanks to Alexander Tabakov.)
|
|
|
|
|
- Changed the fill character for "FullDebugMode" from zero to $80 to be able
|
|
|
|
|
to differentiate between invalid memory accesses using nil pointers to
|
|
|
|
|
invalid memory accesses using fields of freed objects. FastMM tries to
|
|
|
|
|
reserve the 64K block starting at $80800000 at startup to ensure that an
|
|
|
|
|
A/V will occur when this block is accessed. (Thanks to Alexander Tabakov.)
|
|
|
|
|
- Fixed some compiler warnings. (Thanks to M. Skloff)
|
|
|
|
|
- Fixed some display bugs in the memory leak report. (Thanks to Leonel
|
|
|
|
|
Togniolli.)
|
|
|
|
|
- Added a "LogMemoryLeakDetailToFile" option. Some applications leak a lot of
|
|
|
|
|
memory and can make the log file grow very large very quickly.
|
|
|
|
|
- Added the option to use madExcept instead of the JCL Debug library in the
|
|
|
|
|
debug info support DLL. (Thanks to Martin Aignesberger.)
|
|
|
|
|
- Added procedures "GetMemoryManagerState" and "GetMemoryMap" to retrieve
|
|
|
|
|
statistics about the current state of the memory manager and memory pool.
|
|
|
|
|
(A usage tracker form together with a demo is also available.)
|
|
|
|
|
Version 4.15 (14 July 2005):
|
|
|
|
|
- Fixed a false 4GB(!) memory leak reported in some instances.
|
|
|
|
|
Version 4.16 (15 July 2005):
|
|
|
|
|
- Added the "CatchUseOfFreedInterfaces" option to catch the use of interfaces
|
|
|
|
|
of freed objects. This option is not compatible with checking that a freed
|
|
|
|
|
block has not been modified, so enable this option only when hunting an
|
|
|
|
|
invalid interface reference. (Only relevant if "FullDebugMode" is set.)
|
|
|
|
|
- During shutdown FastMM now checks that all free blocks have not been
|
|
|
|
|
modified since being freed. (Only when "FullDebugMode" is set and
|
|
|
|
|
"CatchUseOfFreedInterfaces" is disabled.)
|
|
|
|
|
Version 4.17 (15 July 2005):
|
|
|
|
|
- Added the AddExpectedMemoryLeaks and RemoveExpectedMemoryLeaks procedures to
|
|
|
|
|
register/unregister expected leaks, thus preventing the leak report from
|
|
|
|
|
displaying if only expected leaks occurred. (Thanks to Diederik and Dennis
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Passmore for the suggestion.) (Note: these functions were renamed in later
|
|
|
|
|
versions.)
|
2006-04-15 03:44:05 -04:00
|
|
|
|
- Fixed the "LogMemoryLeakDetailToFile" not logging memory leak detail to file
|
|
|
|
|
as it is supposed to. (Thanks to Leonel Togniolli.)
|
|
|
|
|
Version 4.18 (18 July 2005):
|
|
|
|
|
- Fixed some issues when range checking or complete boolean evaluation is
|
|
|
|
|
switched on. (Thanks to Dario Tiraboschi and Mark Gebauer.)
|
|
|
|
|
- Added the "OutputInstallUninstallDebugString" option to display a message when
|
|
|
|
|
FastMM is installed or uninstalled. (Thanks to Hanspeter Widmer.)
|
|
|
|
|
- Moved the options to a separate include file. (Thanks to Hanspeter Widmer.)
|
|
|
|
|
- Moved message strings to a separate file for easy translation.
|
|
|
|
|
Version 4.19 (19 July 2005):
|
|
|
|
|
- Fixed Kylix support that was broken in 4.14.
|
|
|
|
|
Version 4.20 (20 July 2005):
|
|
|
|
|
- Fixed a false memory overwrite report at shutdown in "FullDebugMode". If you
|
|
|
|
|
consistently got a "Block Header Has Been Corrupted" error message during
|
|
|
|
|
shutdown at address $xxxx0070 then it was probably a false alarm. (Thanks to
|
|
|
|
|
Theo Carr-Brion and Hanspeter Widmer.}
|
|
|
|
|
Version 4.21 (27 July 2005):
|
|
|
|
|
- Minor change to the block header flags to make it possible to immediately
|
|
|
|
|
tell whether a medium block is being used as a small block pool or not.
|
|
|
|
|
(Simplifies the leak checking and status reporting code.)
|
|
|
|
|
- Expanded the functionality around the management of expected memory leaks.
|
|
|
|
|
- Added the "ClearLogFileOnStartup" option. Deletes the log file during
|
|
|
|
|
initialization. (Thanks to M. Skloff.)
|
|
|
|
|
- Changed "OutputInstallUninstallDebugString" to use OutputDebugString instead
|
|
|
|
|
of MessageBox. (Thanks to Hanspeter Widmer.)
|
|
|
|
|
Version 4.22 (1 August 2005):
|
|
|
|
|
- Added a FastAllocMem function that avoids an unnecessary FillChar call with
|
|
|
|
|
large blocks.
|
|
|
|
|
- Changed large block resizing behavior to be a bit more conservative. Large
|
|
|
|
|
blocks will be downsized if the new size is less than half of the old size
|
|
|
|
|
(the threshold was a quarter previously).
|
|
|
|
|
Version 4.23 (6 August 2005):
|
|
|
|
|
- Fixed BCB6 support (Thanks to Omar Zelaya).
|
|
|
|
|
- Renamed "OutputInstallUninstallDebugString" to "UseOutputDebugString", and
|
|
|
|
|
added debug string output on memory leak or error detection.
|
|
|
|
|
Version 4.24 (11 August 2005):
|
|
|
|
|
- Added the "NoMessageBoxes" option to suppress the display of message boxes,
|
|
|
|
|
which is useful for services that should not be interrupted. (Thanks to Dan
|
|
|
|
|
Miser).
|
|
|
|
|
- Changed the stack trace code to return the line number of the caller and not
|
|
|
|
|
the line number of the return address. (Thanks to Dan Miser).
|
|
|
|
|
Version 4.25 (15 August 2005):
|
|
|
|
|
- Fixed GetMemoryLeakType not detecting expected leaks registered by class
|
|
|
|
|
when in "FullDebugMode". (Thanks to Arjen de Ruijter).
|
|
|
|
|
Version 4.26 (18 August 2005):
|
|
|
|
|
- Added a "UseRuntimePackages" option that allows FastMM to be used in a main
|
|
|
|
|
application together with DLLs that all use runtime packages. (Thanks to
|
|
|
|
|
Aleksander Oven.)
|
|
|
|
|
Version 4.27 (24 August 2005):
|
|
|
|
|
- Fixed a bug that sometimes caused the leak report to be shown even though all
|
|
|
|
|
leaks were registered as expected leaks. (Thanks to Kristofer Skaug.)
|
|
|
|
|
Version 4.29 (30 September 2005):
|
|
|
|
|
- Added the "RequireDebuggerPresenceForLeakReporting" option to only display
|
|
|
|
|
the leak report if the application is run inside the IDE. (Thanks to G<EFBFBD>nther
|
|
|
|
|
Schoch.)
|
|
|
|
|
- Added the "ForceMMX" option, which when disabled will check the CPU for
|
|
|
|
|
MMX compatibility before using MMX. (Thanks to Jan Schl<EFBFBD>ter.)
|
|
|
|
|
- Added the module name to the title of error dialogs to more easily identify
|
|
|
|
|
which application caused the error. (Thanks to Kristofer Skaug.)
|
|
|
|
|
- Added an ASCII dump to the "FullDebugMode" memory dumps. (Thanks to Hallvard
|
|
|
|
|
Vassbotn.)
|
|
|
|
|
- Added the option "HideExpectedLeaksRegisteredByPointer" to suppress the
|
|
|
|
|
display and logging of expected memory leaks that were registered by pointer.
|
|
|
|
|
(Thanks to Dan Miser.) Leaks registered by size or class are often ambiguous,
|
|
|
|
|
so these expected leaks are always logged to file (in FullDebugMode) and are
|
|
|
|
|
never hidden from the leak display (only displayed if there is at least one
|
|
|
|
|
unexpected leak).
|
|
|
|
|
- Added a procedure "GetRegisteredMemoryLeaks" to return a list of all
|
|
|
|
|
registered memory leaks. (Thanks to Dan Miser.)
|
|
|
|
|
- Added the "RawStackTraces" option to perform "raw" stack traces, negating
|
|
|
|
|
the need for stack frames. This will usually result in more complete stack
|
|
|
|
|
traces in FullDebugMode error reports, but it is significantly slower.
|
|
|
|
|
(Thanks to Hallvard Vassbotn, Dan Miser and the JCL team.)
|
|
|
|
|
Version 4.31 (2 October 2005):
|
|
|
|
|
- Fixed the crash bug when both "RawStackTraces" and "FullDebugMode" were
|
|
|
|
|
enabled. (Thanks to Dan Miser and Mark Edington.)
|
|
|
|
|
Version 4.33 (6 October 2005):
|
|
|
|
|
- Added a header corruption check to all memory blocks that are identified as
|
|
|
|
|
leaks in FullDebugMode. This allows better differentiation between memory
|
|
|
|
|
pool corruption bugs and actual memory leaks.
|
|
|
|
|
- Fixed the stack overflow bug when using "RawStackTraces".
|
|
|
|
|
Version 4.35 (6 October 2005):
|
|
|
|
|
- Fixed a compilation error when the "NoMessageBoxes" option is set. (Thanks
|
|
|
|
|
to Paul Ishenin.)
|
|
|
|
|
- Before performing a "raw" stack trace, FastMM now checks whether exception
|
|
|
|
|
handling is in place. If exception handling is not in place FastMM falls
|
|
|
|
|
back to stack frame tracing. (Exception handling is required to handle the
|
|
|
|
|
possible A/Vs when reading invalid call addresses. Exception handling is
|
|
|
|
|
usually always available except when SysUtils hasn't been initialized yet or
|
|
|
|
|
after SysUtils has been finalized.)
|
|
|
|
|
Version 4.37 (8 October 2005):
|
|
|
|
|
- Fixed the missing call stack trace entry issue when dynamically loading DLLs.
|
|
|
|
|
(Thanks to Paul Ishenin.)
|
|
|
|
|
Version 4.39 (12 October 2005):
|
|
|
|
|
- Restored the performance with "RawStackTraces" enabled back to the level it
|
|
|
|
|
was in 4.35.
|
|
|
|
|
- Fixed the stack overflow error when using "RawStackTraces" that I thought I
|
|
|
|
|
had fixed in 4.31, but unfortunately didn't. (Thanks to Craig Peterson.)
|
|
|
|
|
Version 4.40 (13 October 2005):
|
|
|
|
|
- Improved "RawStackTraces" to have less incorrect extra entries. (Thanks to
|
|
|
|
|
Craig Peterson.)
|
|
|
|
|
- Added the Russian (by Paul Ishenin) and Afrikaans translations of
|
|
|
|
|
FastMM4Messages.pas.
|
|
|
|
|
Version 4.42 (13 October 2005):
|
|
|
|
|
- Fixed the compilation error when "CatchUseOfFreedInterfaces" is enabled.
|
|
|
|
|
(Thanks to Cristian Nicola.)
|
|
|
|
|
Version 4.44 (25 October 2005):
|
|
|
|
|
- Implemented a FastGetHeapStatus function in analogy with GetHeapStatus.
|
|
|
|
|
(Suggested by Cristian Nicola.)
|
|
|
|
|
- Shifted more of the stack trace code over to the support dll to allow third
|
|
|
|
|
party vendors to make available their own stack tracing and stack trace
|
|
|
|
|
logging facilities.
|
|
|
|
|
- Mathias Rauen (madshi) improved the support for madExcept in the debug info
|
|
|
|
|
support DLL. Thanks!
|
|
|
|
|
- Added support for BCB5. (Thanks to Roddy Pratt.)
|
|
|
|
|
- Added the Czech translation by Rene Mihula.
|
|
|
|
|
- Added the "DetectMMOperationsAfterUninstall" option. This will catch
|
|
|
|
|
attempts to use the MM after FastMM has been uninstalled, and is useful for
|
|
|
|
|
debugging.
|
|
|
|
|
Version 4.46 (26 October 2005):
|
|
|
|
|
- Renamed FastMM_DebugInfo.dll to FastMM_FullDebugMode.dll and made the
|
|
|
|
|
dependency on this library a static one. This solves a DLL unload order
|
|
|
|
|
problem when using FullDebugMode together with the replacement
|
|
|
|
|
borlndmm.dll. (Thanks to Bart van der Werf.)
|
|
|
|
|
- Added the Polish translation by Artur Redzko.
|
|
|
|
|
Version 4.48 (10 November 2005):
|
|
|
|
|
- Fixed class detection for objects leaked in dynamically loaded DLLs that
|
|
|
|
|
were relocated.
|
|
|
|
|
- Fabio Dell'Aria implemented support for EurekaLog in the FullDebugMode
|
|
|
|
|
support DLL. Thanks!
|
|
|
|
|
- Added the Spanish translation by JRG ("The Delphi Guy").
|
|
|
|
|
Version 4.49 (10 November 2005):
|
|
|
|
|
- Implemented support for installing replacement AllocMem and leak
|
|
|
|
|
registration mechanisms for Delphi/BCB versions that support it.
|
|
|
|
|
- Added support for Delphi 4. (Thanks to Justus Janssen.)
|
|
|
|
|
Version 4.50 (5 December 2005):
|
|
|
|
|
- Renamed the ReportMemoryLeaks global variable to ReportMemoryLeaksOnShutdown
|
|
|
|
|
to be more consistent with the Delphi 2006 memory manager.
|
|
|
|
|
- Improved the handling of large blocks. Large blocks can now consist of
|
|
|
|
|
several consecutive segments allocated through VirtualAlloc. This
|
|
|
|
|
significantly improves speed when frequently resizing large blocks, since
|
|
|
|
|
these blocks can now often be upsized in-place.
|
|
|
|
|
Version 4.52 (7 December 2005):
|
|
|
|
|
- Fixed the compilation error with Delphi 5. (Thanks to Vadim Lopushansky and
|
|
|
|
|
Charles Vinal for reporting the error.)
|
|
|
|
|
Version 4.54 (15 December 2005):
|
|
|
|
|
- Added the Brazilian Portuguese translation by Johni Jeferson Capeletto.
|
|
|
|
|
- Fixed the compilation error with BCB6. (Thanks to Kurt Fitzner.)
|
|
|
|
|
Version 4.56 (20 December 2005):
|
|
|
|
|
- Fixed the Kylix compilation problem. (Thanks to Michal Niklas.)
|
|
|
|
|
Version 4.58 (1 February 2006):
|
|
|
|
|
- Added the German translations by Thomas Speck and Uwe Queisser.
|
|
|
|
|
- Added the Indonesian translation by Zaenal Mutaqin.
|
|
|
|
|
- Added the Portuguese translation by Carlos Macao.
|
|
|
|
|
Version 4.60 (21 February 2006):
|
|
|
|
|
- Fixed a performance issue due to an unnecessary block move operation when
|
|
|
|
|
allocating a block in the range 1261-1372 bytes and then reallocating it in
|
|
|
|
|
the range 1373-1429 bytes twice. (Thanks to Michael Winter.)
|
|
|
|
|
- Added the Belarussian translation by dzmitry[li].
|
|
|
|
|
- Added the updated Spanish translation by Marcelo Montenegro.
|
|
|
|
|
- Added a new option "EnableSharingWithDefaultMM". This option allows FastMM
|
|
|
|
|
to be shared with the default MM of Delphi 2006. It is on by default, but
|
|
|
|
|
MM sharing has to be enabled otherwise it has no effect (refer to the
|
|
|
|
|
documentation for the "ShareMM" and "AttemptToUseSharedMM" options).
|
|
|
|
|
Version 4.62 (22 February 2006):
|
|
|
|
|
- Fixed a possible read access violation in the MoveX16L4 routine when the
|
|
|
|
|
UseCustomVariableSizeMoveRoutines option is enabled. (Thanks to Jud Cole for
|
|
|
|
|
some great detective work in finding this bug.)
|
|
|
|
|
- Improved the downsizing behaviour of medium blocks to better correlate with
|
|
|
|
|
the reallocation behaviour of small blocks. This change reduces the number
|
|
|
|
|
of transitions between small and medium block types when reallocating blocks
|
|
|
|
|
in the 0.7K to 2.6K range. It cuts down on the number of memory move
|
|
|
|
|
operations and improves performance.
|
|
|
|
|
Version 4.64 (31 March 2006):
|
|
|
|
|
- Added the following functions for use with FullDebugMode (and added the
|
|
|
|
|
exports to the replacement BorlndMM.dll): SetMMLogFileName,
|
|
|
|
|
GetCurrentAllocationGroup, PushAllocationGroup, PopAllocationGroup and
|
|
|
|
|
LogAllocatedBlocksToFile. The purpose of these functions are to allow you to
|
|
|
|
|
identify and log related memory leaks while your application is still
|
|
|
|
|
running.
|
|
|
|
|
- Fixed a bug in the memory manager sharing mechanism affecting Windows
|
|
|
|
|
95/98/ME. (Thanks to Zdenek Vasku.)
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Version 4.66 (9 May 2006):
|
|
|
|
|
- Added a hint comment in this file so that FastMM4Messages.pas will also be
|
|
|
|
|
backed up by GExperts. (Thanks to RB Winston.)
|
|
|
|
|
- Fixed a bug affecting large address space (> 2GB) support under
|
|
|
|
|
FullDebugMode. (Thanks to Thomas Schulz.)
|
|
|
|
|
Version 4.68 (3 July 2006):
|
|
|
|
|
- Added the Italian translation by Luigi Sandon.
|
|
|
|
|
- If FastMM is used inside a DLL it will now use the name of the DLL as base
|
|
|
|
|
for the log file name. (Previously it always used the name of the main
|
|
|
|
|
application executable file.)
|
|
|
|
|
- Fixed a rare A/V when both the FullDebugMode and RawStackTraces options were
|
|
|
|
|
enabled. (Thanks to Primoz Gabrijelcic.)
|
|
|
|
|
- Added the "NeverSleepOnThreadContention" option. This option may improve
|
|
|
|
|
performance if the ratio of the the number of active threads to the number
|
|
|
|
|
of CPU cores is low (typically < 2). This option is only useful for 4+ CPU
|
|
|
|
|
systems, it almost always hurts performance on single and dual CPU systems.
|
|
|
|
|
(Thanks to Werner Bochtler and Markus Beth.)
|
|
|
|
|
Version 4.70 (4 August 2006):
|
|
|
|
|
- Added the Simplified Chinese translation by JiYuan Xie.
|
|
|
|
|
- Added the updated Russian as well as the Ukrainian translation by Andrey
|
|
|
|
|
Shtukaturov.
|
|
|
|
|
- Fixed two bugs in the leak class detection code that would sometimes fail
|
|
|
|
|
to detect the class of leaked objects and strings, and report them as
|
|
|
|
|
'unknown'. (Thanks to Dimitry Timokhov)
|
|
|
|
|
Version 4.72 (24 September 2006):
|
|
|
|
|
- Fixed a bug that caused AllocMem to not clear blocks > 256K in
|
|
|
|
|
FullDebugMode. (Thanks to Paulo Moreno.)
|
|
|
|
|
Version 4.74 (9 November 2006):
|
|
|
|
|
- Fixed a bug in the segmented large block functionality that could lead to
|
|
|
|
|
an application freeze when upsizing blocks greater than 256K in a
|
|
|
|
|
multithreaded application (one of those "what the heck was I thinking?"
|
|
|
|
|
type bugs).
|
|
|
|
|
Version 4.76 (12 January 2007):
|
|
|
|
|
- Changed the RawStackTraces code in the FullDebugMode DLL
|
|
|
|
|
to prevent it from modifying the Windows "GetLastError" error code.
|
|
|
|
|
(Thanks to Primoz Gabrijelcic.)
|
|
|
|
|
- Fixed a threading issue when the "CheckHeapForCorruption" option was
|
|
|
|
|
enabled, but the "FullDebugMode" option was disabled. (Thanks to Primoz
|
|
|
|
|
Gabrijelcic.)
|
|
|
|
|
- Removed some unnecessary startup code when the MM sharing mechanism is
|
|
|
|
|
disabled. (Thanks to Vladimir Bochkarev.)
|
|
|
|
|
- In FullDebugMode leaked blocks would sometimes be reported as belonging to
|
|
|
|
|
the class "TFreedObject" if they were allocated but never used. Such blocks
|
|
|
|
|
will now be reported as "unknown". (Thanks to Francois Malan.)
|
|
|
|
|
- In recent versions the replacement borlndmm.dll created a log file (when
|
|
|
|
|
enabled) that used the "borlndmm" prefix instead of the application name.
|
|
|
|
|
It is now fixed to use the application name, however if FastMM is used
|
|
|
|
|
inside other DLLs the name of those DLLs will be used. (Thanks to Bart van
|
|
|
|
|
der Werf.)
|
|
|
|
|
- Added a "FastMMVersion" constant. (Suggested by Loris Luise.)
|
|
|
|
|
- Fixed an issue with error message boxes not displaying under certain
|
|
|
|
|
configurations. (Thanks to J.W. de Bokx.)
|
|
|
|
|
- FastMM will now display only one error message at a time. If many errors
|
|
|
|
|
occur in quick succession, only the first error will be shown (but all will
|
|
|
|
|
be logged). This avoids a stack overflow with badly misbehaved programs.
|
|
|
|
|
(Thanks to Bart van der Werf.)
|
|
|
|
|
- Added a LoadDebugDLLDynamically option to be used in conjunction with
|
|
|
|
|
FullDebugMode. In this mode FastMM_FullDebugMode.dll is loaded dynamically.
|
|
|
|
|
If the DLL cannot be found, stack traces will not be available. (Thanks to
|
|
|
|
|
Rene Mihula.)
|
|
|
|
|
Version 4.78 (1 March 2007):
|
|
|
|
|
- The MB_DEFAULT_DESKTOP_ONLY constant that is used when displaying messages
|
|
|
|
|
boxes since 4.76 is not defined under Kylix, and the source would thus not
|
|
|
|
|
compile. That constant is now defined. (Thanks to Werner Bochtler.)
|
|
|
|
|
- Moved the medium block locking code that was duplicated in several places
|
|
|
|
|
to a subroutine to reduce code size. (Thanks to Hallvard Vassbotn.)
|
|
|
|
|
- Fixed a bug in the leak registration code that sometimes caused registered
|
|
|
|
|
leaks to be reported erroneously. (Thanks to Primoz Gabrijelcic.)
|
|
|
|
|
- Added the NoDebugInfo option (on by default) that suppresses the generation
|
|
|
|
|
of debug info for the FastMM4.pas unit. This will prevent the integrated
|
|
|
|
|
debugger from stepping into the memory manager. (Thanks to Primoz
|
|
|
|
|
Gabrijelcic.)
|
|
|
|
|
- Increased the default stack trace depth in FullDebugMode from 9 to 10 to
|
|
|
|
|
ensure that the Align16Bytes setting works in FullDebugMode. (Thanks to
|
|
|
|
|
Igor Lindunen.)
|
|
|
|
|
- Updated the Czech translation. (Thanks to Rene Mihula.)
|
|
|
|
|
Version 4.84 (7 July 2008):
|
|
|
|
|
- Added the Romanian translation. (Thanks to Ionut Muntean.)
|
|
|
|
|
- Optimized the GetMemoryMap procedure to improve speed.
|
|
|
|
|
- Added the GetMemoryManagerUsageSummary function that returns a summary of
|
|
|
|
|
the GetMemoryManagerState call. (Thanks to Hallvard Vassbotn.)
|
|
|
|
|
- Added the French translation. (Thanks to Florent Ouchet.)
|
|
|
|
|
- Added the "AlwaysAllocateTopDown" FullDebugMode option to help with
|
|
|
|
|
catching bad pointer arithmetic code in an address space > 2GB. This option
|
|
|
|
|
is enabled by default.
|
|
|
|
|
- Added the "InstallOnlyIfRunningInIDE" option. Enable this option to
|
|
|
|
|
only install FastMM as the memory manager when the application is run
|
|
|
|
|
inside the Delphi IDE. This is useful when you want to deploy the same EXE
|
|
|
|
|
that you use for testing, but only want the debugging features active on
|
|
|
|
|
development machines. When this option is enabled and the application is
|
|
|
|
|
not being run inside the IDE, then the default Delphi memory manager will
|
|
|
|
|
be used (which, since Delphi 2006, is FastMM without FullDebugMode.) This
|
|
|
|
|
option is off by default.
|
|
|
|
|
- Added the "FullDebugModeInIDE" option. This is a convenient shorthand for
|
|
|
|
|
enabling FullDebugMode, InstallOnlyIfRunningInIDE and
|
|
|
|
|
LoadDebugDLLDynamically. This causes FastMM to be used in FullDebugMode
|
|
|
|
|
when the application is being debugged on development machines, and the
|
|
|
|
|
default memory manager when the same executable is deployed. This allows
|
|
|
|
|
the debugging and deployment of an application without having to compile
|
|
|
|
|
separate executables. This option is off by default.
|
|
|
|
|
- Added a ScanMemoryPoolForCorruptions procedure that checks the entire
|
|
|
|
|
memory pool for corruptions and raises an exception if one is found. It can
|
|
|
|
|
be called at any time, but is only available in FullDebugMode. (Thanks to
|
|
|
|
|
Marcus M<EFBFBD>nnig.)
|
|
|
|
|
- Added a global variable "FullDebugModeScanMemoryPoolBeforeEveryOperation".
|
|
|
|
|
When this variable is set to true and FullDebugMode is enabled, then the
|
|
|
|
|
entire memory pool is checked for consistency before every GetMem, FreeMem
|
|
|
|
|
and ReallocMem operation. An "Out of Memory" error is raised if a
|
|
|
|
|
corruption is found (and this variable is set to false to prevent recursive
|
|
|
|
|
errors). This obviously incurs a massive performance hit, so enable it only
|
|
|
|
|
when hunting for elusive memory corruption bugs. (Thanks to Marcus M<EFBFBD>nnig.)
|
|
|
|
|
- Fixed a bug in AllocMem that caused the FPU stack to be shifted by one
|
|
|
|
|
position.
|
|
|
|
|
- Changed the default for option "EnableMMX" to false, since using MMX may
|
|
|
|
|
cause unexpected behaviour in code that passes parameters on the FPU stack
|
|
|
|
|
(like some "compiler magic" routines, e.g. VarFromReal).
|
|
|
|
|
- Removed the "EnableSharingWithDefaultMM" option. This is now the default
|
|
|
|
|
behaviour and cannot be disabled. (FastMM will always try to share memory
|
|
|
|
|
managers between itself and the default memory manager when memory manager
|
|
|
|
|
sharing is enabled.)
|
|
|
|
|
- Introduced a new memory manager sharing mechanism based on memory mapped
|
|
|
|
|
files. This solves compatibility issues with console and service
|
|
|
|
|
applications. This sharing mechanism currently runs in parallel with the
|
|
|
|
|
old mechanism, but the old mechanism can be disabled by undefining
|
|
|
|
|
"EnableBackwardCompatibleMMSharing" in FastMM4Options.inc.
|
|
|
|
|
- Fixed the recursive call error when the EnableMemoryLeakReporting option
|
|
|
|
|
is disabled and an attempt is made to register a memory leak under Delphi
|
|
|
|
|
2006 or later. (Thanks to Thomas Schulz.)
|
|
|
|
|
- Added a global variable "SuppressMessageBoxes" to enable or disable
|
|
|
|
|
messageboxes at runtime. (Thanks to Craig Peterson.)
|
|
|
|
|
- Added the leak reporting code for C++ Builder, as well as various other
|
|
|
|
|
C++ Builder bits written by JiYuan Xie. (Thank you!)
|
|
|
|
|
- Added the new Usage Tracker written by Hanspeter Widmer. (Thank you!)
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
|
|
|
|
*)
|
|
|
|
|
|
|
|
|
|
unit FastMM4;
|
|
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
|
|
{$Include FastMM4Options.inc}
|
|
|
|
|
|
|
|
|
|
{$RANGECHECKS OFF}
|
|
|
|
|
{$BOOLEVAL OFF}
|
|
|
|
|
{$OVERFLOWCHECKS OFF}
|
|
|
|
|
{$OPTIMIZATION ON}
|
|
|
|
|
{$TYPEDADDRESS OFF}
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{IDE debug mode always enables FullDebugMode and dynamic loading of the FullDebugMode DLL.}
|
|
|
|
|
{$ifdef FullDebugModeInIDE}
|
|
|
|
|
{$define InstallOnlyIfRunningInIDE}
|
|
|
|
|
{$define FullDebugMode}
|
|
|
|
|
{$define LoadDebugDLLDynamically}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Some features not currently supported under Kylix}
|
|
|
|
|
{$ifdef Linux}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$undef FullDebugMode}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$undef LogErrorsToFile}
|
|
|
|
|
{$undef LogMemoryLeakDetailToFile}
|
|
|
|
|
{$undef ShareMM}
|
|
|
|
|
{$undef AttemptToUseSharedMM}
|
|
|
|
|
{$undef RequireIDEPresenceForLeakReporting}
|
|
|
|
|
{$undef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef PIC}
|
|
|
|
|
{BASM version does not support position independent code}
|
|
|
|
|
{$undef ASMVersion}
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Do we require debug info for leak checking?}
|
|
|
|
|
{$ifdef RequireDebugInfoForLeakReporting}
|
|
|
|
|
{$ifopt D-}
|
|
|
|
|
{$undef EnableMemoryLeakReporting}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Enable heap checking and leak reporting in full debug mode}
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{$STACKFRAMES ON}
|
|
|
|
|
{$define CheckHeapForCorruption}
|
|
|
|
|
{$ifndef CatchUseOfFreedInterfaces}
|
|
|
|
|
{$define CheckUseOfFreedBlocksOnShutdown}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$else}
|
|
|
|
|
{Error logging requires FullDebugMode}
|
|
|
|
|
{$undef LogErrorsToFile}
|
|
|
|
|
{$undef CatchUseOfFreedInterfaces}
|
|
|
|
|
{$undef RawStackTraces}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$undef AlwaysAllocateTopDown}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Only the pascal version supports extended heap corruption checking.}
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{$undef ASMVersion}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifdef UseRuntimePackages}
|
|
|
|
|
{$define AssumeMultiThreaded}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Delphi versions}
|
|
|
|
|
{$ifndef BCB}
|
|
|
|
|
{$ifdef ver120}
|
|
|
|
|
{$define Delphi4or5}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef ver130}
|
|
|
|
|
{$define Delphi4or5}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef ver140}
|
|
|
|
|
{$define Delphi6}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef ver150}
|
|
|
|
|
{$define Delphi7}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef ver170}
|
|
|
|
|
{$define Delphi2005}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$else}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef PatchBCBTerminate}
|
|
|
|
|
{Cannot uninstall safely under BCB}
|
|
|
|
|
{$define NeverUninstall}
|
|
|
|
|
{Disable memory leak reporting}
|
|
|
|
|
{$undef EnableMemoryLeakReporting}
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{for BCB5, use the Delphi 5 codepath}
|
|
|
|
|
{$ifdef ver130}
|
|
|
|
|
{$define Delphi4or5}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef ver180}
|
|
|
|
|
{$define BDS2006}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifndef Delphi4or5}
|
|
|
|
|
{$ifndef BCB}
|
|
|
|
|
{$define Delphi6AndUp}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef Delphi6}
|
|
|
|
|
{$define BCB6OrDelphi7AndUp}
|
|
|
|
|
{$ifndef BCB}
|
|
|
|
|
{$define Delphi7AndUp}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef BCB}
|
|
|
|
|
{$ifndef Delphi7}
|
|
|
|
|
{$ifndef Delphi2005}
|
|
|
|
|
{$define BDS2006AndUp}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifdef Delphi6AndUp}
|
|
|
|
|
{$WARN SYMBOL_PLATFORM OFF}
|
|
|
|
|
{$WARN SYMBOL_DEPRECATED OFF}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Leak detail logging requires error logging}
|
|
|
|
|
{$ifndef LogErrorsToFile}
|
|
|
|
|
{$undef LogMemoryLeakDetailToFile}
|
|
|
|
|
{$undef ClearLogFileOnStartup}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifndef EnableMemoryLeakReporting}
|
|
|
|
|
{Manual leak reporting control requires leak reporting to be enabled}
|
|
|
|
|
{$undef ManualLeakReportingControl}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifndef EnableMMX}
|
|
|
|
|
{$undef ForceMMX}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Are any of the MM sharing options enabled?}
|
|
|
|
|
{$ifdef ShareMM}
|
|
|
|
|
{$define MMSharingEnabled}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef AttemptToUseSharedMM}
|
|
|
|
|
{$define MMSharingEnabled}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Instruct GExperts to back up the messages file as well.}
|
|
|
|
|
{#BACKUP FastMM4Messages.pas}
|
|
|
|
|
|
|
|
|
|
{Should debug info be disabled?}
|
|
|
|
|
{$ifdef NoDebugInfo}
|
|
|
|
|
{$DEBUGINFO OFF}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{-------------------------Public constants-----------------------------}
|
|
|
|
|
const
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{The current version of FastMM}
|
|
|
|
|
FastMMVersion = '4.84';
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{The number of small block types}
|
|
|
|
|
{$ifdef Align16Bytes}
|
|
|
|
|
NumSmallBlockTypes = 46;
|
|
|
|
|
{$else}
|
|
|
|
|
NumSmallBlockTypes = 55;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{----------------------------Public types------------------------------}
|
|
|
|
|
type
|
|
|
|
|
TSmallBlockTypeState = packed record
|
|
|
|
|
{The internal size of the block type}
|
|
|
|
|
InternalBlockSize: Cardinal;
|
|
|
|
|
{Useable block size: The number of non-reserved bytes inside the block.}
|
|
|
|
|
UseableBlockSize: Cardinal;
|
|
|
|
|
{The number of allocated blocks}
|
|
|
|
|
AllocatedBlockCount: Cardinal;
|
|
|
|
|
{The total address space reserved for this block type (both allocated and
|
|
|
|
|
free blocks)}
|
|
|
|
|
ReservedAddressSpace: Cardinal;
|
|
|
|
|
end;
|
|
|
|
|
TSmallBlockTypeStates = array[0..NumSmallBlockTypes - 1] of TSmallBlockTypeState;
|
|
|
|
|
|
|
|
|
|
TMemoryManagerState = packed record
|
|
|
|
|
{Small block type states}
|
|
|
|
|
SmallBlockTypeStates: TSmallBlockTypeStates;
|
|
|
|
|
{Medium block stats}
|
|
|
|
|
AllocatedMediumBlockCount: Cardinal;
|
|
|
|
|
TotalAllocatedMediumBlockSize: Cardinal;
|
|
|
|
|
ReservedMediumBlockAddressSpace: Cardinal;
|
|
|
|
|
{Large block stats}
|
|
|
|
|
AllocatedLargeBlockCount: Cardinal;
|
|
|
|
|
TotalAllocatedLargeBlockSize: Cardinal;
|
|
|
|
|
ReservedLargeBlockAddressSpace: Cardinal;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
TMemoryManagerUsageSummary = packed record
|
|
|
|
|
{The total number of bytes allocated by the application.}
|
|
|
|
|
AllocatedBytes: Cardinal;
|
|
|
|
|
{The total number of address space bytes used by control structures, or
|
|
|
|
|
lost due to fragmentation and other overhead.}
|
|
|
|
|
OverheadBytes: Cardinal;
|
|
|
|
|
{The efficiency of the memory manager expressed as a percentage. This is
|
|
|
|
|
100 * AllocatedBytes / (AllocatedBytes + OverheadBytes).}
|
|
|
|
|
EfficiencyPercentage: Double;
|
|
|
|
|
end;
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Memory map}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
TChunkStatus = (csUnallocated, csAllocated, csReserved, csSysAllocated,
|
|
|
|
|
csSysReserved);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
TMemoryMap = array[0..65535] of TChunkStatus;
|
|
|
|
|
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
{List of registered leaks}
|
|
|
|
|
TRegisteredMemoryLeak = packed record
|
|
|
|
|
LeakAddress: Pointer;
|
|
|
|
|
LeakedClass: TClass;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LeakedCppTypeIdPtr: Pointer;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LeakSize: Integer;
|
|
|
|
|
LeakCount: Integer;
|
|
|
|
|
end;
|
|
|
|
|
TRegisteredMemoryLeaks = array of TRegisteredMemoryLeak;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{--------------------------Public variables----------------------------}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
var
|
|
|
|
|
{If this variable is set to true and FullDebugMode is enabled, then the
|
|
|
|
|
entire memory pool is checked for consistency before every memory
|
|
|
|
|
operation. Note that this incurs a massive performance hit on top of
|
|
|
|
|
the already significant FullDebugMode overhead, so enable this option
|
|
|
|
|
only when absolutely necessary.}
|
|
|
|
|
FullDebugModeScanMemoryPoolBeforeEveryOperation: Boolean = False;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifdef ManualLeakReportingControl}
|
|
|
|
|
{Variable is declared in system.pas in newer Delphi versions.}
|
|
|
|
|
{$ifndef BDS2006AndUp}
|
|
|
|
|
ReportMemoryLeaksOnShutdown: Boolean;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{If set to true, disables the display of all messageboxes}
|
|
|
|
|
SuppressMessageBoxes: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
|
|
|
|
{-------------------------Public procedures----------------------------}
|
|
|
|
|
{Installation procedures must be exposed for the BCB helper unit FastMM4BCB.cpp}
|
|
|
|
|
{$ifdef BCB}
|
|
|
|
|
procedure InitializeMemoryManager;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function CheckCanInstallMemoryManager: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
procedure InstallMemoryManager;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
(*$HPPEMIT '#define FullDebugMode' *)
|
|
|
|
|
|
|
|
|
|
{$ifdef ClearLogFileOnStartup}
|
|
|
|
|
(*$HPPEMIT ' #define ClearLogFileOnStartup' *)
|
|
|
|
|
procedure DeleteEventLog;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifdef LoadDebugDLLDynamically}
|
|
|
|
|
(*$HPPEMIT ' #define LoadDebugDLLDynamically' *)
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifdef RawStackTraces}
|
|
|
|
|
(*$HPPEMIT ' #define RawStackTraces' *)
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifdef PatchBCBTerminate}
|
|
|
|
|
(*$HPPEMIT ''#13#10 *)
|
|
|
|
|
(*$HPPEMIT '#define PatchBCBTerminate' *)
|
|
|
|
|
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
(*$HPPEMIT ''#13#10 *)
|
|
|
|
|
(*$HPPEMIT '#define EnableMemoryLeakReporting' *)
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifdef DetectMMOperationsAfterUninstall}
|
|
|
|
|
(*$HPPEMIT ''#13#10 *)
|
|
|
|
|
(*$HPPEMIT '#define DetectMMOperationsAfterUninstall' *)
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Called in FastMM4BCB.cpp, should contain codes of original "finalization" section}
|
|
|
|
|
procedure FinalizeMemoryManager;
|
|
|
|
|
|
|
|
|
|
{For completion of "RequireDebuggerPresenceForLeakReporting" checking in "FinalizeMemoryManager"}
|
|
|
|
|
var
|
|
|
|
|
pCppDebugHook: PInteger = nil;
|
|
|
|
|
|
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
(*$HPPEMIT ''#13#10 *)
|
|
|
|
|
(*$HPPEMIT '#define CheckCppObjectTypeEnabled' *)
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
|
TGetCppVirtObjSizeByTypeIdPtrFunc = function(APointer: Pointer): Cardinal;
|
|
|
|
|
TGetCppVirtObjTypeIdPtrFunc = function(APointer: Pointer; ASize: Cardinal): Pointer;
|
|
|
|
|
TGetCppVirtObjTypeNameFunc = function(APointer: Pointer; ASize: Cardinal): PAnsiChar;
|
|
|
|
|
TGetCppVirtObjTypeNameByTypeIdPtrFunc = function (APointer: Pointer): PAnsiChar;
|
|
|
|
|
TGetCppVirtObjTypeNameByVTablePtrFunc = function(AVTablePtr: Pointer; AVTablePtrOffset: Cardinal): PAnsiChar;
|
|
|
|
|
var
|
|
|
|
|
{Return virtual object's size from typeId pointer}
|
|
|
|
|
GetCppVirtObjSizeByTypeIdPtrFunc: TGetCppVirtObjSizeByTypeIdPtrFunc = nil;
|
|
|
|
|
{Retrieve virtual object's typeId pointer}
|
|
|
|
|
GetCppVirtObjTypeIdPtrFunc: TGetCppVirtObjTypeIdPtrFunc = nil;
|
|
|
|
|
{Retrieve virtual object's type name}
|
|
|
|
|
GetCppVirtObjTypeNameFunc: TGetCppVirtObjTypeNameFunc = nil;
|
|
|
|
|
{Return virtual object's type name from typeId pointer}
|
|
|
|
|
GetCppVirtObjTypeNameByTypeIdPtrFunc: TGetCppVirtObjTypeNameByTypeIdPtrFunc = nil;
|
|
|
|
|
{Retrieve virtual object's typeId pointer from it's virtual table pointer}
|
|
|
|
|
GetCppVirtObjTypeNameByVTablePtrFunc: TGetCppVirtObjTypeNameByVTablePtrFunc = nil;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{The standard memory manager functions}
|
|
|
|
|
function FastGetMem(ASize: Integer): Pointer;
|
|
|
|
|
function FastFreeMem(APointer: Pointer): Integer;
|
|
|
|
|
function FastReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
|
|
|
|
|
function FastAllocMem(ASize: Cardinal): Pointer;
|
|
|
|
|
{$else}
|
|
|
|
|
{The FullDebugMode memory manager functions}
|
|
|
|
|
function DebugGetMem(ASize: Integer): Pointer;
|
|
|
|
|
function DebugFreeMem(APointer: Pointer): Integer;
|
|
|
|
|
function DebugReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
|
|
|
|
|
function DebugAllocMem(ASize: Cardinal): Pointer;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Scans the memory pool for any corruptions. If a corruption is encountered an "Out of Memory" exception is
|
|
|
|
|
raised.}
|
|
|
|
|
procedure ScanMemoryPoolForCorruptions;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Specify the full path and name for the filename to be used for logging memory
|
|
|
|
|
errors, etc. If ALogFileName is nil or points to an empty string it will
|
|
|
|
|
revert to the default log file name.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
procedure SetMMLogFileName(ALogFileName: PAnsiChar = nil);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Returns the current "allocation group". Whenever a GetMem request is serviced
|
|
|
|
|
in FullDebugMode, the current "allocation group" is stored in the block header.
|
|
|
|
|
This may help with debugging. Note that if a block is subsequently reallocated
|
|
|
|
|
that it keeps its original "allocation group" and "allocation number" (all
|
|
|
|
|
allocations are also numbered sequentially).}
|
|
|
|
|
function GetCurrentAllocationGroup: Cardinal;
|
|
|
|
|
{Allocation groups work in a stack like fashion. Group numbers are pushed onto
|
|
|
|
|
and popped off the stack. Note that the stack size is limited, so every push
|
|
|
|
|
should have a matching pop.}
|
|
|
|
|
procedure PushAllocationGroup(ANewCurrentAllocationGroup: Cardinal);
|
|
|
|
|
procedure PopAllocationGroup;
|
|
|
|
|
{Logs detail about currently allocated memory blocks for the specified range of
|
|
|
|
|
allocation groups. if ALastAllocationGroupToLog is less than
|
|
|
|
|
AFirstAllocationGroupToLog or it is zero, then all allocation groups are
|
|
|
|
|
logged. This routine also checks the memory pool for consistency at the same
|
2008-07-25 09:23:00 -04:00
|
|
|
|
time, raising an "Out of Memory" error if the check fails.}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
procedure LogAllocatedBlocksToFile(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Releases all allocated memory (use with extreme care)}
|
|
|
|
|
procedure FreeAllMemory;
|
|
|
|
|
|
|
|
|
|
{Returns summarised information about the state of the memory manager. (For
|
|
|
|
|
backward compatibility.)}
|
|
|
|
|
function FastGetHeapStatus: THeapStatus;
|
|
|
|
|
{Returns statistics about the current state of the memory manager}
|
|
|
|
|
procedure GetMemoryManagerState(var AMemoryManagerState: TMemoryManagerState);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Returns a summary of the information returned by GetMemoryManagerState}
|
|
|
|
|
procedure GetMemoryManagerUsageSummary(
|
|
|
|
|
var AMemoryManagerUsageSummary: TMemoryManagerUsageSummary);
|
|
|
|
|
{$ifndef Linux}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Gets the state of every 64K block in the 4GB address space}
|
|
|
|
|
procedure GetMemoryMap(var AMemoryMap: TMemoryMap);
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
{Registers expected memory leaks. Returns true on success. The list of leaked
|
|
|
|
|
blocks is limited, so failure is possible if the list is full.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
|
|
|
|
|
function RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
|
|
|
|
|
function RegisterExpectedMemoryLeak(ALeakedBlockSize: Integer; ACount: Integer = 1): Boolean; overload;
|
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
{Registers expected memory leaks by virtual object's typeId pointer.
|
|
|
|
|
Usage: RegisterExpectedMemoryLeak(typeid(ACppObject).tpp, Count);}
|
|
|
|
|
function RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer = 1): boolean; overload;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Removes expected memory leaks. Returns true on success.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
|
|
|
|
|
function UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
|
|
|
|
|
function UnregisterExpectedMemoryLeak(ALeakedBlockSize: Integer; ACount: Integer = 1): Boolean; overload;
|
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
{Usage: UnregisterExpectedMemoryLeak(typeid(ACppObject).tpp, Count);}
|
|
|
|
|
function UnregisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer = 1): boolean; overload;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Returns a list of all expected memory leaks}
|
|
|
|
|
function GetRegisteredMemoryLeaks: TRegisteredMemoryLeaks;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
|
|
uses
|
|
|
|
|
{$ifndef Linux}
|
|
|
|
|
Windows,
|
|
|
|
|
{$else}
|
|
|
|
|
Libc,
|
|
|
|
|
{$endif}
|
|
|
|
|
FastMM4Messages;
|
|
|
|
|
|
|
|
|
|
{Fixed size move procedures}
|
|
|
|
|
procedure Move12(const ASource; var ADest; ACount: Integer); forward;
|
|
|
|
|
procedure Move20(const ASource; var ADest; ACount: Integer); forward;
|
|
|
|
|
procedure Move28(const ASource; var ADest; ACount: Integer); forward;
|
|
|
|
|
procedure Move36(const ASource; var ADest; ACount: Integer); forward;
|
|
|
|
|
procedure Move44(const ASource; var ADest; ACount: Integer); forward;
|
|
|
|
|
procedure Move52(const ASource; var ADest; ACount: Integer); forward;
|
|
|
|
|
procedure Move60(const ASource; var ADest; ACount: Integer); forward;
|
|
|
|
|
procedure Move68(const ASource; var ADest; ACount: Integer); forward;
|
|
|
|
|
|
|
|
|
|
{$ifdef DetectMMOperationsAfterUninstall}
|
|
|
|
|
{Invalid handlers to catch MM operations after uninstall}
|
|
|
|
|
function InvalidFreeMem(APointer: Pointer): Integer; forward;
|
|
|
|
|
function InvalidGetMem(ASize: Integer): Pointer; forward;
|
|
|
|
|
function InvalidReallocMem(APointer: Pointer; ANewSize: Integer): Pointer; forward;
|
|
|
|
|
function InvalidAllocMem(ASize: Cardinal): Pointer; forward;
|
|
|
|
|
function InvalidRegisterAndUnRegisterMemoryLeak(APointer: Pointer): Boolean; forward;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{-------------------------Private constants----------------------------}
|
|
|
|
|
const
|
|
|
|
|
{The size of a medium block pool. This is allocated through VirtualAlloc and
|
|
|
|
|
is used to serve medium blocks. The size must be a multiple of 16 and at
|
|
|
|
|
least 4 bytes less than a multiple of 4K (the page size) to prevent a
|
|
|
|
|
possible read access violation when reading past the end of a memory block
|
|
|
|
|
in the optimized move routine (MoveX16L4). In Full Debug mode we leave a
|
|
|
|
|
trailing 256 bytes to be able to safely do a memory dump.}
|
|
|
|
|
MediumBlockPoolSize = 20 * 64 * 1024{$ifndef FullDebugMode} - 16{$else} - 256{$endif};
|
|
|
|
|
{The granularity of small blocks}
|
|
|
|
|
{$ifdef Align16Bytes}
|
|
|
|
|
SmallBlockGranularity = 16;
|
|
|
|
|
{$else}
|
|
|
|
|
SmallBlockGranularity = 8;
|
|
|
|
|
{$endif}
|
|
|
|
|
{The granularity of medium blocks. Newly allocated medium blocks are
|
|
|
|
|
a multiple of this size plus MediumBlockSizeOffset, to avoid cache line
|
|
|
|
|
conflicts}
|
|
|
|
|
MediumBlockGranularity = 256;
|
|
|
|
|
MediumBlockSizeOffset = 48;
|
|
|
|
|
{The granularity of large blocks}
|
|
|
|
|
LargeBlockGranularity = 65536;
|
|
|
|
|
{The maximum size of a small block. Blocks Larger than this are either
|
|
|
|
|
medium or large blocks.}
|
|
|
|
|
MaximumSmallBlockSize = 2608;
|
|
|
|
|
{The smallest medium block size. (Medium blocks are rounded up to the nearest
|
|
|
|
|
multiple of MediumBlockGranularity plus MediumBlockSizeOffset)}
|
|
|
|
|
MinimumMediumBlockSize = 11 * 256 + MediumBlockSizeOffset;
|
|
|
|
|
{The number of bins reserved for medium blocks}
|
|
|
|
|
MediumBlockBinsPerGroup = 32;
|
|
|
|
|
MediumBlockBinGroupCount = 32;
|
|
|
|
|
MediumBlockBinCount = MediumBlockBinGroupCount * MediumBlockBinsPerGroup;
|
|
|
|
|
{The maximum size allocatable through medium blocks. Blocks larger than this
|
|
|
|
|
fall through to VirtualAlloc ( = large blocks).}
|
|
|
|
|
MaximumMediumBlockSize = MinimumMediumBlockSize + (MediumBlockBinCount - 1) * MediumBlockGranularity;
|
|
|
|
|
{The target number of small blocks per pool. The actual number of blocks per
|
|
|
|
|
pool may be much greater for very small sizes and less for larger sizes. The
|
|
|
|
|
cost of allocating the small block pool is amortized across all the small
|
|
|
|
|
blocks in the pool, however the blocks may not all end up being used so they
|
|
|
|
|
may be lying idle.}
|
|
|
|
|
TargetSmallBlocksPerPool = 48;
|
|
|
|
|
{The minimum number of small blocks per pool. Any available medium block must
|
|
|
|
|
have space for roughly this many small blocks (or more) to be useable as a
|
|
|
|
|
small block pool.}
|
|
|
|
|
MinimumSmallBlocksPerPool = 12;
|
|
|
|
|
{The lower and upper limits for the optimal small block pool size}
|
|
|
|
|
OptimalSmallBlockPoolSizeLowerLimit = 29 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
|
|
|
|
|
OptimalSmallBlockPoolSizeUpperLimit = 64 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
|
|
|
|
|
{The maximum small block pool size. If a free block is this size or larger
|
|
|
|
|
then it will be split.}
|
|
|
|
|
MaximumSmallBlockPoolSize = OptimalSmallBlockPoolSizeUpperLimit + MinimumMediumBlockSize;
|
|
|
|
|
{-------------Block type flags--------------}
|
|
|
|
|
{The lower 3 bits in the dword header of small blocks (4 bits in medium and
|
|
|
|
|
large blocks) are used as flags to indicate the state of the block}
|
|
|
|
|
{Set if the block is not in use}
|
|
|
|
|
IsFreeBlockFlag = 1;
|
|
|
|
|
{Set if this is a medium block}
|
|
|
|
|
IsMediumBlockFlag = 2;
|
|
|
|
|
{Set if it is a medium block being used as a small block pool. Only valid if
|
|
|
|
|
IsMediumBlockFlag is set.}
|
|
|
|
|
IsSmallBlockPoolInUseFlag = 4;
|
|
|
|
|
{Set if it is a large block. Only valid if IsMediumBlockFlag is not set.}
|
|
|
|
|
IsLargeBlockFlag = 4;
|
|
|
|
|
{Is the medium block preceding this block available? (Only used by medium
|
|
|
|
|
blocks)}
|
|
|
|
|
PreviousMediumBlockIsFreeFlag = 8;
|
|
|
|
|
{Is this large block segmented? I.e. is it actually built up from more than
|
|
|
|
|
one chunk allocated through VirtualAlloc? (Only used by large blocks.)}
|
|
|
|
|
LargeBlockIsSegmented = 8;
|
|
|
|
|
{The flags masks for small blocks}
|
|
|
|
|
DropSmallFlagsMask = -8;
|
|
|
|
|
ExtractSmallFlagsMask = 7;
|
|
|
|
|
{The flags masks for medium and large blocks}
|
|
|
|
|
DropMediumAndLargeFlagsMask = -16;
|
|
|
|
|
ExtractMediumAndLargeFlagsMask = 15;
|
|
|
|
|
{-------------Block resizing constants---------------}
|
|
|
|
|
SmallBlockDownsizeCheckAdder = 64;
|
|
|
|
|
SmallBlockUpsizeAdder = 32;
|
|
|
|
|
{When a medium block is reallocated to a size smaller than this, then it must
|
|
|
|
|
be reallocated to a small block and the data moved. If not, then it is
|
|
|
|
|
shrunk in place down to MinimumMediumBlockSize. Currently the limit is set
|
|
|
|
|
at a quarter of the minimum medium block size.}
|
|
|
|
|
MediumInPlaceDownsizeLimit = MinimumMediumBlockSize div 4;
|
|
|
|
|
{-------------Memory leak reporting constants---------------}
|
|
|
|
|
ExpectedMemoryLeaksListSize = 64 * 1024;
|
|
|
|
|
{-------------FullDebugMode constants---------------}
|
|
|
|
|
{$ifdef FullDebugMode}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{The stack trace depth. (Must be an even number to ensure that the
|
|
|
|
|
Align16Bytes option works in FullDebugMode.)}
|
|
|
|
|
StackTraceDepth = 10;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{The number of entries in the allocation group stack}
|
|
|
|
|
AllocationGroupStackSize = 1000;
|
|
|
|
|
{The number of fake VMT entries - used to track virtual method calls on
|
|
|
|
|
freed objects. Do not change this value without also updating TFreedObject.GetVirtualMethodIndex}
|
|
|
|
|
MaxFakeVMTEntries = 200;
|
|
|
|
|
{The pattern used to fill unused memory}
|
|
|
|
|
DebugFillByte = $80;
|
|
|
|
|
DebugFillDWord = $01010101 * Cardinal(DebugFillByte);
|
|
|
|
|
{The address that is reserved so that accesses to the address of the fill
|
|
|
|
|
pattern will result in an A/V}
|
|
|
|
|
DebugReservedAddress = $01010000 * Cardinal(DebugFillByte);
|
|
|
|
|
{$endif}
|
|
|
|
|
{-------------Other constants---------------}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Sleep time when a resource (small/medium/large block manager) is in use}
|
|
|
|
|
InitialSleepTime = 0;
|
|
|
|
|
{Used when the resource is still in use after the first sleep}
|
|
|
|
|
AdditionalSleepTime = 10;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Hexadecimal characters}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
HexTable: array[0..15] of AnsiChar = ('0', '1', '2', '3', '4', '5', '6', '7',
|
|
|
|
|
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Copyright message - not used anywhere in the code}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Copyright: AnsiString = 'FastMM4 (c) 2004 - 2008 Pierre le Riche / Professional Software Development';
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{Virtual Method Called On Freed Object Errors}
|
|
|
|
|
StandardVirtualMethodNames: array[1 + vmtParent div 4 .. -1] of PAnsiChar = (
|
|
|
|
|
'SafeCallException',
|
|
|
|
|
'AfterConstruction',
|
|
|
|
|
'BeforeDestruction',
|
|
|
|
|
'Dispatch',
|
|
|
|
|
'DefaultHandler',
|
|
|
|
|
'NewInstance',
|
|
|
|
|
'FreeInstance',
|
|
|
|
|
'Destroy');
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
|
|
|
|
{-------------------------Private types----------------------------}
|
|
|
|
|
type
|
|
|
|
|
|
|
|
|
|
{$ifdef Delphi4or5}
|
|
|
|
|
{Delphi 5 Compatibility}
|
|
|
|
|
PCardinal = ^Cardinal;
|
|
|
|
|
PPointer = ^Pointer;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Move procedure type}
|
|
|
|
|
TMoveProc = procedure(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
|
|
|
|
|
{Registers structure (for GetCPUID)}
|
|
|
|
|
TRegisters = record
|
|
|
|
|
RegEAX, RegEBX, RegECX, RegEDX: Integer;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{The layout of a string allocation. Used to detect string leaks.}
|
|
|
|
|
PStrRec = ^StrRec;
|
|
|
|
|
StrRec = packed record
|
|
|
|
|
refCnt: Longint;
|
|
|
|
|
length: Longint;
|
|
|
|
|
end;
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
{Different kinds of memory leaks}
|
|
|
|
|
TMemoryLeakType = (mltUnexpectedLeak, mltExpectedLeakRegisteredByPointer,
|
|
|
|
|
mltExpectedLeakRegisteredByClass, mltExpectedLeakRegisteredBySize);
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{---------------Small block structures-------------}
|
|
|
|
|
|
|
|
|
|
{Pointer to the header of a small block pool}
|
|
|
|
|
PSmallBlockPoolHeader = ^TSmallBlockPoolHeader;
|
|
|
|
|
|
|
|
|
|
{Small block type (Size = 32)}
|
|
|
|
|
PSmallBlockType = ^TSmallBlockType;
|
|
|
|
|
TSmallBlockType = packed record
|
|
|
|
|
{True = Block type is locked}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
BlockTypeLocked: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Bitmap indicating which of the first 8 medium block groups contain blocks
|
|
|
|
|
of a suitable size for a block pool.}
|
|
|
|
|
AllowedGroupsForBlockPoolBitmap: byte;
|
|
|
|
|
{The block size for this block type}
|
|
|
|
|
BlockSize: Word;
|
|
|
|
|
{The first partially free pool for the given small block type (offset = +4
|
|
|
|
|
for typecast compatibility with TSmallBlockPoolHeader). This is a circular
|
|
|
|
|
buffer.}
|
|
|
|
|
NextPartiallyFreePool: PSmallBlockPoolHeader;
|
|
|
|
|
{The offset of the last block that was served sequentially (0ffset = +8 to
|
|
|
|
|
to be at the same offset as the "FirstFreeBlock" of TSmallBlockPoolHeader}
|
|
|
|
|
NextSequentialFeedBlockAddress: Pointer;
|
|
|
|
|
{The last block that can be served sequentially. Offset is at +12 to be
|
|
|
|
|
at the same address as the "BlocksInUse" field of TSmallBlockPoolHeader}
|
|
|
|
|
MaxSequentialFeedBlockAddress: Pointer;
|
|
|
|
|
{The pool that is current being used to serve blocks in sequential order}
|
|
|
|
|
CurrentSequentialFeedPool: PSmallBlockPoolHeader;
|
|
|
|
|
{The previous partially free pool for the small block type (offset = +20
|
|
|
|
|
for typecast compatibility with TSmallBlockPoolHeader)}
|
|
|
|
|
PreviousPartiallyFreePool: PSmallBlockPoolHeader;
|
|
|
|
|
{The minimum and optimal size of a small block pool for this block type}
|
|
|
|
|
MinimumBlockPoolSize: Word;
|
|
|
|
|
OptimalBlockPoolSize: Word;
|
|
|
|
|
{$ifdef UseCustomFixedSizeMoveRoutines}
|
|
|
|
|
{The fixed size move procedure used to move data for this block size when
|
|
|
|
|
it is upsized. When a block is downsized (which usually does not occur
|
|
|
|
|
that often) the variable size move routine is used.}
|
|
|
|
|
UpsizeMoveProcedure: TMoveProc;
|
|
|
|
|
{$else}
|
|
|
|
|
Reserved1: Cardinal;
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Small block pool (Size = 32 bytes)}
|
|
|
|
|
TSmallBlockPoolHeader = packed record
|
|
|
|
|
{BlockType}
|
|
|
|
|
BlockType: PSmallBlockType;
|
|
|
|
|
{The next pool that has free blocks of this size. Must be at offset +4
|
|
|
|
|
to be typecast compatible with TSmallBlockType}
|
|
|
|
|
NextPartiallyFreePool: PSmallBlockPoolHeader;
|
|
|
|
|
{Pointer to the first free block inside this pool. Must be at offset + 8
|
|
|
|
|
to be at the same offset as "NextSequentialFeedBlockAddress" of
|
|
|
|
|
TSmallBlockType}
|
|
|
|
|
FirstFreeBlock: Pointer;
|
|
|
|
|
{The number of blocks allocated in this pool. Must be at offset + 12
|
|
|
|
|
to be at the same offset as "MaxSequentialFeedBlockAddress" of
|
|
|
|
|
TSmallBlockType}
|
|
|
|
|
BlocksInUse: Cardinal;
|
|
|
|
|
{Reserved}
|
|
|
|
|
Reserved1: Cardinal;
|
|
|
|
|
{The previous pool that has free blocks of this size. Must be at offset +20
|
|
|
|
|
to be compatible with TSmallBlockType}
|
|
|
|
|
PreviousPartiallyFreePool: PSmallBlockPoolHeader;
|
|
|
|
|
{Reserved}
|
|
|
|
|
Reserved2: Cardinal;
|
|
|
|
|
{The pool pointer and flags of the first block}
|
|
|
|
|
FirstBlockPoolPointerAndFlags: Cardinal;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Small block layout:
|
|
|
|
|
Offset: -4 = Flags + address of the small block pool
|
|
|
|
|
Offset: BlockSize - 4 = Flags + address of the small block pool for the next small block
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{----------------------Medium block structures----------------------}
|
|
|
|
|
|
|
|
|
|
{The medium block pool from which medium blocks are drawn}
|
|
|
|
|
PMediumBlockPoolHeader = ^TMediumBlockPoolHeader;
|
|
|
|
|
TMediumBlockPoolHeader = packed record
|
|
|
|
|
{Points to the previous and next medium block pools. This circular linked
|
|
|
|
|
list is used to track memory leaks on program shutdown.}
|
|
|
|
|
PreviousMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
NextMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
{Unused dword}
|
|
|
|
|
Reserved: Cardinal;
|
|
|
|
|
{The block size and flags of the first medium block in the block pool}
|
|
|
|
|
FirstMediumBlockSizeAndFlags: Cardinal;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Medium block layout:
|
|
|
|
|
Offset: -8 = Previous Block Size (only if the previous block is free)
|
|
|
|
|
Offset: -4 = This block size and flags
|
|
|
|
|
Offset: 0 = User data / Previous Free Block (if this block is free)
|
|
|
|
|
Offset: 4 = Next Free Block (if this block is free)
|
|
|
|
|
Offset: BlockSize - 8 = Size of this block (if this block is free)
|
|
|
|
|
Offset: BlockSize - 4 = Size of the next block and flags
|
|
|
|
|
|
|
|
|
|
{A medium block that is unused}
|
|
|
|
|
PMediumFreeBlock = ^TMediumFreeBlock;
|
|
|
|
|
TMediumFreeBlock = packed record
|
|
|
|
|
PreviousFreeBlock: PMediumFreeBlock;
|
|
|
|
|
NextFreeBlock: PMediumFreeBlock;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{-------------------------Large block structures--------------------}
|
|
|
|
|
|
|
|
|
|
{Large block header record (size = 16)}
|
|
|
|
|
PLargeBlockHeader = ^TLargeBlockHeader;
|
|
|
|
|
TLargeBlockHeader = packed record
|
|
|
|
|
{Points to the previous and next large blocks. This circular linked
|
|
|
|
|
list is used to track memory leaks on program shutdown.}
|
|
|
|
|
PreviousLargeBlockHeader: PLargeBlockHeader;
|
|
|
|
|
NextLargeBlockHeader: PLargeBlockHeader;
|
|
|
|
|
{The user allocated size of the Large block}
|
|
|
|
|
UserAllocatedSize: Cardinal;
|
|
|
|
|
{The size of this block plus the flags}
|
|
|
|
|
BlockSizeAndFlags: Cardinal;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{-------------------------Expected Memory Leak Structures--------------------}
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
|
|
|
|
|
{The layout of an expected leak. All fields may not be specified, in which
|
|
|
|
|
case it may be harder to determine which leaks are expected and which are
|
|
|
|
|
not.}
|
|
|
|
|
PExpectedMemoryLeak = ^TExpectedMemoryLeak;
|
|
|
|
|
PPExpectedMemoryLeak = ^PExpectedMemoryLeak;
|
|
|
|
|
TExpectedMemoryLeak = packed record
|
|
|
|
|
{Linked list pointers}
|
|
|
|
|
PreviousLeak, NextLeak: PExpectedMemoryLeak;
|
|
|
|
|
{Information about the expected leak}
|
|
|
|
|
LeakAddress: Pointer;
|
|
|
|
|
LeakedClass: TClass;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LeakedCppTypeIdPtr: Pointer;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LeakSize: Integer;
|
|
|
|
|
LeakCount: Integer;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
TExpectedMemoryLeaks = packed record
|
|
|
|
|
{The number of entries used in the expected leaks buffer}
|
|
|
|
|
EntriesUsed: Integer;
|
|
|
|
|
{Freed entries}
|
|
|
|
|
FirstFreeSlot: PExpectedMemoryLeak;
|
|
|
|
|
{Entries with the address specified}
|
|
|
|
|
FirstEntryByAddress: PExpectedMemoryLeak;
|
|
|
|
|
{Entries with no address specified, but with the class specified}
|
|
|
|
|
FirstEntryByClass: PExpectedMemoryLeak;
|
|
|
|
|
{Entries with only size specified}
|
|
|
|
|
FirstEntryBySizeOnly: PExpectedMemoryLeak;
|
|
|
|
|
{The expected leaks buffer}
|
|
|
|
|
ExpectedLeaks: packed array[0..(ExpectedMemoryLeaksListSize - 20) div SizeOf(TExpectedMemoryLeak) - 1] of TExpectedMemoryLeak;
|
|
|
|
|
end;
|
|
|
|
|
PExpectedMemoryLeaks = ^TExpectedMemoryLeaks;
|
|
|
|
|
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{-------------------------Full Debug Mode Structures--------------------}
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
|
|
|
|
|
PStackTrace = ^TStackTrace;
|
|
|
|
|
TStackTrace = array[0..StackTraceDepth - 1] of Cardinal;
|
|
|
|
|
|
|
|
|
|
TBlockOperation = (boBlockCheck, boGetMem, boFreeMem, boReallocMem);
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{The header placed in front blocks in FullDebugMode (just after the standard
|
|
|
|
|
header). Must be a multiple of 16 bytes in size otherwise the Align16Bytes
|
|
|
|
|
option will not work.}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
PFullDebugBlockHeader = ^TFullDebugBlockHeader;
|
|
|
|
|
TFullDebugBlockHeader = packed record
|
|
|
|
|
{Space used by the medium block manager for previous/next block management.
|
|
|
|
|
If a medium block is binned then these two dwords will be modified.}
|
|
|
|
|
Reserved1: Cardinal;
|
|
|
|
|
Reserved2: Cardinal;
|
|
|
|
|
{Is the block currently allocated?}
|
|
|
|
|
BlockInUse: LongBool;
|
|
|
|
|
{The allocation group: Can be used in the debugging process to group
|
|
|
|
|
related memory leaks together}
|
|
|
|
|
AllocationGroup: Cardinal;
|
|
|
|
|
{The allocation number: All new allocations are numbered sequentially. This
|
|
|
|
|
number may be useful in memory leak analysis. If it reaches 4GB it wraps
|
|
|
|
|
back to 0.}
|
|
|
|
|
AllocationNumber: Cardinal;
|
|
|
|
|
{The call stack when the block was allocated}
|
|
|
|
|
AllocationStackTrace: TStackTrace;
|
|
|
|
|
{The call stack when the block was freed}
|
|
|
|
|
FreeStackTrace: TStackTrace;
|
|
|
|
|
{The user requested size for the block. 0 if this is the first time the
|
|
|
|
|
block is used.}
|
|
|
|
|
UserSize: Cardinal;
|
|
|
|
|
{The object class this block was used for the previous time it was
|
|
|
|
|
allocated. When a block is freed, the dword that would normally be in the
|
|
|
|
|
space of the class pointer is copied here, so if it is detected that
|
|
|
|
|
the block was used after being freed we have an idea what class it is.}
|
|
|
|
|
PreviouslyUsedByClass: Cardinal;
|
|
|
|
|
{The sum of all the dwords excluding reserved dwords.}
|
|
|
|
|
HeaderCheckSum: Cardinal;
|
|
|
|
|
end;
|
|
|
|
|
{The last four bytes of the actual allocated block is the inverse of the
|
|
|
|
|
header checksum}
|
|
|
|
|
|
|
|
|
|
{The class used to catch attempts to execute a virtual method of a freed
|
|
|
|
|
object}
|
|
|
|
|
TFreedObject = class
|
|
|
|
|
public
|
|
|
|
|
procedure GetVirtualMethodIndex;
|
|
|
|
|
procedure VirtualMethodError;
|
|
|
|
|
{$ifdef CatchUseOfFreedInterfaces}
|
|
|
|
|
procedure InterfaceError;
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{-------------------------Private constants----------------------------}
|
|
|
|
|
const
|
|
|
|
|
{$ifndef BCB6OrDelphi7AndUp}
|
|
|
|
|
reInvalidPtr = 2;
|
|
|
|
|
{$endif}
|
|
|
|
|
{The size of the block header in front of small and medium blocks}
|
|
|
|
|
BlockHeaderSize = 4;
|
|
|
|
|
{The size of a small block pool header}
|
|
|
|
|
SmallBlockPoolHeaderSize = SizeOf(TSmallBlockPoolHeader);
|
|
|
|
|
{The size of a medium block pool header}
|
|
|
|
|
MediumBlockPoolHeaderSize = SizeOf(TMediumBlockPoolHeader);
|
|
|
|
|
{The size of the header in front of Large blocks}
|
|
|
|
|
LargeBlockHeaderSize = SizeOf(TLargeBlockHeader);
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{We need space for the header. 4 bytes for the trailer and 4 bytes for the
|
2008-07-25 09:23:00 -04:00
|
|
|
|
trailing block size when the block is free}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
FullDebugBlockOverhead = SizeOf(TFullDebugBlockHeader) + 2 * SizeOf(Pointer);
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{-------------------------Private variables----------------------------}
|
|
|
|
|
var
|
|
|
|
|
{-----------------Small block management------------------}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{The small block types. Sizes include the leading 4-byte header. Sizes are
|
2006-04-15 03:44:05 -04:00
|
|
|
|
picked to limit maximum wastage to about 10% or 256 bytes (whichever is
|
|
|
|
|
less) where possible.}
|
|
|
|
|
SmallBlockTypes: packed array[0..NumSmallBlockTypes - 1] of TSmallBlockType =(
|
|
|
|
|
{8/16 byte jumps}
|
|
|
|
|
(BlockSize: 16 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move12{$endif}),
|
|
|
|
|
{$ifndef Align16Bytes}
|
|
|
|
|
(BlockSize: 24 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move20{$endif}),
|
|
|
|
|
{$endif}
|
|
|
|
|
(BlockSize: 32 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move28{$endif}),
|
|
|
|
|
{$ifndef Align16Bytes}
|
|
|
|
|
(BlockSize: 40 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move36{$endif}),
|
|
|
|
|
{$endif}
|
|
|
|
|
(BlockSize: 48 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move44{$endif}),
|
|
|
|
|
{$ifndef Align16Bytes}
|
|
|
|
|
(BlockSize: 56 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move52{$endif}),
|
|
|
|
|
{$endif}
|
|
|
|
|
(BlockSize: 64 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move60{$endif}),
|
|
|
|
|
{$ifndef Align16Bytes}
|
|
|
|
|
(BlockSize: 72 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move68{$endif}),
|
|
|
|
|
{$endif}
|
|
|
|
|
(BlockSize: 80),
|
|
|
|
|
{$ifndef Align16Bytes}
|
|
|
|
|
(BlockSize: 88),
|
|
|
|
|
{$endif}
|
|
|
|
|
(BlockSize: 96),
|
|
|
|
|
{$ifndef Align16Bytes}
|
|
|
|
|
(BlockSize: 104),
|
|
|
|
|
{$endif}
|
|
|
|
|
(BlockSize: 112),
|
|
|
|
|
{$ifndef Align16Bytes}
|
|
|
|
|
(BlockSize: 120),
|
|
|
|
|
{$endif}
|
|
|
|
|
(BlockSize: 128),
|
|
|
|
|
{$ifndef Align16Bytes}
|
|
|
|
|
(BlockSize: 136),
|
|
|
|
|
{$endif}
|
|
|
|
|
(BlockSize: 144),
|
|
|
|
|
{$ifndef Align16Bytes}
|
|
|
|
|
(BlockSize: 152),
|
|
|
|
|
{$endif}
|
|
|
|
|
(BlockSize: 160),
|
|
|
|
|
{16 byte jumps}
|
|
|
|
|
(BlockSize: 176),
|
|
|
|
|
(BlockSize: 192),
|
|
|
|
|
(BlockSize: 208),
|
|
|
|
|
(BlockSize: 224),
|
|
|
|
|
(BlockSize: 240),
|
|
|
|
|
(BlockSize: 256),
|
|
|
|
|
(BlockSize: 272),
|
|
|
|
|
(BlockSize: 288),
|
|
|
|
|
(BlockSize: 304),
|
|
|
|
|
(BlockSize: 320),
|
|
|
|
|
{32 byte jumps}
|
|
|
|
|
(BlockSize: 352),
|
|
|
|
|
(BlockSize: 384),
|
|
|
|
|
(BlockSize: 416),
|
|
|
|
|
(BlockSize: 448),
|
|
|
|
|
(BlockSize: 480),
|
|
|
|
|
{48 byte jumps}
|
|
|
|
|
(BlockSize: 528),
|
|
|
|
|
(BlockSize: 576),
|
|
|
|
|
(BlockSize: 624),
|
|
|
|
|
(BlockSize: 672),
|
|
|
|
|
{64 byte jumps}
|
|
|
|
|
(BlockSize: 736),
|
|
|
|
|
(BlockSize: 800),
|
|
|
|
|
{80 byte jumps}
|
|
|
|
|
(BlockSize: 880),
|
|
|
|
|
(BlockSize: 960),
|
|
|
|
|
{96 byte jumps}
|
|
|
|
|
(BlockSize: 1056),
|
|
|
|
|
(BlockSize: 1152),
|
|
|
|
|
{112 byte jumps}
|
|
|
|
|
(BlockSize: 1264),
|
|
|
|
|
(BlockSize: 1376),
|
|
|
|
|
{128 byte jumps}
|
|
|
|
|
(BlockSize: 1504),
|
|
|
|
|
{144 byte jumps}
|
|
|
|
|
(BlockSize: 1648),
|
|
|
|
|
{160 byte jumps}
|
|
|
|
|
(BlockSize: 1808),
|
|
|
|
|
{176 byte jumps}
|
|
|
|
|
(BlockSize: 1984),
|
|
|
|
|
{192 byte jumps}
|
|
|
|
|
(BlockSize: 2176),
|
|
|
|
|
{208 byte jumps}
|
|
|
|
|
(BlockSize: 2384),
|
|
|
|
|
{224 byte jumps}
|
|
|
|
|
(BlockSize: MaximumSmallBlockSize),
|
|
|
|
|
{The last block size occurs three times. If, during a GetMem call, the
|
|
|
|
|
requested block size is already locked by another thread then up to two
|
|
|
|
|
larger block sizes may be used instead. Having the last block size occur
|
|
|
|
|
three times avoids the need to have a size overflow check.}
|
|
|
|
|
(BlockSize: MaximumSmallBlockSize),
|
|
|
|
|
(BlockSize: MaximumSmallBlockSize));
|
|
|
|
|
{Size to small block type translation table}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
AllocSize2SmallBlockTypeIndX4: packed array[0..(MaximumSmallBlockSize - 1) div SmallBlockGranularity] of Byte;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{-----------------Medium block management------------------}
|
|
|
|
|
{A dummy medium block pool header: Maintains a circular list of all medium
|
|
|
|
|
block pools to enable memory leak detection on program shutdown.}
|
|
|
|
|
MediumBlockPoolsCircularList: TMediumBlockPoolHeader;
|
|
|
|
|
{Are medium blocks locked?}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
MediumBlocksLocked: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{The sequential feed medium block pool.}
|
|
|
|
|
LastSequentiallyFedMediumBlock: Pointer;
|
|
|
|
|
MediumSequentialFeedBytesLeft: Cardinal;
|
|
|
|
|
{The medium block bins are divided into groups of 32 bins. If a bit
|
|
|
|
|
is set in this group bitmap, then at least one bin in the group has free
|
|
|
|
|
blocks.}
|
|
|
|
|
MediumBlockBinGroupBitmap: Cardinal;
|
|
|
|
|
{The medium block bins: total of 32 * 32 = 1024 bins of a certain
|
|
|
|
|
minimum size.}
|
|
|
|
|
MediumBlockBinBitmaps: packed array[0..MediumBlockBinGroupCount - 1] of Cardinal;
|
|
|
|
|
{The medium block bins. There are 1024 LIFO circular linked lists each
|
|
|
|
|
holding blocks of a specified minimum size. The sizes vary in size from
|
|
|
|
|
MinimumMediumBlockSize to MaximumMediumBlockSize. The bins are treated as
|
|
|
|
|
type TMediumFreeBlock to avoid pointer checks.}
|
|
|
|
|
MediumBlockBins: packed array[0..MediumBlockBinCount - 1] of TMediumFreeBlock;
|
|
|
|
|
{-----------------Large block management------------------}
|
|
|
|
|
{Are large blocks locked?}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LargeBlocksLocked: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{A dummy large block header: Maintains a list of all allocated large blocks
|
|
|
|
|
to enable memory leak detection on program shutdown.}
|
|
|
|
|
LargeBlocksCircularList: TLargeBlockHeader;
|
|
|
|
|
{-------------------------Expected Memory Leak Structures--------------------}
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
{The expected memory leaks}
|
|
|
|
|
ExpectedMemoryLeaks: PExpectedMemoryLeaks;
|
|
|
|
|
ExpectedMemoryLeaksListLocked: Boolean;
|
|
|
|
|
{$endif}
|
|
|
|
|
{---------------------Full Debug Mode structures--------------------}
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{The allocation group stack}
|
|
|
|
|
AllocationGroupStack: array[0..AllocationGroupStackSize - 1] of Cardinal;
|
|
|
|
|
{The allocation group stack top (it is an index into AllocationGroupStack)}
|
|
|
|
|
AllocationGroupStackTop: Cardinal;
|
|
|
|
|
{The last allocation number used}
|
|
|
|
|
CurrentAllocationNumber: Cardinal;
|
|
|
|
|
{The current log file name}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
MMLogFileName: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{The 64K block of reserved memory used to trap invalid memory accesses using
|
|
|
|
|
fields in a freed object.}
|
|
|
|
|
ReservedBlock: Pointer;
|
|
|
|
|
{The virtual method index count - used to get the virtual method index for a
|
|
|
|
|
virtual method call on a freed object.}
|
|
|
|
|
VMIndex: Integer;
|
|
|
|
|
{The fake VMT used to catch virtual method calls on freed objects.}
|
|
|
|
|
FreedObjectVMT: packed record
|
|
|
|
|
VMTData: array[vmtSelfPtr .. vmtParent + 3] of byte;
|
|
|
|
|
VMTMethods: array[4 + vmtParent .. MaxFakeVMTEntries * 4 + vmtParent + 3] of Byte;
|
|
|
|
|
end;
|
|
|
|
|
{$ifdef CatchUseOfFreedInterfaces}
|
|
|
|
|
VMTBadInterface: array[0..MaxFakeVMTEntries - 1] of Pointer;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{--------------Other info--------------}
|
|
|
|
|
{The memory manager that was replaced}
|
|
|
|
|
OldMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif};
|
|
|
|
|
{The replacement memory manager}
|
|
|
|
|
NewMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif};
|
|
|
|
|
{$ifdef DetectMMOperationsAfterUninstall}
|
|
|
|
|
{Invalid handlers to catch MM operations after uninstall}
|
|
|
|
|
InvalidMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif} = (
|
|
|
|
|
GetMem: InvalidGetMem;
|
|
|
|
|
FreeMem: InvalidFreeMem;
|
|
|
|
|
ReallocMem: InvalidReallocMem
|
|
|
|
|
{$ifdef BDS2006AndUp};
|
|
|
|
|
AllocMem: InvalidAllocMem;
|
|
|
|
|
RegisterExpectedMemoryLeak: InvalidRegisterAndUnRegisterMemoryLeak;
|
|
|
|
|
UnRegisterExpectedMemoryLeak: InvalidRegisterAndUnRegisterMemoryLeak;
|
|
|
|
|
{$endif}
|
|
|
|
|
);
|
|
|
|
|
{$endif}
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef MMSharingEnabled}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{A string uniquely identifying the current process (for sharing the memory
|
|
|
|
|
manager between DLLs and the main application)}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
MappingObjectName: array[0..25] of AnsiChar = ('L', 'o', 'c', 'a', 'l', '\',
|
|
|
|
|
'F', 'a', 's', 't', 'M', 'M', '_', 'P', 'I', 'D', '_', '?', '?', '?', '?',
|
|
|
|
|
'?', '?', '?', '?', #0);
|
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
|
|
|
|
UniqueProcessIDString: array[1..20] of AnsiChar = ('?', '?', '?', '?', '?',
|
|
|
|
|
'?', '?', '?', '_', 'P', 'I', 'D', '_', 'F', 'a', 's', 't', 'M', 'M', #0);
|
|
|
|
|
UniqueProcessIDStringBE: array[1..23] of AnsiChar = ('?', '?', '?', '?', '?',
|
|
|
|
|
'?', '?', '?', '_', 'P', 'I', 'D', '_', 'F', 'a', 's', 't', 'M', 'M', '_',
|
|
|
|
|
'B', 'E', #0);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{The handle of the MM window}
|
|
|
|
|
MMWindow: HWND;
|
|
|
|
|
{The handle of the MM window (for default MM of Delphi 2006 compatibility)}
|
|
|
|
|
MMWindowBE: HWND;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{The handle of the memory mapped file}
|
|
|
|
|
MappingObjectHandle: Cardinal;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{Has FastMM been installed?}
|
|
|
|
|
FastMMIsInstalled: Boolean;
|
|
|
|
|
{Is the MM in place a shared memory manager?}
|
|
|
|
|
IsMemoryManagerOwner: Boolean;
|
|
|
|
|
{Must MMX be used for move operations?}
|
|
|
|
|
{$ifdef EnableMMX}
|
|
|
|
|
{$ifndef ForceMMX}
|
|
|
|
|
UseMMX: Boolean;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Is a MessageBox currently showing? If so, do not show another one.}
|
|
|
|
|
ShowingMessageBox: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
|
|
|
|
{----------------Utility Functions------------------}
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{A copy StrLen in order to avoid the SysUtils unit, which would have introduced
|
|
|
|
|
overhead like exception handling code.}
|
|
|
|
|
function StrLen(const Str: PAnsiChar): Cardinal;
|
|
|
|
|
asm
|
|
|
|
|
{Check the first byte}
|
|
|
|
|
cmp byte ptr [eax], 0
|
|
|
|
|
je @ZeroLength
|
|
|
|
|
{Get the negative of the string start in edx}
|
|
|
|
|
mov edx, eax
|
|
|
|
|
neg edx
|
|
|
|
|
{Word align}
|
|
|
|
|
add eax, 1
|
|
|
|
|
and eax, -2
|
|
|
|
|
@ScanLoop:
|
|
|
|
|
mov cx, [eax]
|
|
|
|
|
add eax, 2
|
|
|
|
|
test cl, ch
|
|
|
|
|
jnz @ScanLoop
|
|
|
|
|
test cl, cl
|
|
|
|
|
jz @ReturnLess2
|
|
|
|
|
test ch, ch
|
|
|
|
|
jnz @ScanLoop
|
|
|
|
|
lea eax, [eax + edx - 1]
|
|
|
|
|
ret
|
|
|
|
|
@ReturnLess2:
|
|
|
|
|
lea eax, [eax + edx - 2]
|
|
|
|
|
ret
|
|
|
|
|
@ZeroLength:
|
|
|
|
|
xor eax, eax
|
|
|
|
|
end;
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifdef EnableMMX}
|
|
|
|
|
{$ifndef ForceMMX}
|
|
|
|
|
{Returns true if the CPUID instruction is supported}
|
|
|
|
|
function CPUID_Supported: Boolean;
|
|
|
|
|
asm
|
|
|
|
|
pushfd
|
|
|
|
|
pop eax
|
|
|
|
|
mov edx, eax
|
|
|
|
|
xor eax, $200000
|
|
|
|
|
push eax
|
|
|
|
|
popfd
|
|
|
|
|
pushfd
|
|
|
|
|
pop eax
|
|
|
|
|
xor eax, edx
|
|
|
|
|
setnz al
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Gets the CPUID}
|
|
|
|
|
function GetCPUID(AInfoRequired: Integer): TRegisters;
|
|
|
|
|
asm
|
|
|
|
|
push ebx
|
|
|
|
|
push esi
|
|
|
|
|
mov esi, edx
|
|
|
|
|
{cpuid instruction}
|
|
|
|
|
{$ifdef Delphi4or5}
|
|
|
|
|
db $0f, $a2
|
|
|
|
|
{$else}
|
|
|
|
|
cpuid
|
|
|
|
|
{$endif}
|
|
|
|
|
{Save registers}
|
|
|
|
|
mov TRegisters[esi].RegEAX, eax
|
|
|
|
|
mov TRegisters[esi].RegEBX, ebx
|
|
|
|
|
mov TRegisters[esi].RegECX, ecx
|
|
|
|
|
mov TRegisters[esi].RegEDX, edx
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Returns true if the CPU supports MMX}
|
|
|
|
|
function MMX_Supported: Boolean;
|
|
|
|
|
var
|
|
|
|
|
LReg: TRegisters;
|
|
|
|
|
begin
|
|
|
|
|
if CPUID_Supported then
|
|
|
|
|
begin
|
|
|
|
|
{Get the CPUID}
|
|
|
|
|
LReg := GetCPUID(1);
|
|
|
|
|
{Bit 23 must be set for MMX support}
|
|
|
|
|
Result := LReg.RegEDX and $800000 <> 0;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
Result := False;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Compare [AAddress], CompareVal:
|
|
|
|
|
If Equal: [AAddress] := NewVal and result = CompareVal
|
|
|
|
|
If Unequal: Result := [AAddress]}
|
|
|
|
|
function LockCmpxchg(CompareVal, NewVal: byte; AAddress: PByte): Byte;
|
|
|
|
|
asm
|
|
|
|
|
{On entry:
|
|
|
|
|
al = CompareVal,
|
|
|
|
|
dl = NewVal,
|
|
|
|
|
ecx = AAddress}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef Linux}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
lock cmpxchg [ecx], dl
|
|
|
|
|
{$else}
|
|
|
|
|
{Workaround for Kylix compiler bug}
|
|
|
|
|
db $F0, $0F, $B0, $11
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{$ifndef AsmVersion}
|
|
|
|
|
{Gets the first set bit and resets it, returning the bit index}
|
|
|
|
|
function FindFirstSetBit(ACardinal: Cardinal): Cardinal;
|
|
|
|
|
asm
|
|
|
|
|
{On entry:
|
|
|
|
|
eax = ACardinal}
|
|
|
|
|
bsf eax, eax
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Writes the module filename to the specified buffer and returns the number of
|
|
|
|
|
characters written.}
|
|
|
|
|
function AppendModuleFileName(ABuffer: PAnsiChar): Integer;
|
|
|
|
|
var
|
|
|
|
|
LModuleHandle: HModule;
|
|
|
|
|
begin
|
|
|
|
|
{Get the module handle}
|
|
|
|
|
{$ifndef borlndmmdll}
|
|
|
|
|
if IsLibrary then
|
|
|
|
|
LModuleHandle := HInstance
|
|
|
|
|
else
|
|
|
|
|
{$endif}
|
|
|
|
|
LModuleHandle := 0;
|
|
|
|
|
{Get the module name}
|
|
|
|
|
Result := GetModuleFileNameA(LModuleHandle, ABuffer, 512);
|
|
|
|
|
end;
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Copies the name of the module followed by the given string to the buffer,
|
|
|
|
|
returning the pointer following the buffer.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function AppendStringToModuleName(AString, ABuffer: PAnsiChar): PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LModuleNameLength: Cardinal;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LCopyStart: PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{Get the name of the application}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LModuleNameLength := AppendModuleFileName(ABuffer);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Replace the last few characters}
|
|
|
|
|
if LModuleNameLength > 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Find the last backslash}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LCopyStart := PAnsiChar(Cardinal(ABuffer) + LModuleNameLength - 1);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LModuleNameLength := 0;
|
|
|
|
|
while (Cardinal(LCopyStart) >= Cardinal(ABuffer))
|
|
|
|
|
and (LCopyStart^ <> '\') do
|
|
|
|
|
begin
|
|
|
|
|
Inc(LModuleNameLength);
|
|
|
|
|
Dec(LCopyStart);
|
|
|
|
|
end;
|
|
|
|
|
{Copy the name to the start of the buffer}
|
|
|
|
|
Inc(LCopyStart);
|
|
|
|
|
System.Move(LCopyStart^, ABuffer^, LModuleNameLength);
|
|
|
|
|
Inc(ABuffer, LModuleNameLength);
|
|
|
|
|
ABuffer^ := ':';
|
|
|
|
|
Inc(ABuffer);
|
|
|
|
|
ABuffer^ := ' ';
|
|
|
|
|
Inc(ABuffer);
|
|
|
|
|
end;
|
|
|
|
|
{Append the string}
|
|
|
|
|
while AString^ <> #0 do
|
|
|
|
|
begin
|
|
|
|
|
ABuffer^ := AString^;
|
|
|
|
|
Inc(ABuffer);
|
|
|
|
|
{Next char}
|
|
|
|
|
Inc(AString);
|
|
|
|
|
end;
|
|
|
|
|
ABuffer^ := #0;
|
|
|
|
|
Result := ABuffer;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{----------------Faster Move Procedures-------------------}
|
|
|
|
|
|
|
|
|
|
{Fixed size move operations ignore the size parameter. All moves are assumed to
|
|
|
|
|
be non-overlapping.}
|
|
|
|
|
|
|
|
|
|
procedure Move12(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
mov ecx, [eax]
|
|
|
|
|
mov [edx], ecx
|
|
|
|
|
mov ecx, [eax + 4]
|
|
|
|
|
mov eax, [eax + 8]
|
|
|
|
|
mov [edx + 4], ecx
|
|
|
|
|
mov [edx + 8], eax
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure Move20(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
mov ecx, [eax]
|
|
|
|
|
mov [edx], ecx
|
|
|
|
|
mov ecx, [eax + 4]
|
|
|
|
|
mov [edx + 4], ecx
|
|
|
|
|
mov ecx, [eax + 8]
|
|
|
|
|
mov [edx + 8], ecx
|
|
|
|
|
mov ecx, [eax + 12]
|
|
|
|
|
mov eax, [eax + 16]
|
|
|
|
|
mov [edx + 12], ecx
|
|
|
|
|
mov [edx + 16], eax
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure Move28(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
mov ecx, [eax]
|
|
|
|
|
mov [edx], ecx
|
|
|
|
|
mov ecx, [eax + 4]
|
|
|
|
|
mov [edx + 4], ecx
|
|
|
|
|
mov ecx, [eax + 8]
|
|
|
|
|
mov [edx + 8], ecx
|
|
|
|
|
mov ecx, [eax + 12]
|
|
|
|
|
mov [edx + 12], ecx
|
|
|
|
|
mov ecx, [eax + 16]
|
|
|
|
|
mov [edx + 16], ecx
|
|
|
|
|
mov ecx, [eax + 20]
|
|
|
|
|
mov eax, [eax + 24]
|
|
|
|
|
mov [edx + 20], ecx
|
|
|
|
|
mov [edx + 24], eax
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure Move36(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
fild qword ptr [eax]
|
|
|
|
|
fild qword ptr [eax + 8]
|
|
|
|
|
fild qword ptr [eax + 16]
|
|
|
|
|
fild qword ptr [eax + 24]
|
|
|
|
|
mov ecx, [eax + 32]
|
|
|
|
|
mov [edx + 32], ecx
|
|
|
|
|
fistp qword ptr [edx + 24]
|
|
|
|
|
fistp qword ptr [edx + 16]
|
|
|
|
|
fistp qword ptr [edx + 8]
|
|
|
|
|
fistp qword ptr [edx]
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure Move44(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
fild qword ptr [eax]
|
|
|
|
|
fild qword ptr [eax + 8]
|
|
|
|
|
fild qword ptr [eax + 16]
|
|
|
|
|
fild qword ptr [eax + 24]
|
|
|
|
|
fild qword ptr [eax + 32]
|
|
|
|
|
mov ecx, [eax + 40]
|
|
|
|
|
mov [edx + 40], ecx
|
|
|
|
|
fistp qword ptr [edx + 32]
|
|
|
|
|
fistp qword ptr [edx + 24]
|
|
|
|
|
fistp qword ptr [edx + 16]
|
|
|
|
|
fistp qword ptr [edx + 8]
|
|
|
|
|
fistp qword ptr [edx]
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure Move52(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
fild qword ptr [eax]
|
|
|
|
|
fild qword ptr [eax + 8]
|
|
|
|
|
fild qword ptr [eax + 16]
|
|
|
|
|
fild qword ptr [eax + 24]
|
|
|
|
|
fild qword ptr [eax + 32]
|
|
|
|
|
fild qword ptr [eax + 40]
|
|
|
|
|
mov ecx, [eax + 48]
|
|
|
|
|
mov [edx + 48], ecx
|
|
|
|
|
fistp qword ptr [edx + 40]
|
|
|
|
|
fistp qword ptr [edx + 32]
|
|
|
|
|
fistp qword ptr [edx + 24]
|
|
|
|
|
fistp qword ptr [edx + 16]
|
|
|
|
|
fistp qword ptr [edx + 8]
|
|
|
|
|
fistp qword ptr [edx]
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure Move60(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
fild qword ptr [eax]
|
|
|
|
|
fild qword ptr [eax + 8]
|
|
|
|
|
fild qword ptr [eax + 16]
|
|
|
|
|
fild qword ptr [eax + 24]
|
|
|
|
|
fild qword ptr [eax + 32]
|
|
|
|
|
fild qword ptr [eax + 40]
|
|
|
|
|
fild qword ptr [eax + 48]
|
|
|
|
|
mov ecx, [eax + 56]
|
|
|
|
|
mov [edx + 56], ecx
|
|
|
|
|
fistp qword ptr [edx + 48]
|
|
|
|
|
fistp qword ptr [edx + 40]
|
|
|
|
|
fistp qword ptr [edx + 32]
|
|
|
|
|
fistp qword ptr [edx + 24]
|
|
|
|
|
fistp qword ptr [edx + 16]
|
|
|
|
|
fistp qword ptr [edx + 8]
|
|
|
|
|
fistp qword ptr [edx]
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure Move68(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
fild qword ptr [eax]
|
|
|
|
|
fild qword ptr [eax + 8]
|
|
|
|
|
fild qword ptr [eax + 16]
|
|
|
|
|
fild qword ptr [eax + 24]
|
|
|
|
|
fild qword ptr [eax + 32]
|
|
|
|
|
fild qword ptr [eax + 40]
|
|
|
|
|
fild qword ptr [eax + 48]
|
|
|
|
|
fild qword ptr [eax + 56]
|
|
|
|
|
mov ecx, [eax + 64]
|
|
|
|
|
mov [edx + 64], ecx
|
|
|
|
|
fistp qword ptr [edx + 56]
|
|
|
|
|
fistp qword ptr [edx + 48]
|
|
|
|
|
fistp qword ptr [edx + 40]
|
|
|
|
|
fistp qword ptr [edx + 32]
|
|
|
|
|
fistp qword ptr [edx + 24]
|
|
|
|
|
fistp qword ptr [edx + 16]
|
|
|
|
|
fistp qword ptr [edx + 8]
|
|
|
|
|
fistp qword ptr [edx]
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Variable size move procedure: Assumes ACount is 4 less than a multiple of 16.
|
|
|
|
|
Always moves at least 12 bytes, irrespective of ACount.}
|
|
|
|
|
procedure MoveX16L4(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
{Make the counter negative based: The last 12 bytes are moved separately}
|
|
|
|
|
sub ecx, 12
|
|
|
|
|
add eax, ecx
|
|
|
|
|
add edx, ecx
|
|
|
|
|
{$ifdef EnableMMX}
|
|
|
|
|
{$ifndef ForceMMX}
|
|
|
|
|
cmp UseMMX, True
|
|
|
|
|
jne @FPUMove
|
|
|
|
|
{$endif}
|
|
|
|
|
{Make the counter negative based: The last 12 bytes are moved separately}
|
|
|
|
|
neg ecx
|
|
|
|
|
jns @MMXMoveLast12
|
|
|
|
|
@MMXMoveLoop:
|
|
|
|
|
{Move a 16 byte block}
|
|
|
|
|
{$ifdef Delphi4or5}
|
|
|
|
|
{Delphi 5 compatibility}
|
|
|
|
|
db $0f, $6f, $04, $01
|
|
|
|
|
db $0f, $6f, $4c, $01, $08
|
|
|
|
|
db $0f, $7f, $04, $11
|
|
|
|
|
db $0f, $7f, $4c, $11, $08
|
|
|
|
|
{$else}
|
|
|
|
|
movq mm0, [eax + ecx]
|
|
|
|
|
movq mm1, [eax + ecx + 8]
|
|
|
|
|
movq [edx + ecx], mm0
|
|
|
|
|
movq [edx + ecx + 8], mm1
|
|
|
|
|
{$endif}
|
|
|
|
|
{Are there another 16 bytes to move?}
|
|
|
|
|
add ecx, 16
|
|
|
|
|
js @MMXMoveLoop
|
|
|
|
|
@MMXMoveLast12:
|
|
|
|
|
{Do the last 12 bytes}
|
|
|
|
|
{$ifdef Delphi4or5}
|
|
|
|
|
{Delphi 5 compatibility}
|
|
|
|
|
db $0f, $6f, $04, $01
|
|
|
|
|
{$else}
|
|
|
|
|
movq mm0, [eax + ecx]
|
|
|
|
|
{$endif}
|
|
|
|
|
mov eax, [eax + ecx + 8]
|
|
|
|
|
{$ifdef Delphi4or5}
|
|
|
|
|
{Delphi 5 compatibility}
|
|
|
|
|
db $0f, $7f, $04, $11
|
|
|
|
|
{$else}
|
|
|
|
|
movq [edx + ecx], mm0
|
|
|
|
|
{$endif}
|
|
|
|
|
mov [edx + ecx + 8], eax
|
|
|
|
|
{Exit MMX state}
|
|
|
|
|
{$ifdef Delphi4or5}
|
|
|
|
|
{Delphi 5 compatibility}
|
|
|
|
|
db $0f, $77
|
|
|
|
|
{$else}
|
|
|
|
|
emms
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef ForceMMX}
|
|
|
|
|
ret
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{FPU code is only used if MMX is not forced}
|
|
|
|
|
{$ifndef ForceMMX}
|
|
|
|
|
@FPUMove:
|
|
|
|
|
neg ecx
|
|
|
|
|
jns @FPUMoveLast12
|
|
|
|
|
@FPUMoveLoop:
|
|
|
|
|
{Move a 16 byte block}
|
|
|
|
|
fild qword ptr [eax + ecx]
|
|
|
|
|
fild qword ptr [eax + ecx + 8]
|
|
|
|
|
fistp qword ptr [edx + ecx + 8]
|
|
|
|
|
fistp qword ptr [edx + ecx]
|
|
|
|
|
{Are there another 16 bytes to move?}
|
|
|
|
|
add ecx, 16
|
|
|
|
|
js @FPUMoveLoop
|
|
|
|
|
@FPUMoveLast12:
|
|
|
|
|
{Do the last 12 bytes}
|
|
|
|
|
fild qword ptr [eax + ecx]
|
|
|
|
|
fistp qword ptr [edx + ecx]
|
|
|
|
|
mov eax, [eax + ecx + 8]
|
|
|
|
|
mov [edx + ecx + 8], eax
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Variable size move procedure: Assumes ACount is 4 less than a multiple of 8.
|
|
|
|
|
Always moves at least 12 bytes, irrespective of ACount.}
|
|
|
|
|
procedure MoveX8L4(const ASource; var ADest; ACount: Integer);
|
|
|
|
|
asm
|
|
|
|
|
{Make the counter negative based: The last 4 bytes are moved separately}
|
|
|
|
|
sub ecx, 4
|
|
|
|
|
add eax, ecx
|
|
|
|
|
add edx, ecx
|
|
|
|
|
neg ecx
|
|
|
|
|
{$ifdef EnableMMX}
|
|
|
|
|
{$ifndef ForceMMX}
|
|
|
|
|
cmp UseMMX, True
|
|
|
|
|
jne @FPUMoveLoop
|
|
|
|
|
{$endif}
|
|
|
|
|
@MMXMoveLoop:
|
|
|
|
|
{Move an 8 byte block}
|
|
|
|
|
{$ifdef Delphi4or5}
|
|
|
|
|
{Delphi 5 compatibility}
|
|
|
|
|
db $0f, $6f, $04, $01
|
|
|
|
|
db $0f, $7f, $04, $11
|
|
|
|
|
{$else}
|
|
|
|
|
movq mm0, [eax + ecx]
|
|
|
|
|
movq [edx + ecx], mm0
|
|
|
|
|
{$endif}
|
|
|
|
|
{Are there another 8 bytes to move?}
|
|
|
|
|
add ecx, 8
|
|
|
|
|
js @MMXMoveLoop
|
|
|
|
|
{Exit MMX state}
|
|
|
|
|
{$ifdef Delphi4or5}
|
|
|
|
|
{Delphi 5 compatibility}
|
|
|
|
|
db $0f, $77
|
|
|
|
|
{$else}
|
|
|
|
|
emms
|
|
|
|
|
{$endif}
|
|
|
|
|
{Do the last 4 bytes}
|
|
|
|
|
mov eax, [eax + ecx]
|
|
|
|
|
mov [edx + ecx], eax
|
|
|
|
|
{$ifndef ForceMMX}
|
|
|
|
|
ret
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{FPU code is only used if MMX is not forced}
|
|
|
|
|
{$ifndef ForceMMX}
|
|
|
|
|
@FPUMoveLoop:
|
|
|
|
|
{Move an 8 byte block}
|
|
|
|
|
fild qword ptr [eax + ecx]
|
|
|
|
|
fistp qword ptr [edx + ecx]
|
|
|
|
|
{Are there another 8 bytes to move?}
|
|
|
|
|
add ecx, 8
|
|
|
|
|
js @FPUMoveLoop
|
|
|
|
|
{Do the last 4 bytes}
|
|
|
|
|
mov eax, [eax + ecx]
|
|
|
|
|
mov [edx + ecx], eax
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{----------------Windows Emulation Functions for Kylix Support-----------------}
|
|
|
|
|
|
|
|
|
|
{$ifdef Linux}
|
|
|
|
|
|
|
|
|
|
const
|
|
|
|
|
{Messagebox constants}
|
|
|
|
|
MB_OK = 0;
|
|
|
|
|
MB_ICONERROR = $10;
|
|
|
|
|
MB_TASKMODAL = $2000;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
MB_DEFAULT_DESKTOP_ONLY = $20000;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Virtual memory constants}
|
|
|
|
|
MEM_COMMIT = $1000;
|
|
|
|
|
MEM_RELEASE = $8000;
|
|
|
|
|
MEM_TOP_DOWN = $100000;
|
|
|
|
|
PAGE_READWRITE = 4;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
procedure MessageBoxA(hWnd: Cardinal; AMessageText, AMessageTitle: PAnsiChar; uType: Cardinal); stdcall;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
writeln(AMessageText);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function VirtualAlloc(lpvAddress: Pointer; dwSize, flAllocationType, flProtect: Cardinal): Pointer; stdcall;
|
|
|
|
|
begin
|
|
|
|
|
Result := valloc(dwSize);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function VirtualFree(lpAddress: Pointer; dwSize, dwFreeType: Cardinal): LongBool; stdcall;
|
|
|
|
|
begin
|
|
|
|
|
free(lpAddress);
|
|
|
|
|
Result := True;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
procedure Sleep(dwMilliseconds: Cardinal); stdcall;
|
|
|
|
|
begin
|
|
|
|
|
{Convert to microseconds (more or less)}
|
|
|
|
|
usleep(dwMilliseconds shl 10);
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
|
|
|
|
{-----------------Debugging Support Functions and Procedures------------------}
|
|
|
|
|
|
|
|
|
|
{$ifdef FullDebugMode}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
|
|
|
|
|
{Fills a block of memory with the given dword. Always fills a multiple of 4 bytes}
|
|
|
|
|
procedure FillDWord(var AAddress; AByteCount: Integer; ADWordFillValue: Cardinal);
|
|
|
|
|
asm
|
|
|
|
|
{On Entry: eax = AAddress
|
|
|
|
|
edx = AByteCount
|
|
|
|
|
ecx = ADWordFillValue}
|
|
|
|
|
add eax, edx
|
|
|
|
|
neg edx
|
|
|
|
|
jns @Done
|
|
|
|
|
@FillLoop:
|
|
|
|
|
mov [eax + edx], ecx
|
|
|
|
|
add edx, 4
|
|
|
|
|
js @FillLoop
|
|
|
|
|
@Done:
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{$ifndef LoadDebugDLLDynamically}
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{The stack trace procedure. The stack trace module is external since it may
|
|
|
|
|
raise handled access violations that result in the creation of exception
|
|
|
|
|
objects and the stack trace code is not re-entrant.}
|
|
|
|
|
procedure GetStackTrace(AReturnAddresses: PCardinal;
|
|
|
|
|
AMaxDepth, ASkipFrames: Cardinal); external FullDebugModeLibraryName
|
|
|
|
|
name {$ifdef RawStackTraces}'GetRawStackTrace'{$else}'GetFrameBasedStackTrace'{$endif};
|
2008-07-25 09:23:00 -04:00
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{The exported procedure in the FastMM_FullDebugMode.dll library used to convert
|
|
|
|
|
the return addresses of a stack trace to a text string.}
|
|
|
|
|
function LogStackTrace(AReturnAddresses: PCardinal;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar; external FullDebugModeLibraryName
|
2006-04-15 03:44:05 -04:00
|
|
|
|
name 'LogStackTrace';
|
2008-07-25 09:23:00 -04:00
|
|
|
|
|
|
|
|
|
{$else}
|
|
|
|
|
|
|
|
|
|
{Default no-op stack trace and logging handlers}
|
|
|
|
|
procedure NoOpGetStackTrace(AReturnAddresses: PCardinal;
|
|
|
|
|
AMaxDepth, ASkipFrames: Cardinal);
|
|
|
|
|
begin
|
|
|
|
|
FillDWord(AReturnAddresses^, AMaxDepth * 4, 0);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function NoOpLogStackTrace(AReturnAddresses: PCardinal;
|
|
|
|
|
AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
|
|
|
|
|
begin
|
|
|
|
|
Result := ABuffer;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
var
|
|
|
|
|
|
|
|
|
|
{Handle to the FullDebugMode DLL}
|
|
|
|
|
FullDebugModeDLL: HMODULE;
|
|
|
|
|
|
|
|
|
|
GetStackTrace: procedure (AReturnAddresses: PCardinal;
|
|
|
|
|
AMaxDepth, ASkipFrames: Cardinal) = NoOpGetStackTrace;
|
|
|
|
|
|
|
|
|
|
LogStackTrace: function (AReturnAddresses: PCardinal;
|
|
|
|
|
AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar = NoOpLogStackTrace;
|
|
|
|
|
|
|
|
|
|
{$endif}
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifndef Linux}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function DelphiIsRunning: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Result := FindWindowA('TAppBuilder', nil) <> 0;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Converts a cardinal to string at the buffer location, returning the new
|
|
|
|
|
buffer position.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function CardinalToStrBuf(ACardinal: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
asm
|
|
|
|
|
{On entry: eax = ACardinal, edx = ABuffer}
|
|
|
|
|
push edi
|
|
|
|
|
mov edi, edx //Pointer to the first character in edi
|
|
|
|
|
//Calculate leading digit: divide the number by 1e9
|
|
|
|
|
add eax, 1 //Increment the number
|
|
|
|
|
mov edx, $89705F41 //1e9 reciprocal
|
|
|
|
|
mul edx //Multplying with reciprocal
|
|
|
|
|
shr eax, 30 //Save fraction bits
|
|
|
|
|
mov ecx, edx //First digit in bits <31:29>
|
|
|
|
|
and edx, $1FFFFFFF //Filter fraction part edx<28:0>
|
|
|
|
|
shr ecx, 29 //Get leading digit into accumulator
|
|
|
|
|
lea edx, [edx+4*edx] //Calculate ...
|
|
|
|
|
add edx, eax //... 5*fraction
|
|
|
|
|
mov eax, ecx //Copy leading digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store digit out to memory
|
|
|
|
|
//Calculate digit #2
|
|
|
|
|
mov eax, edx //Point format such that 1.0 = 2^28
|
|
|
|
|
cmp ecx, 1 //Any non-zero digit yet ?
|
|
|
|
|
sbb edi, -1 //Yes->increment ptr, No->keep old ptr
|
|
|
|
|
shr eax, 28 //Next digit
|
|
|
|
|
and edx, $0fffffff //Fraction part edx<27:0>
|
|
|
|
|
or ecx, eax //Accumulate next digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store digit out to memory
|
|
|
|
|
//Calculate digit #3
|
|
|
|
|
lea eax, [edx*4+edx] //5*fraction, new digit eax<31:27>
|
|
|
|
|
lea edx, [edx*4+edx] //5*fraction, new fraction edx<26:0>
|
|
|
|
|
cmp ecx, 1 //Any non-zero digit yet ?
|
|
|
|
|
sbb edi, -1 //Yes->increment ptr, No->keep old ptr
|
|
|
|
|
shr eax, 27 //Next digit
|
|
|
|
|
and edx, $07ffffff //Fraction part
|
|
|
|
|
or ecx, eax //Accumulate next digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store digit out to memory
|
|
|
|
|
//Calculate digit #4
|
|
|
|
|
lea eax, [edx*4+edx] //5*fraction, new digit eax<31:26>
|
|
|
|
|
lea edx, [edx*4+edx] //5*fraction, new fraction edx<25:0>
|
|
|
|
|
cmp ecx, 1 //Any non-zero digit yet ?
|
|
|
|
|
sbb edi, -1 //Yes->increment ptr, No->keep old ptr
|
|
|
|
|
shr eax, 26 //Next digit
|
|
|
|
|
and edx, $03ffffff //Fraction part
|
|
|
|
|
or ecx, eax //Accumulate next digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store digit out to memory
|
|
|
|
|
//Calculate digit #5
|
|
|
|
|
lea eax, [edx*4+edx] //5*fraction, new digit eax<31:25>
|
|
|
|
|
lea edx, [edx*4+edx] //5*fraction, new fraction edx<24:0>
|
|
|
|
|
cmp ecx, 1 //Any non-zero digit yet ?
|
|
|
|
|
sbb edi, -1 //Yes->increment ptr, No->keep old ptr
|
|
|
|
|
shr eax, 25 //Next digit
|
|
|
|
|
and edx, $01ffffff //Fraction part
|
|
|
|
|
or ecx, eax //Accumulate next digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store digit out to memory
|
|
|
|
|
//Calculate digit #6
|
|
|
|
|
lea eax, [edx*4+edx] //5*fraction, new digit eax<31:24>
|
|
|
|
|
lea edx, [edx*4+edx] //5*fraction, new fraction edx<23:0>
|
|
|
|
|
cmp ecx, 1 //Any non-zero digit yet ?
|
|
|
|
|
sbb edi, -1 //Yes->increment ptr, No->keep old ptr
|
|
|
|
|
shr eax, 24 //Next digit
|
|
|
|
|
and edx, $00ffffff //Fraction part
|
|
|
|
|
or ecx, eax //Accumulate next digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store digit out to memory
|
|
|
|
|
//Calculate digit #7
|
|
|
|
|
lea eax, [edx*4+edx] //5*fraction, new digit eax<31:23>
|
|
|
|
|
lea edx, [edx*4+edx] //5*fraction, new fraction edx<31:23>
|
|
|
|
|
cmp ecx, 1 //Any non-zero digit yet ?
|
|
|
|
|
sbb edi, -1 //Yes->increment ptr, No->keep old ptr
|
|
|
|
|
shr eax, 23 //Next digit
|
|
|
|
|
and edx, $007fffff //Fraction part
|
|
|
|
|
or ecx, eax //Accumulate next digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store digit out to memory
|
|
|
|
|
//Calculate digit #8
|
|
|
|
|
lea eax, [edx*4+edx] //5*fraction, new digit eax<31:22>
|
|
|
|
|
lea edx, [edx*4+edx] //5*fraction, new fraction edx<22:0>
|
|
|
|
|
cmp ecx, 1 //Any non-zero digit yet ?
|
|
|
|
|
sbb edi, -1 //Yes->increment ptr, No->keep old ptr
|
|
|
|
|
shr eax, 22 //Next digit
|
|
|
|
|
and edx, $003fffff //Fraction part
|
|
|
|
|
or ecx, eax //Accumulate next digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store digit out to memory
|
|
|
|
|
//Calculate digit #9
|
|
|
|
|
lea eax, [edx*4+edx] //5*fraction, new digit eax<31:21>
|
|
|
|
|
lea edx, [edx*4+edx] //5*fraction, new fraction edx<21:0>
|
|
|
|
|
cmp ecx, 1 //Any non-zero digit yet ?
|
|
|
|
|
sbb edi, -1 //Yes->increment ptr, No->keep old ptr
|
|
|
|
|
shr eax, 21 //Next digit
|
|
|
|
|
and edx, $001fffff //Fraction part
|
|
|
|
|
or ecx, eax //Accumulate next digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store digit out to memory
|
|
|
|
|
//Calculate digit #10
|
|
|
|
|
lea eax, [edx*4+edx] //5*fraction, new digit eax<31:20>
|
|
|
|
|
cmp ecx, 1 //Any-non-zero digit yet ?
|
|
|
|
|
sbb edi, -1 //Yes->increment ptr, No->keep old ptr
|
|
|
|
|
shr eax, 20 //Next digit
|
|
|
|
|
or eax, '0' //Convert digit to ASCII
|
|
|
|
|
mov [edi], al //Store last digit and end marker out to memory
|
|
|
|
|
{Return a pointer to the next character}
|
|
|
|
|
lea eax, [edi + 1]
|
|
|
|
|
{Restore edi}
|
|
|
|
|
pop edi
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Converts a cardinal to a hexadecimal string at the buffer location, returning
|
|
|
|
|
the new buffer position.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function CardinalToHexBuf(ACardinal: integer; ABuffer: PAnsiChar): PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
asm
|
|
|
|
|
{On entry:
|
|
|
|
|
eax = ACardinal
|
|
|
|
|
edx = ABuffer}
|
|
|
|
|
push ebx
|
|
|
|
|
push edi
|
|
|
|
|
{Save ACardinal in ebx}
|
|
|
|
|
mov ebx, eax
|
|
|
|
|
{Get a pointer to the first character in edi}
|
|
|
|
|
mov edi, edx
|
|
|
|
|
{Get the number in ecx as well}
|
|
|
|
|
mov ecx, eax
|
|
|
|
|
{Keep the low nibbles in ebx and the high nibbles in ecx}
|
|
|
|
|
and ebx, $0f0f0f0f
|
|
|
|
|
and ecx, $f0f0f0f0
|
|
|
|
|
{Swap the bytes into the right order}
|
|
|
|
|
ror ebx, 16
|
|
|
|
|
ror ecx, 20
|
|
|
|
|
{Get nibble 7}
|
|
|
|
|
movzx eax, ch
|
|
|
|
|
mov dl, ch
|
|
|
|
|
mov al, byte ptr HexTable[eax]
|
|
|
|
|
mov [edi], al
|
|
|
|
|
cmp dl, 1
|
|
|
|
|
sbb edi, -1
|
|
|
|
|
{Get nibble 6}
|
|
|
|
|
movzx eax, bh
|
|
|
|
|
or dl, bh
|
|
|
|
|
mov al, byte ptr HexTable[eax]
|
|
|
|
|
mov [edi], al
|
|
|
|
|
cmp dl, 1
|
|
|
|
|
sbb edi, -1
|
|
|
|
|
{Get nibble 5}
|
|
|
|
|
movzx eax, cl
|
|
|
|
|
or dl, cl
|
|
|
|
|
mov al, byte ptr HexTable[eax]
|
|
|
|
|
mov [edi], al
|
|
|
|
|
cmp dl, 1
|
|
|
|
|
sbb edi, -1
|
|
|
|
|
{Get nibble 4}
|
|
|
|
|
movzx eax, bl
|
|
|
|
|
or dl, bl
|
|
|
|
|
mov al, byte ptr HexTable[eax]
|
|
|
|
|
mov [edi], al
|
|
|
|
|
cmp dl, 1
|
|
|
|
|
sbb edi, -1
|
|
|
|
|
{Rotate ecx and ebx so we get access to the rest}
|
|
|
|
|
shr ebx, 16
|
|
|
|
|
shr ecx, 16
|
|
|
|
|
{Get nibble 3}
|
|
|
|
|
movzx eax, ch
|
|
|
|
|
or dl, ch
|
|
|
|
|
mov al, byte ptr HexTable[eax]
|
|
|
|
|
mov [edi], al
|
|
|
|
|
cmp dl, 1
|
|
|
|
|
sbb edi, -1
|
|
|
|
|
{Get nibble 2}
|
|
|
|
|
movzx eax, bh
|
|
|
|
|
or dl, bh
|
|
|
|
|
mov al, byte ptr HexTable[eax]
|
|
|
|
|
mov [edi], al
|
|
|
|
|
cmp dl, 1
|
|
|
|
|
sbb edi, -1
|
|
|
|
|
{Get nibble 1}
|
|
|
|
|
movzx eax, cl
|
|
|
|
|
or dl, cl
|
|
|
|
|
mov al, byte ptr HexTable[eax]
|
|
|
|
|
mov [edi], al
|
|
|
|
|
cmp dl, 1
|
|
|
|
|
sbb edi, -1
|
|
|
|
|
{Get nibble 0}
|
|
|
|
|
movzx eax, bl
|
|
|
|
|
mov al, byte ptr HexTable[eax]
|
|
|
|
|
mov [edi], al
|
|
|
|
|
{Return a pointer to the end of the string}
|
|
|
|
|
lea eax, [edi + 1]
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop edi
|
|
|
|
|
pop ebx
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Appends the source text to the destination and returns the new destination
|
|
|
|
|
position}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function AppendStringToBuffer(const ASource, ADestination: PAnsiChar; ACount: Cardinal): PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
System.Move(ASource^, ADestination^, ACount);
|
|
|
|
|
Result := Pointer(Cardinal(ADestination) + ACount);
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Appends the name of the class to the destination buffer and returns the new
|
|
|
|
|
destination position}
|
|
|
|
|
function AppendClassNameToBuffer(AClass: TClass; ADestination: PAnsiChar): PAnsiChar;
|
|
|
|
|
var
|
|
|
|
|
LPClassName: PShortString;
|
|
|
|
|
begin
|
|
|
|
|
{Get a pointer to the class name}
|
|
|
|
|
if AClass <> nil then
|
|
|
|
|
begin
|
|
|
|
|
LPClassName := PShortString(PPointer(Integer(AClass) + vmtClassName)^);
|
|
|
|
|
{Append the class name}
|
|
|
|
|
Result := AppendStringToBuffer(@LPClassName^[1], ADestination, Length(LPClassName^));
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result := AppendStringToBuffer(UnknownClassNameMsg, ADestination, Length(UnknownClassNameMsg));
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Shows a message box if the program is not showing one already.}
|
|
|
|
|
procedure ShowMessageBox(AText, ACaption: PAnsiChar);
|
|
|
|
|
begin
|
|
|
|
|
if (not ShowingMessageBox) and (not SuppressMessageBoxes) then
|
|
|
|
|
begin
|
|
|
|
|
ShowingMessageBox := True;
|
|
|
|
|
MessageBoxA(0, AText, ACaption,
|
|
|
|
|
MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_DEFAULT_DESKTOP_ONLY);
|
|
|
|
|
ShowingMessageBox := False;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Returns the class for a memory block. Returns nil if it is not a valid class}
|
|
|
|
|
function GetObjectClass(APointer: Pointer): TClass;
|
|
|
|
|
{$ifndef Linux}
|
|
|
|
|
var
|
|
|
|
|
LMemInfo: TMemoryBasicInformation;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Checks whether the given address is a valid address for a VMT entry.}
|
|
|
|
|
function IsValidVMTAddress(APAddress: PCardinal): Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Do some basic pointer checks: Must be dword aligned and beyond 64K}
|
|
|
|
|
if (Cardinal(APAddress) > 65535)
|
|
|
|
|
and (Cardinal(APAddress) and 3 = 0) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Do we need to recheck the virtual memory?}
|
|
|
|
|
if (Cardinal(LMemInfo.BaseAddress) > Cardinal(APAddress))
|
|
|
|
|
or ((Cardinal(LMemInfo.BaseAddress) + LMemInfo.RegionSize) < (Cardinal(APAddress) + 4)) then
|
|
|
|
|
begin
|
|
|
|
|
{Get the VM status for the pointer}
|
|
|
|
|
LMemInfo.RegionSize := 0;
|
|
|
|
|
VirtualQuery(APAddress, LMemInfo, SizeOf(LMemInfo));
|
|
|
|
|
end;
|
|
|
|
|
{Check the readability of the memory address}
|
|
|
|
|
Result := (LMemInfo.RegionSize >= 4)
|
|
|
|
|
and (LMemInfo.State = MEM_COMMIT)
|
|
|
|
|
and (LMemInfo.Protect and (PAGE_READONLY or PAGE_READWRITE or PAGE_EXECUTE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE or PAGE_EXECUTE_WRITECOPY) <> 0)
|
|
|
|
|
and (LMemInfo.Protect and PAGE_GUARD = 0);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
Result := False;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Returns true if AClassPointer points to a class VMT}
|
|
|
|
|
function InternalIsValidClass(AClassPointer: Pointer; ADepth: Integer = 0): Boolean;
|
|
|
|
|
var
|
|
|
|
|
LParentClassSelfPointer: PCardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Check that the self pointer as well as parent class self pointer addresses
|
|
|
|
|
are valid}
|
|
|
|
|
if (ADepth < 1000)
|
|
|
|
|
and IsValidVMTAddress(Pointer(Integer(AClassPointer) + vmtSelfPtr))
|
|
|
|
|
and IsValidVMTAddress(Pointer(Integer(AClassPointer) + vmtParent)) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Get a pointer to the parent class' self pointer}
|
|
|
|
|
LParentClassSelfPointer := PPointer(Integer(AClassPointer) + vmtParent)^;
|
|
|
|
|
{Check that the self pointer as well as the parent class is valid}
|
|
|
|
|
Result := (PPointer(Integer(AClassPointer) + vmtSelfPtr)^ = AClassPointer)
|
|
|
|
|
and ((LParentClassSelfPointer = nil)
|
|
|
|
|
or (IsValidVMTAddress(LParentClassSelfPointer)
|
|
|
|
|
and InternalIsValidClass(PCardinal(LParentClassSelfPointer^), ADepth + 1)));
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
Result := False;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
{Get the class pointer from the (suspected) object}
|
|
|
|
|
Result := TClass(PCardinal(APointer)^);
|
|
|
|
|
{No VM info yet}
|
|
|
|
|
LMemInfo.RegionSize := 0;
|
|
|
|
|
{Check the block}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if (not InternalIsValidClass(Pointer(Result), 0))
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
or (Result = @FreedObjectVMT.VMTMethods[0])
|
|
|
|
|
{$endif}
|
|
|
|
|
then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
{$else}
|
|
|
|
|
begin
|
|
|
|
|
{Not currently supported under Linux}
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Gets the available size inside a block}
|
|
|
|
|
function GetAvailableSpaceInBlock(APointer: Pointer): Cardinal;
|
|
|
|
|
var
|
|
|
|
|
LBlockHeader: Cardinal;
|
|
|
|
|
LPSmallBlockPool: PSmallBlockPoolHeader;
|
|
|
|
|
begin
|
|
|
|
|
LBlockHeader := PCardinal(Cardinal(APointer) - 4)^;
|
|
|
|
|
if LBlockHeader and (IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
|
|
|
|
|
begin
|
|
|
|
|
LPSmallBlockPool := PSmallBlockPoolHeader(LBlockHeader and DropSmallFlagsMask);
|
|
|
|
|
Result := LPSmallBlockPool.BlockType.BlockSize - BlockHeaderSize;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result := (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
|
|
|
|
|
if (LBlockHeader and IsMediumBlockFlag) = 0 then
|
|
|
|
|
Dec(Result, LargeBlockHeaderSize);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{-----------------Small Block Management------------------}
|
|
|
|
|
|
|
|
|
|
{Locks all small block types}
|
|
|
|
|
procedure LockAllSmallBlockTypes;
|
|
|
|
|
var
|
|
|
|
|
LInd: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Lock the medium blocks}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
if IsMultiThread then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
for LInd := 0 to NumSmallBlockTypes - 1 do
|
|
|
|
|
begin
|
|
|
|
|
while LockCmpxchg(0, 1, @SmallBlockTypes[LInd].BlockTypeLocked) <> 0 do
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(InitialSleepTime);
|
|
|
|
|
if LockCmpxchg(0, 1, @SmallBlockTypes[LInd].BlockTypeLocked) = 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(AdditionalSleepTime);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Gets the first and last block pointer for a small block pool}
|
|
|
|
|
procedure GetFirstAndLastSmallBlockInPool(APSmallBlockPool: PSmallBlockPoolHeader;
|
|
|
|
|
var AFirstPtr, ALastPtr: Pointer);
|
|
|
|
|
var
|
|
|
|
|
LBlockSize: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Get the pointer to the first block}
|
|
|
|
|
AFirstPtr := Pointer(Cardinal(APSmallBlockPool) + SmallBlockPoolHeaderSize);
|
|
|
|
|
{Get a pointer to the last block}
|
|
|
|
|
if (APSmallBlockPool.BlockType.CurrentSequentialFeedPool <> APSmallBlockPool)
|
|
|
|
|
or (Cardinal(APSmallBlockPool.BlockType.NextSequentialFeedBlockAddress) > Cardinal(APSmallBlockPool.BlockType.MaxSequentialFeedBlockAddress)) then
|
|
|
|
|
begin
|
|
|
|
|
{Not the sequential feed - point to the end of the block}
|
|
|
|
|
LBlockSize := PCardinal(Cardinal(APSmallBlockPool) - 4)^ and DropMediumAndLargeFlagsMask;
|
|
|
|
|
ALastPtr := Pointer(Cardinal(APSmallBlockPool) + LBlockSize - APSmallBlockPool.BlockType.BlockSize);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{The sequential feed pool - point to before the next sequential feed block}
|
|
|
|
|
ALastPtr := Pointer(Cardinal(APSmallBlockPool.BlockType.NextSequentialFeedBlockAddress) - 1);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{-----------------Medium Block Management------------------}
|
|
|
|
|
|
|
|
|
|
{Advances to the next medium block. Returns nil if the end of the medium block
|
|
|
|
|
pool has been reached}
|
|
|
|
|
function NextMediumBlock(APMediumBlock: Pointer): Pointer;
|
|
|
|
|
var
|
|
|
|
|
LBlockSize: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Get the size of this block}
|
|
|
|
|
LBlockSize := PCardinal(Cardinal(APMediumBlock) - 4)^ and DropMediumAndLargeFlagsMask;
|
|
|
|
|
{Advance the pointer}
|
|
|
|
|
Result := Pointer(Cardinal(APMediumBlock) + LBlockSize);
|
|
|
|
|
{Is the next block the end of medium pool marker?}
|
|
|
|
|
LBlockSize := PCardinal(Cardinal(Result) - 4)^ and DropMediumAndLargeFlagsMask;
|
|
|
|
|
if LBlockSize = 0 then
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Gets the first medium block in the medium block pool}
|
|
|
|
|
function GetFirstMediumBlockInPool(APMediumBlockPoolHeader: PMediumBlockPoolHeader): Pointer;
|
|
|
|
|
begin
|
|
|
|
|
if (MediumSequentialFeedBytesLeft = 0)
|
|
|
|
|
or (Cardinal(LastSequentiallyFedMediumBlock) < Cardinal(APMediumBlockPoolHeader))
|
|
|
|
|
or (Cardinal(LastSequentiallyFedMediumBlock) > Cardinal(APMediumBlockPoolHeader) + MediumBlockPoolSize) then
|
|
|
|
|
begin
|
|
|
|
|
Result := Pointer(Cardinal(APMediumBlockPoolHeader) + MediumBlockPoolHeaderSize);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Is the sequential feed pool empty?}
|
|
|
|
|
if MediumSequentialFeedBytesLeft <> MediumBlockPoolSize - MediumBlockPoolHeaderSize then
|
|
|
|
|
Result := LastSequentiallyFedMediumBlock
|
|
|
|
|
else
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Locks the medium blocks. Note that if AsmVersion is defined that the routine
|
|
|
|
|
is assumed to preserve all registers except eax.}
|
|
|
|
|
{$ifndef AsmVersion}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
procedure LockMediumBlocks;
|
|
|
|
|
begin
|
|
|
|
|
{Lock the medium blocks}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
if IsMultiThread then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
while LockCmpxchg(0, 1, @MediumBlocksLocked) <> 0 do
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(InitialSleepTime);
|
|
|
|
|
if LockCmpxchg(0, 1, @MediumBlocksLocked) = 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(AdditionalSleepTime);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$else}
|
|
|
|
|
procedure LockMediumBlocks;
|
|
|
|
|
asm
|
|
|
|
|
{Note: This routine is assumed to preserve all registers except eax}
|
|
|
|
|
@MediumBlockLockLoop:
|
|
|
|
|
mov eax, $100
|
|
|
|
|
{Attempt to lock the medium blocks}
|
|
|
|
|
lock cmpxchg MediumBlocksLocked, ah
|
|
|
|
|
je @Done
|
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
|
|
|
|
{Couldn't lock the medium blocks - sleep and try again}
|
|
|
|
|
push ecx
|
|
|
|
|
push edx
|
|
|
|
|
push InitialSleepTime
|
|
|
|
|
call Sleep
|
|
|
|
|
pop edx
|
|
|
|
|
pop ecx
|
|
|
|
|
{Try again}
|
|
|
|
|
mov eax, $100
|
|
|
|
|
{Attempt to grab the block type}
|
|
|
|
|
lock cmpxchg MediumBlocksLocked, ah
|
|
|
|
|
je @Done
|
|
|
|
|
{Couldn't lock the medium blocks - sleep and try again}
|
|
|
|
|
push ecx
|
|
|
|
|
push edx
|
|
|
|
|
push AdditionalSleepTime
|
|
|
|
|
call Sleep
|
|
|
|
|
pop edx
|
|
|
|
|
pop ecx
|
|
|
|
|
{Try again}
|
|
|
|
|
jmp @MediumBlockLockLoop
|
|
|
|
|
{$else}
|
|
|
|
|
{Pause instruction (improves performance on P4)}
|
|
|
|
|
rep nop
|
|
|
|
|
{Try again}
|
|
|
|
|
jmp @MediumBlockLockLoop
|
|
|
|
|
{$endif}
|
|
|
|
|
@Done:
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
|
|
|
|
{$ifndef AsmVersion}
|
|
|
|
|
{Removes a medium block from the circular linked list of free blocks.
|
|
|
|
|
Does not change any header flags. Medium blocks should be locked
|
|
|
|
|
before calling this procedure.}
|
|
|
|
|
procedure RemoveMediumFreeBlock(APMediumFreeBlock: PMediumFreeBlock);
|
|
|
|
|
var
|
|
|
|
|
LPreviousFreeBlock, LNextFreeBlock: PMediumFreeBlock;
|
|
|
|
|
LBinNumber, LBinGroupNumber: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Get the current previous and next blocks}
|
|
|
|
|
LNextFreeBlock := APMediumFreeBlock.NextFreeBlock;
|
|
|
|
|
LPreviousFreeBlock := APMediumFreeBlock.PreviousFreeBlock;
|
|
|
|
|
{Remove this block from the linked list}
|
|
|
|
|
LPreviousFreeBlock.NextFreeBlock := LNextFreeBlock;
|
|
|
|
|
LNextFreeBlock.PreviousFreeBlock := LPreviousFreeBlock;
|
|
|
|
|
{Is this bin now empty? If the previous and next free block pointers are
|
|
|
|
|
equal, they must point to the bin.}
|
|
|
|
|
if LPreviousFreeBlock = LNextFreeBlock then
|
|
|
|
|
begin
|
|
|
|
|
{Get the bin number for this block size}
|
|
|
|
|
LBinNumber := (Cardinal(LNextFreeBlock) - Cardinal(@MediumBlockBins)) div SizeOf(TMediumFreeBlock);
|
|
|
|
|
LBinGroupNumber := LBinNumber div 32;
|
|
|
|
|
{Flag this bin as empty}
|
|
|
|
|
MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
|
|
|
|
|
and (not (1 shl (LBinNumber and 31)));
|
|
|
|
|
{Is the group now entirely empty?}
|
|
|
|
|
if MediumBlockBinBitmaps[LBinGroupNumber] = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Flag this group as empty}
|
|
|
|
|
MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
|
|
|
|
|
and (not (1 shl LBinGroupNumber));
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$else}
|
|
|
|
|
{Removes a medium block from the circular linked list of free blocks.
|
|
|
|
|
Does not change any header flags. Medium blocks should be locked
|
|
|
|
|
before calling this procedure.}
|
|
|
|
|
procedure RemoveMediumFreeBlock(APMediumFreeBlock: PMediumFreeBlock);
|
|
|
|
|
asm
|
|
|
|
|
{On entry: eax = APMediumFreeBlock}
|
|
|
|
|
{Get the current previous and next blocks}
|
|
|
|
|
mov ecx, TMediumFreeBlock[eax].NextFreeBlock
|
|
|
|
|
mov edx, TMediumFreeBlock[eax].PreviousFreeBlock
|
|
|
|
|
{Is this bin now empty? If the previous and next free block pointers are
|
|
|
|
|
equal, they must point to the bin.}
|
|
|
|
|
cmp ecx, edx
|
|
|
|
|
{Remove this block from the linked list}
|
|
|
|
|
mov TMediumFreeBlock[ecx].PreviousFreeBlock, edx
|
|
|
|
|
mov TMediumFreeBlock[edx].NextFreeBlock, ecx
|
|
|
|
|
{Is this bin now empty? If the previous and next free block pointers are
|
|
|
|
|
equal, they must point to the bin.}
|
|
|
|
|
je @BinIsNowEmpty
|
|
|
|
|
@Done:
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
@BinIsNowEmpty:
|
|
|
|
|
{Get the bin number for this block size in ecx}
|
|
|
|
|
sub ecx, offset MediumBlockBins
|
|
|
|
|
mov edx, ecx
|
|
|
|
|
shr ecx, 3
|
|
|
|
|
{Get the group number in edx}
|
|
|
|
|
movzx edx, dh
|
|
|
|
|
{Flag this bin as empty}
|
|
|
|
|
mov eax, -2
|
|
|
|
|
rol eax, cl
|
|
|
|
|
and dword ptr [MediumBlockBinBitmaps + edx * 4], eax
|
|
|
|
|
jnz @Done
|
|
|
|
|
{Flag this group as empty}
|
|
|
|
|
mov eax, -2
|
|
|
|
|
mov ecx, edx
|
|
|
|
|
rol eax, cl
|
|
|
|
|
and MediumBlockBinGroupBitmap, eax
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifndef AsmVersion}
|
|
|
|
|
{Inserts a medium block into the appropriate medium block bin.}
|
|
|
|
|
procedure InsertMediumBlockIntoBin(APMediumFreeBlock: PMediumFreeBlock; AMediumBlockSize: Cardinal);
|
|
|
|
|
var
|
|
|
|
|
LBinNumber, LBinGroupNumber: Cardinal;
|
|
|
|
|
LPBin, LPFirstFreeBlock: PMediumFreeBlock;
|
|
|
|
|
begin
|
|
|
|
|
{Get the bin number for this block size. Get the bin that holds blocks of at
|
|
|
|
|
least this size.}
|
|
|
|
|
LBinNumber := (AMediumBlockSize - MinimumMediumBlockSize) div MediumBlockGranularity;
|
|
|
|
|
if LBinNumber >= MediumBlockBinCount then
|
|
|
|
|
LBinNumber := MediumBlockBinCount - 1;
|
|
|
|
|
{Get the bin}
|
|
|
|
|
LPBin := @MediumBlockBins[LBinNumber];
|
|
|
|
|
{Bins are LIFO, se we insert this block as the first free block in the bin}
|
|
|
|
|
LPFirstFreeBlock := LPBin.NextFreeBlock;
|
|
|
|
|
APMediumFreeBlock.PreviousFreeBlock := LPBin;
|
|
|
|
|
APMediumFreeBlock.NextFreeBlock := LPFirstFreeBlock;
|
|
|
|
|
LPFirstFreeBlock.PreviousFreeBlock := APMediumFreeBlock;
|
|
|
|
|
LPBin.NextFreeBlock := APMediumFreeBlock;
|
|
|
|
|
{Was this bin empty?}
|
|
|
|
|
if LPFirstFreeBlock = LPBin then
|
|
|
|
|
begin
|
|
|
|
|
{Get the group number}
|
|
|
|
|
LBinGroupNumber := LBinNumber div 32;
|
|
|
|
|
{Flag this bin as used}
|
|
|
|
|
MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
|
|
|
|
|
or (1 shl (LBinNumber and 31));
|
|
|
|
|
{Flag the group as used}
|
|
|
|
|
MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
|
|
|
|
|
or (1 shl LBinGroupNumber);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$else}
|
|
|
|
|
{Inserts a medium block into the appropriate medium block bin.}
|
|
|
|
|
procedure InsertMediumBlockIntoBin(APMediumFreeBlock: PMediumFreeBlock; AMediumBlockSize: Cardinal);
|
|
|
|
|
asm
|
|
|
|
|
{On entry: eax = APMediumFreeBlock, edx = AMediumBlockSize}
|
|
|
|
|
{Get the bin number for this block size. Get the bin that holds blocks of at
|
|
|
|
|
least this size.}
|
|
|
|
|
sub edx, MinimumMediumBlockSize
|
|
|
|
|
shr edx, 8
|
|
|
|
|
{Validate the bin number}
|
|
|
|
|
sub edx, MediumBlockBinCount - 1
|
|
|
|
|
sbb ecx, ecx
|
|
|
|
|
and edx, ecx
|
|
|
|
|
add edx, MediumBlockBinCount - 1
|
|
|
|
|
{Get the bin in ecx}
|
|
|
|
|
lea ecx, [MediumBlockBins + edx * 8]
|
|
|
|
|
{Bins are LIFO, se we insert this block as the first free block in the bin}
|
|
|
|
|
mov edx, TMediumFreeBlock[ecx].NextFreeBlock
|
|
|
|
|
{Was this bin empty?}
|
|
|
|
|
cmp edx, ecx
|
|
|
|
|
mov TMediumFreeBlock[eax].PreviousFreeBlock, ecx
|
|
|
|
|
mov TMediumFreeBlock[eax].NextFreeBlock, edx
|
|
|
|
|
mov TMediumFreeBlock[edx].PreviousFreeBlock, eax
|
|
|
|
|
mov TMediumFreeBlock[ecx].NextFreeBlock, eax
|
|
|
|
|
{Was this bin empty?}
|
|
|
|
|
je @BinWasEmpty
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@BinWasEmpty:
|
|
|
|
|
{Get the bin number in ecx}
|
|
|
|
|
sub ecx, offset MediumBlockBins
|
|
|
|
|
mov edx, ecx
|
|
|
|
|
shr ecx, 3
|
|
|
|
|
{Get the group number in edx}
|
|
|
|
|
movzx edx, dh
|
|
|
|
|
{Flag this bin as not empty}
|
|
|
|
|
mov eax, 1
|
|
|
|
|
shl eax, cl
|
|
|
|
|
or dword ptr [MediumBlockBinBitmaps + edx * 4], eax
|
|
|
|
|
{Flag the group as not empty}
|
|
|
|
|
mov eax, 1
|
|
|
|
|
mov ecx, edx
|
|
|
|
|
shl eax, cl
|
|
|
|
|
or MediumBlockBinGroupBitmap, eax
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifndef AsmVersion}
|
|
|
|
|
{Bins what remains in the current sequential feed medium block pool. Medium
|
|
|
|
|
blocks must be locked.}
|
|
|
|
|
procedure BinMediumSequentialFeedRemainder;
|
|
|
|
|
var
|
|
|
|
|
LSequentialFeedFreeSize, LNextBlockSizeAndFlags: Cardinal;
|
|
|
|
|
LPRemainderBlock, LNextMediumBlock: Pointer;
|
|
|
|
|
begin
|
|
|
|
|
LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
|
|
|
|
|
if LSequentialFeedFreeSize > 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get the block after the open space}
|
|
|
|
|
LNextMediumBlock := LastSequentiallyFedMediumBlock;
|
|
|
|
|
LNextBlockSizeAndFlags := PCardinal(Cardinal(LNextMediumBlock) - BlockHeaderSize)^;
|
|
|
|
|
{Point to the remainder}
|
|
|
|
|
LPRemainderBlock := Pointer(Cardinal(LNextMediumBlock) - LSequentialFeedFreeSize);
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{Can the next block be combined with the remainder?}
|
|
|
|
|
if (LNextBlockSizeAndFlags and IsFreeBlockFlag) <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Increase the size of this block}
|
|
|
|
|
Inc(LSequentialFeedFreeSize, LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask);
|
|
|
|
|
{Remove the next block as well}
|
|
|
|
|
if (LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask) >= MinimumMediumBlockSize then
|
|
|
|
|
RemoveMediumFreeBlock(LNextMediumBlock);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
{Set the "previous block is free" flag of the next block}
|
|
|
|
|
PCardinal(Cardinal(LNextMediumBlock) - BlockHeaderSize)^ := LNextBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Store the size of the block as well as the flags}
|
|
|
|
|
PCardinal(Cardinal(LPRemainderBlock) - BlockHeaderSize)^ := LSequentialFeedFreeSize or IsMediumBlockFlag or IsFreeBlockFlag;
|
|
|
|
|
{Store the trailing size marker}
|
|
|
|
|
PCardinal(Cardinal(LPRemainderBlock) + LSequentialFeedFreeSize - 8)^ := LSequentialFeedFreeSize;
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{In full debug mode the sequential feed remainder will never be too small to
|
|
|
|
|
fit a full debug header.}
|
|
|
|
|
{Clear the user area of the block}
|
|
|
|
|
FillDWord(Pointer(Cardinal(LPRemainderBlock) + SizeOf(TFullDebugBlockHeader) + 4)^,
|
|
|
|
|
LSequentialFeedFreeSize - FullDebugBlockOverhead - 4,
|
|
|
|
|
{$ifndef CatchUseOfFreedInterfaces}DebugFillDWord{$else}Cardinal(@VMTBadInterface){$endif});
|
|
|
|
|
{We need to set a valid debug header and footer in the remainder}
|
|
|
|
|
PFullDebugBlockHeader(LPRemainderBlock).HeaderCheckSum := Cardinal(LPRemainderBlock);
|
|
|
|
|
PCardinal(Cardinal(LPRemainderBlock) + SizeOf(TFullDebugBlockHeader))^ := not Cardinal(LPRemainderBlock);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Bin this medium block}
|
|
|
|
|
if LSequentialFeedFreeSize >= MinimumMediumBlockSize then
|
|
|
|
|
InsertMediumBlockIntoBin(LPRemainderBlock, LSequentialFeedFreeSize);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$else}
|
|
|
|
|
{Bins what remains in the current sequential feed medium block pool. Medium
|
|
|
|
|
blocks must be locked.}
|
|
|
|
|
procedure BinMediumSequentialFeedRemainder;
|
|
|
|
|
asm
|
|
|
|
|
cmp MediumSequentialFeedBytesLeft, 0
|
|
|
|
|
jne @MustBinMedium
|
|
|
|
|
{Nothing to bin}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@MustBinMedium:
|
|
|
|
|
{Get a pointer to the last sequentially allocated medium block}
|
|
|
|
|
mov eax, LastSequentiallyFedMediumBlock
|
|
|
|
|
{Is the block that was last fed sequentially free?}
|
|
|
|
|
test byte ptr [eax - 4], IsFreeBlockFlag
|
|
|
|
|
jnz @LastBlockFedIsFree
|
|
|
|
|
{Set the "previous block is free" flag in the last block fed}
|
|
|
|
|
or dword ptr [eax - 4], PreviousMediumBlockIsFreeFlag
|
|
|
|
|
{Get the remainder in edx}
|
|
|
|
|
mov edx, MediumSequentialFeedBytesLeft
|
|
|
|
|
{Point eax to the start of the remainder}
|
|
|
|
|
sub eax, edx
|
|
|
|
|
@BinTheRemainder:
|
|
|
|
|
{Status: eax = start of remainder, edx = size of remainder}
|
|
|
|
|
{Store the size of the block as well as the flags}
|
|
|
|
|
lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
|
|
|
|
|
mov [eax - 4], ecx
|
|
|
|
|
{Store the trailing size marker}
|
|
|
|
|
mov [eax + edx - 8], edx
|
|
|
|
|
{Bin this medium block}
|
|
|
|
|
cmp edx, MinimumMediumBlockSize
|
|
|
|
|
jnb InsertMediumBlockIntoBin
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@LastBlockFedIsFree:
|
|
|
|
|
{Drop the flags}
|
|
|
|
|
mov edx, DropMediumAndLargeFlagsMask
|
|
|
|
|
and edx, [eax - 4]
|
|
|
|
|
{Free the last block fed}
|
|
|
|
|
cmp edx, MinimumMediumBlockSize
|
|
|
|
|
jb @DontRemoveLastFed
|
|
|
|
|
{Last fed block is free - remove it from its size bin}
|
|
|
|
|
call RemoveMediumFreeBlock
|
|
|
|
|
{Re-read eax and edx}
|
|
|
|
|
mov eax, LastSequentiallyFedMediumBlock
|
|
|
|
|
mov edx, DropMediumAndLargeFlagsMask
|
|
|
|
|
and edx, [eax - 4]
|
|
|
|
|
@DontRemoveLastFed:
|
|
|
|
|
{Get the number of bytes left in ecx}
|
|
|
|
|
mov ecx, MediumSequentialFeedBytesLeft
|
|
|
|
|
{Point eax to the start of the remainder}
|
|
|
|
|
sub eax, ecx
|
|
|
|
|
{edx = total size of the remainder}
|
|
|
|
|
add edx, ecx
|
|
|
|
|
jmp @BinTheRemainder
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Allocates a new sequential feed medium block pool and immediately splits off a
|
|
|
|
|
block of the requested size. The block size must be a multiple of 16 and
|
|
|
|
|
medium blocks must be locked.}
|
|
|
|
|
function AllocNewSequentialFeedMediumPool(AFirstBlockSize: Cardinal): Pointer;
|
|
|
|
|
var
|
|
|
|
|
LOldFirstMediumBlockPool: PMediumBlockPoolHeader;
|
|
|
|
|
LNewPool: Pointer;
|
|
|
|
|
begin
|
|
|
|
|
{Bin the current sequential feed remainder}
|
|
|
|
|
BinMediumSequentialFeedRemainder;
|
|
|
|
|
{Allocate a new sequential feed block pool}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LNewPool := VirtualAlloc(nil, MediumBlockPoolSize,
|
|
|
|
|
MEM_COMMIT{$ifdef AlwaysAllocateTopDown} or MEM_TOP_DOWN{$endif}, PAGE_READWRITE);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
if LNewPool <> nil then
|
|
|
|
|
begin
|
|
|
|
|
{Insert this block pool into the list of block pools}
|
|
|
|
|
LOldFirstMediumBlockPool := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
|
|
|
|
|
PMediumBlockPoolHeader(LNewPool).PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
|
|
|
|
|
MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := LNewPool;
|
|
|
|
|
PMediumBlockPoolHeader(LNewPool).NextMediumBlockPoolHeader := LOldFirstMediumBlockPool;
|
|
|
|
|
LOldFirstMediumBlockPool.PreviousMediumBlockPoolHeader := LNewPool;
|
|
|
|
|
{Store the sequential feed pool trailer}
|
|
|
|
|
PCardinal(Cardinal(LNewPool) + MediumBlockPoolSize - BlockHeaderSize)^ := IsMediumBlockFlag;
|
|
|
|
|
{Get the number of bytes still available}
|
|
|
|
|
MediumSequentialFeedBytesLeft := (MediumBlockPoolSize - MediumBlockPoolHeaderSize) - AFirstBlockSize;
|
|
|
|
|
{Get the result}
|
|
|
|
|
Result := Pointer(Cardinal(LNewPool) + MediumBlockPoolSize - AFirstBlockSize);
|
|
|
|
|
LastSequentiallyFedMediumBlock := Result;
|
|
|
|
|
{Store the block header}
|
|
|
|
|
PCardinal(Cardinal(Result) - BlockHeaderSize)^ := AFirstBlockSize or IsMediumBlockFlag;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Out of memory}
|
|
|
|
|
MediumSequentialFeedBytesLeft := 0;
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Frees a medium block pool. Medium blocks must be locked on entry.}
|
|
|
|
|
procedure FreeMediumBlockPool(AMediumBlockPool: PMediumBlockPoolHeader);
|
|
|
|
|
var
|
|
|
|
|
LPPreviousMediumBlockPoolHeader, LPNextMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
begin
|
|
|
|
|
{Remove this medium block pool from the linked list}
|
|
|
|
|
LPPreviousMediumBlockPoolHeader := AMediumBlockPool.PreviousMediumBlockPoolHeader;
|
|
|
|
|
LPNextMediumBlockPoolHeader := AMediumBlockPool.NextMediumBlockPoolHeader;
|
|
|
|
|
LPPreviousMediumBlockPoolHeader.NextMediumBlockPoolHeader := LPNextMediumBlockPoolHeader;
|
|
|
|
|
LPNextMediumBlockPoolHeader.PreviousMediumBlockPoolHeader := LPPreviousMediumBlockPoolHeader;
|
|
|
|
|
{Free the medium block pool}
|
|
|
|
|
VirtualFree(AMediumBlockPool, 0, MEM_RELEASE);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{-----------------Large Block Management------------------}
|
|
|
|
|
|
|
|
|
|
{Locks the large blocks}
|
|
|
|
|
procedure LockLargeBlocks;
|
|
|
|
|
begin
|
|
|
|
|
{Lock the large blocks}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
if IsMultiThread then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
while LockCmpxchg(0, 1, @LargeBlocksLocked) <> 0 do
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(InitialSleepTime);
|
|
|
|
|
if LockCmpxchg(0, 1, @LargeBlocksLocked) = 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(AdditionalSleepTime);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Allocates a Large block of at least ASize (actual size may be larger to
|
|
|
|
|
allow for alignment etc.). ASize must be the actual user requested size. This
|
|
|
|
|
procedure will pad it to the appropriate page boundary and also add the space
|
|
|
|
|
required by the header.}
|
|
|
|
|
function AllocateLargeBlock(ASize: Cardinal): Pointer;
|
|
|
|
|
var
|
|
|
|
|
LLargeUsedBlockSize: Cardinal;
|
|
|
|
|
LOldFirstLargeBlock: PLargeBlockHeader;
|
|
|
|
|
begin
|
|
|
|
|
{Pad the block size to include the header and granularity. We also add a
|
|
|
|
|
4-byte overhead so a huge block size is a multiple of 16 bytes less 4 (so we
|
|
|
|
|
can use a single move function for reallocating all block types)}
|
|
|
|
|
LLargeUsedBlockSize := (ASize + LargeBlockHeaderSize + LargeBlockGranularity - 1 + BlockHeaderSize)
|
|
|
|
|
and -LargeBlockGranularity;
|
|
|
|
|
{Get the Large block}
|
|
|
|
|
Result := VirtualAlloc(nil, LLargeUsedBlockSize, MEM_COMMIT or MEM_TOP_DOWN,
|
|
|
|
|
PAGE_READWRITE);
|
|
|
|
|
{Set the Large block fields}
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
|
|
|
|
{Set the large block size and flags}
|
|
|
|
|
PLargeBlockHeader(Result).UserAllocatedSize := ASize;
|
|
|
|
|
PLargeBlockHeader(Result).BlockSizeAndFlags := LLargeUsedBlockSize or IsLargeBlockFlag;
|
|
|
|
|
{Insert the large block into the linked list of large blocks}
|
|
|
|
|
LockLargeBlocks;
|
|
|
|
|
LOldFirstLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
|
|
|
|
|
PLargeBlockHeader(Result).PreviousLargeBlockHeader := @LargeBlocksCircularList;
|
|
|
|
|
LargeBlocksCircularList.NextLargeBlockHeader := Result;
|
|
|
|
|
PLargeBlockHeader(Result).NextLargeBlockHeader := LOldFirstLargeBlock;
|
|
|
|
|
LOldFirstLargeBlock.PreviousLargeBlockHeader := Result;
|
|
|
|
|
LargeBlocksLocked := False;
|
|
|
|
|
{Add the size of the header}
|
|
|
|
|
Inc(Cardinal(Result), LargeBlockHeaderSize);
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{Clear the user area of the block}
|
|
|
|
|
FillDWord(Pointer(Cardinal(Result) + SizeOf(TFullDebugBlockHeader) + 4)^,
|
|
|
|
|
LLargeUsedBlockSize - LargeBlockHeaderSize - FullDebugBlockOverhead - 4,
|
|
|
|
|
{$ifndef CatchUseOfFreedInterfaces}DebugFillDWord{$else}Cardinal(@VMTBadInterface){$endif});
|
|
|
|
|
{Set the debug header and footer}
|
|
|
|
|
PFullDebugBlockHeader(Result).HeaderCheckSum := Cardinal(Result);
|
|
|
|
|
PCardinal(Cardinal(Result) + SizeOf(TFullDebugBlockHeader))^ := not Cardinal(Result);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Frees a large block, returning 0 on success, -1 otherwise}
|
|
|
|
|
function FreeLargeBlock(APointer: Pointer): Integer;
|
|
|
|
|
var
|
|
|
|
|
LPreviousLargeBlockHeader, LNextLargeBlockHeader: PLargeBlockHeader;
|
|
|
|
|
{$ifndef Linux}
|
|
|
|
|
LRemainingSize: Cardinal;
|
|
|
|
|
LCurrentSegment: Pointer;
|
|
|
|
|
LMemInfo: TMemoryBasicInformation;
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{Point to the start of the large block}
|
|
|
|
|
APointer := Pointer(Cardinal(APointer) - LargeBlockHeaderSize);
|
|
|
|
|
{Get the previous and next large blocks}
|
|
|
|
|
LockLargeBlocks;
|
|
|
|
|
LPreviousLargeBlockHeader := PLargeBlockHeader(APointer).PreviousLargeBlockHeader;
|
|
|
|
|
LNextLargeBlockHeader := PLargeBlockHeader(APointer).NextLargeBlockHeader;
|
|
|
|
|
{$ifndef Linux}
|
|
|
|
|
{Is the large block segmented?}
|
|
|
|
|
if PLargeBlockHeader(APointer).BlockSizeAndFlags and LargeBlockIsSegmented = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
{Single segment large block: Try to free it}
|
|
|
|
|
if VirtualFree(APointer, 0, MEM_RELEASE) then
|
|
|
|
|
Result := 0
|
|
|
|
|
else
|
|
|
|
|
Result := -1;
|
|
|
|
|
{$ifndef Linux}
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{The large block is segmented - free all segments}
|
|
|
|
|
LCurrentSegment := APointer;
|
|
|
|
|
LRemainingSize := PLargeBlockHeader(APointer).BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
|
|
|
|
|
Result := 0;
|
|
|
|
|
while True do
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Get the size of the current segment}
|
|
|
|
|
VirtualQuery(LCurrentSegment, LMemInfo, SizeOf(LMemInfo));
|
|
|
|
|
{Free the segment}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
if not VirtualFree(LCurrentSegment, 0, MEM_RELEASE) then
|
|
|
|
|
begin
|
|
|
|
|
Result := -1;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Done?}
|
|
|
|
|
if LMemInfo.RegionSize >= LRemainingSize then
|
|
|
|
|
Break;
|
|
|
|
|
{Decrement the remaining size}
|
|
|
|
|
Dec(LRemainingSize, LMemInfo.RegionSize);
|
|
|
|
|
Inc(Cardinal(LCurrentSegment), LMemInfo.RegionSize);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Success?}
|
|
|
|
|
if Result = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Remove the large block from the linked list}
|
|
|
|
|
LNextLargeBlockHeader.PreviousLargeBlockHeader := LPreviousLargeBlockHeader;
|
|
|
|
|
LPreviousLargeBlockHeader.NextLargeBlockHeader := LNextLargeBlockHeader;
|
|
|
|
|
end;
|
|
|
|
|
{Unlock the large blocks}
|
|
|
|
|
LargeBlocksLocked := False;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{Reallocates a large block to at least the requested size. Returns the new
|
|
|
|
|
pointer, or nil on error}
|
|
|
|
|
function ReallocateLargeBlock(APointer: Pointer; ANewSize: Cardinal): Pointer;
|
|
|
|
|
var
|
|
|
|
|
LOldAvailableSize, LBlockHeader, LOldUserSize, LMinimumUpsize,
|
|
|
|
|
LNewAllocSize: Cardinal;
|
|
|
|
|
{$ifndef Linux}
|
|
|
|
|
LNewSegmentSize: Cardinal;
|
|
|
|
|
LNextSegmentPointer: Pointer;
|
|
|
|
|
LMemInfo: TMemoryBasicInformation;
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{Get the block header}
|
|
|
|
|
LBlockHeader := PCardinal(Cardinal(APointer) - BlockHeaderSize)^;
|
|
|
|
|
{Large block - size is (16 + 4) less than the allocated size}
|
|
|
|
|
LOldAvailableSize := (LBlockHeader and DropMediumAndLargeFlagsMask) - (LargeBlockHeaderSize + BlockHeaderSize);
|
|
|
|
|
{Is it an upsize or a downsize?}
|
|
|
|
|
if Cardinal(ANewSize) > LOldAvailableSize then
|
|
|
|
|
begin
|
|
|
|
|
{This pointer is being reallocated to a larger block and therefore it is
|
|
|
|
|
logical to assume that it may be enlarged again. Since reallocations are
|
|
|
|
|
expensive, there is a minimum upsize percentage to avoid unnecessary
|
|
|
|
|
future move operations.}
|
|
|
|
|
{Add 25% for large block upsizes}
|
|
|
|
|
LMinimumUpsize := Cardinal(LOldAvailableSize)
|
|
|
|
|
+ (Cardinal(LOldAvailableSize) shr 2);
|
|
|
|
|
if Cardinal(ANewSize) < LMinimumUpsize then
|
|
|
|
|
LNewAllocSize := LMinimumUpsize
|
|
|
|
|
else
|
|
|
|
|
LNewAllocSize := ANewSize;
|
|
|
|
|
{$ifndef Linux}
|
|
|
|
|
{Can another large block segment be allocated directly after this segment,
|
|
|
|
|
thus negating the need to move the data?}
|
|
|
|
|
LNextSegmentPointer := Pointer(Cardinal(APointer) - LargeBlockHeaderSize + (LBlockHeader and DropMediumAndLargeFlagsMask));
|
|
|
|
|
VirtualQuery(LNextSegmentPointer, LMemInfo, SizeOf(LMemInfo));
|
|
|
|
|
if (LMemInfo.State = MEM_FREE) then
|
|
|
|
|
begin
|
|
|
|
|
{Round the region size to the previous 64K}
|
|
|
|
|
LMemInfo.RegionSize := LMemInfo.RegionSize and -LargeBlockGranularity;
|
|
|
|
|
{Enough space to grow in place?}
|
|
|
|
|
if (LMemInfo.RegionSize > (ANewSize - LOldAvailableSize)) then
|
|
|
|
|
begin
|
|
|
|
|
{There is enough space after the block to extend it - determine by how
|
|
|
|
|
much}
|
|
|
|
|
LNewSegmentSize := (LNewAllocSize - LOldAvailableSize + LargeBlockGranularity - 1) and -LargeBlockGranularity;
|
|
|
|
|
if LNewSegmentSize > LMemInfo.RegionSize then
|
|
|
|
|
LNewSegmentSize := LMemInfo.RegionSize;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Attempy to reserve the address range (which will fail if another
|
|
|
|
|
thread has just reserved it) and commit it immediately afterwards.}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
if (VirtualAlloc(LNextSegmentPointer, LNewSegmentSize, MEM_RESERVE, PAGE_READWRITE) <> nil)
|
|
|
|
|
and (VirtualAlloc(LNextSegmentPointer, LNewSegmentSize, MEM_COMMIT, PAGE_READWRITE) <> nil) then
|
|
|
|
|
begin
|
|
|
|
|
{Update the requested size}
|
|
|
|
|
PLargeBlockHeader(Cardinal(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
|
|
|
|
|
PLargeBlockHeader(Cardinal(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags :=
|
|
|
|
|
(PLargeBlockHeader(Cardinal(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags + LNewSegmentSize)
|
|
|
|
|
or LargeBlockIsSegmented;
|
|
|
|
|
{Success}
|
|
|
|
|
Result := APointer;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Could not resize in place: Allocate the new block}
|
|
|
|
|
Result := FastGetMem(LNewAllocSize);
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{If it's a large block - store the actual user requested size (it may
|
2006-04-15 03:44:05 -04:00
|
|
|
|
not be if the block that is being reallocated from was previously
|
|
|
|
|
downsized)}
|
|
|
|
|
if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
|
|
|
|
|
PLargeBlockHeader(Cardinal(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
|
|
|
|
|
{The user allocated size is stored for large blocks}
|
|
|
|
|
LOldUserSize := PLargeBlockHeader(Cardinal(APointer) - LargeBlockHeaderSize).UserAllocatedSize;
|
|
|
|
|
{The number of bytes to move is the old user size.}
|
|
|
|
|
{$ifdef UseCustomVariableSizeMoveRoutines}
|
|
|
|
|
MoveX16L4(APointer^, Result^, LOldUserSize);
|
|
|
|
|
{$else}
|
|
|
|
|
System.Move(APointer^, Result^, LOldUserSize);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Free the old block}
|
|
|
|
|
FastFreeMem(APointer);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{It's a downsize: do we need to reallocate? Only if the new size is less
|
|
|
|
|
than half the old size}
|
|
|
|
|
if Cardinal(ANewSize) >= (LOldAvailableSize shr 1) then
|
|
|
|
|
begin
|
|
|
|
|
{No need to reallocate}
|
|
|
|
|
Result := APointer;
|
|
|
|
|
{Update the requested size}
|
|
|
|
|
PLargeBlockHeader(Cardinal(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{The block is less than half the old size, and the current size is
|
|
|
|
|
greater than the minimum block size allowing a downsize: reallocate}
|
|
|
|
|
Result := FastGetMem(ANewSize);
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
|
|
|
|
{Still a large block? -> Set the user size}
|
|
|
|
|
if ANewSize > (MaximumMediumBlockSize - BlockHeaderSize) then
|
|
|
|
|
PLargeBlockHeader(Cardinal(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
|
|
|
|
|
{Move the data across}
|
|
|
|
|
{$ifdef UseCustomVariableSizeMoveRoutines}
|
|
|
|
|
{$ifdef Align16Bytes}
|
|
|
|
|
MoveX16L4(APointer^, Result^, ANewSize);
|
|
|
|
|
{$else}
|
|
|
|
|
MoveX8L4(APointer^, Result^, ANewSize);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$else}
|
|
|
|
|
System.Move(APointer^, Result^, ANewSize);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Free the old block}
|
|
|
|
|
FastFreeMem(APointer);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{---------------------Replacement Memory Manager Interface---------------------}
|
|
|
|
|
|
|
|
|
|
{$ifndef ASMVersion}
|
|
|
|
|
{Replacement for SysGetMem (pascal version)}
|
|
|
|
|
function FastGetMem(ASize: Integer): Pointer;
|
|
|
|
|
var
|
|
|
|
|
LMediumBlock{$ifndef FullDebugMode}, LNextFreeBlock, LSecondSplit{$endif}: PMediumFreeBlock;
|
|
|
|
|
LNextMediumBlockHeader: PCardinal;
|
|
|
|
|
LBlockSize, LAvailableBlockSize{$ifndef FullDebugMode}, LSecondSplitSize{$endif}: Cardinal;
|
|
|
|
|
LPSmallBlockType: PSmallBlockType;
|
|
|
|
|
LPSmallBlockPool, LPNewFirstPool: PSmallBlockPoolHeader;
|
|
|
|
|
LBinNumber: Cardinal;
|
|
|
|
|
LNewFirstFreeBlock: Pointer;
|
|
|
|
|
LPMediumBin: PMediumFreeBlock;
|
|
|
|
|
LSequentialFeedFreeSize: Cardinal;
|
|
|
|
|
{$ifndef FullDebugMode}LBinGroupsMasked, {$endif}LBinGroupMasked, LBinGroupNumber: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Is it a small block? -> Take the header size into account when
|
|
|
|
|
determining the required block size}
|
|
|
|
|
if Cardinal(ASize) <= (MaximumSmallBlockSize - BlockHeaderSize) then
|
|
|
|
|
begin
|
|
|
|
|
{-------------------------Allocate a small block---------------------------}
|
|
|
|
|
{Get the block type from the size}
|
|
|
|
|
LPSmallBlockType := PSmallBlockType(AllocSize2SmallBlockTypeIndX4[
|
|
|
|
|
(Cardinal(ASize) + (BlockHeaderSize - 1)) div SmallBlockGranularity] * 8
|
|
|
|
|
+ Cardinal(@SmallBlockTypes));
|
|
|
|
|
{Lock the block type}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
if IsMultiThread then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
while True do
|
|
|
|
|
begin
|
|
|
|
|
{Try to lock the small block type}
|
|
|
|
|
if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Try the next block type}
|
|
|
|
|
Inc(Cardinal(LPSmallBlockType), SizeOf(TSmallBlockType));
|
|
|
|
|
if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Try up to two sizes past the requested size}
|
|
|
|
|
Inc(Cardinal(LPSmallBlockType), SizeOf(TSmallBlockType));
|
|
|
|
|
if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{All three sizes locked - given up and sleep}
|
|
|
|
|
Dec(Cardinal(LPSmallBlockType), 2 * SizeOf(TSmallBlockType));
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Both this block type and the next is in use: sleep}
|
|
|
|
|
Sleep(InitialSleepTime);
|
|
|
|
|
{Try the lock again}
|
|
|
|
|
if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Sleep longer}
|
|
|
|
|
Sleep(AdditionalSleepTime);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{Get the first pool with free blocks}
|
|
|
|
|
LPSmallBlockPool := LPSmallBlockType.NextPartiallyFreePool;
|
|
|
|
|
{Is the pool valid?}
|
|
|
|
|
if Cardinal(LPSmallBlockPool) <> Cardinal(LPSmallBlockType) then
|
|
|
|
|
begin
|
|
|
|
|
{Get the first free offset}
|
|
|
|
|
Result := LPSmallBlockPool.FirstFreeBlock;
|
|
|
|
|
{Get the new first free block}
|
|
|
|
|
LNewFirstFreeBlock := PPointer(Cardinal(Result) - 4)^;
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{The block should be free}
|
|
|
|
|
if (Cardinal(LNewFirstFreeBlock) and ExtractSmallFlagsMask) <> IsFreeBlockFlag then
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
LNewFirstFreeBlock := Pointer(Cardinal(LNewFirstFreeBlock) and DropSmallFlagsMask);
|
|
|
|
|
{Increment the number of used blocks}
|
|
|
|
|
Inc(LPSmallBlockPool.BlocksInUse);
|
|
|
|
|
{Set the new first free block}
|
|
|
|
|
LPSmallBlockPool.FirstFreeBlock := LNewFirstFreeBlock;
|
|
|
|
|
{Is the pool now full?}
|
|
|
|
|
if LNewFirstFreeBlock = nil then
|
|
|
|
|
begin
|
|
|
|
|
{Pool is full - remove it from the partially free list}
|
|
|
|
|
LPNewFirstPool := LPSmallBlockPool.NextPartiallyFreePool;
|
|
|
|
|
LPSmallBlockType.NextPartiallyFreePool := LPNewFirstPool;
|
|
|
|
|
LPNewFirstPool.PreviousPartiallyFreePool := PSmallBlockPoolHeader(LPSmallBlockType);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Try to feed a small block sequentially}
|
|
|
|
|
Result := LPSmallBlockType.NextSequentialFeedBlockAddress;
|
|
|
|
|
{Can another block fit?}
|
|
|
|
|
if Cardinal(Result) <= Cardinal(LPSmallBlockType.MaxSequentialFeedBlockAddress) then
|
|
|
|
|
begin
|
|
|
|
|
{Get the sequential feed block pool}
|
|
|
|
|
LPSmallBlockPool := LPSmallBlockType.CurrentSequentialFeedPool;
|
|
|
|
|
{Increment the number of used blocks in the sequential feed pool}
|
|
|
|
|
Inc(LPSmallBlockPool.BlocksInUse);
|
|
|
|
|
{Store the next sequential feed block address}
|
|
|
|
|
LPSmallBlockType.NextSequentialFeedBlockAddress := Pointer(Cardinal(Result) + LPSmallBlockType.BlockSize);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Need to allocate a pool: Lock the medium blocks}
|
|
|
|
|
LockMediumBlocks;
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{Are there any available blocks of a suitable size?}
|
|
|
|
|
LBinGroupsMasked := MediumBlockBinGroupBitmap and ($ffffff00 or LPSmallBlockType.AllowedGroupsForBlockPoolBitmap);
|
|
|
|
|
if LBinGroupsMasked <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get the bin group with free blocks}
|
|
|
|
|
LBinGroupNumber := FindFirstSetBit(LBinGroupsMasked);
|
|
|
|
|
{Get the bin in the group with free blocks}
|
|
|
|
|
LBinNumber := FindFirstSetBit(MediumBlockBinBitmaps[LBinGroupNumber])
|
|
|
|
|
+ LBinGroupNumber * 32;
|
|
|
|
|
LPMediumBin := @MediumBlockBins[LBinNumber];
|
|
|
|
|
{Get the first block in the bin}
|
|
|
|
|
LMediumBlock := LPMediumBin.NextFreeBlock;
|
|
|
|
|
{Remove the first block from the linked list (LIFO)}
|
|
|
|
|
LNextFreeBlock := LMediumBlock.NextFreeBlock;
|
|
|
|
|
LPMediumBin.NextFreeBlock := LNextFreeBlock;
|
|
|
|
|
LNextFreeBlock.PreviousFreeBlock := LPMediumBin;
|
|
|
|
|
{Is this bin now empty?}
|
|
|
|
|
if LNextFreeBlock = LPMediumBin then
|
|
|
|
|
begin
|
|
|
|
|
{Flag this bin as empty}
|
|
|
|
|
MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
|
|
|
|
|
and (not (1 shl (LBinNumber and 31)));
|
|
|
|
|
{Is the group now entirely empty?}
|
|
|
|
|
if MediumBlockBinBitmaps[LBinGroupNumber] = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Flag this group as empty}
|
|
|
|
|
MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
|
|
|
|
|
and (not (1 shl LBinGroupNumber));
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{Get the size of the available medium block}
|
|
|
|
|
LBlockSize := PCardinal(Cardinal(LMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{Check that this block is actually free and the next and previous blocks
|
|
|
|
|
are both in use.}
|
|
|
|
|
if ((PCardinal(Cardinal(LMediumBlock) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag))
|
|
|
|
|
or ((PCardinal(Cardinal(LMediumBlock) + (PCardinal(Cardinal(LMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and IsFreeBlockFlag) <> 0)
|
|
|
|
|
then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Should the block be split?}
|
|
|
|
|
if LBlockSize >= MaximumSmallBlockPoolSize then
|
|
|
|
|
begin
|
|
|
|
|
{Get the size of the second split}
|
|
|
|
|
LSecondSplitSize := LBlockSize - LPSmallBlockType.OptimalBlockPoolSize;
|
|
|
|
|
{Adjust the block size}
|
|
|
|
|
LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
|
|
|
|
|
{Split the block in two}
|
|
|
|
|
LSecondSplit := PMediumFreeBlock(Cardinal(LMediumBlock) + LBlockSize);
|
|
|
|
|
PCardinal(Cardinal(LSecondSplit) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
|
|
|
|
|
{Store the size of the second split as the second last dword}
|
|
|
|
|
PCardinal(Cardinal(LSecondSplit) + LSecondSplitSize - 8)^ := LSecondSplitSize;
|
|
|
|
|
{Put the remainder in a bin (it will be big enough)}
|
|
|
|
|
InsertMediumBlockIntoBin(LSecondSplit, LSecondSplitSize);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Mark this block as used in the block following it}
|
|
|
|
|
LNextMediumBlockHeader := PCardinal(Cardinal(LMediumBlock) + LBlockSize - BlockHeaderSize);
|
|
|
|
|
LNextMediumBlockHeader^ := LNextMediumBlockHeader^ and (not PreviousMediumBlockIsFreeFlag);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
{Check the sequential feed medium block pool for space}
|
|
|
|
|
LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
|
|
|
|
|
if LSequentialFeedFreeSize >= LPSmallBlockType.MinimumBlockPoolSize then
|
|
|
|
|
begin
|
|
|
|
|
{Enough sequential feed space: Will the remainder be usable?}
|
|
|
|
|
if LSequentialFeedFreeSize >= (LPSmallBlockType.OptimalBlockPoolSize + MinimumMediumBlockSize) then
|
|
|
|
|
begin
|
|
|
|
|
LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
LBlockSize := LSequentialFeedFreeSize;
|
|
|
|
|
{Get the block}
|
|
|
|
|
LMediumBlock := Pointer(Cardinal(LastSequentiallyFedMediumBlock) - LBlockSize);
|
|
|
|
|
{Update the sequential feed parameters}
|
|
|
|
|
LastSequentiallyFedMediumBlock := LMediumBlock;
|
|
|
|
|
MediumSequentialFeedBytesLeft := LSequentialFeedFreeSize - LBlockSize;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Need to allocate a new sequential feed medium block pool: use the
|
|
|
|
|
optimal size for this small block pool}
|
|
|
|
|
LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
|
|
|
|
|
{Allocate the medium block pool}
|
|
|
|
|
LMediumBlock := AllocNewSequentialFeedMediumPool(LBlockSize);
|
|
|
|
|
if LMediumBlock = nil then
|
|
|
|
|
begin
|
|
|
|
|
{Out of memory}
|
|
|
|
|
{Unlock the medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{Unlock the block type}
|
|
|
|
|
LPSmallBlockType.BlockTypeLocked := False;
|
|
|
|
|
{Failed}
|
|
|
|
|
Result := nil;
|
|
|
|
|
{done}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Mark this block as in use}
|
|
|
|
|
{Set the size and flags for this block}
|
|
|
|
|
PCardinal(Cardinal(LMediumBlock) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag or IsSmallBlockPoolInUseFlag;
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{Set up the block pool}
|
|
|
|
|
LPSmallBlockPool := PSmallBlockPoolHeader(LMediumBlock);
|
|
|
|
|
LPSmallBlockPool.BlockType := LPSmallBlockType;
|
|
|
|
|
LPSmallBlockPool.FirstFreeBlock := nil;
|
|
|
|
|
LPSmallBlockPool.BlocksInUse := 1;
|
|
|
|
|
{Set it up for sequential block serving}
|
|
|
|
|
LPSmallBlockType.CurrentSequentialFeedPool := LPSmallBlockPool;
|
|
|
|
|
Result := Pointer(Cardinal(LPSmallBlockPool) + SmallBlockPoolHeaderSize);
|
|
|
|
|
LPSmallBlockType.NextSequentialFeedBlockAddress := Pointer(Cardinal(Result) + LPSmallBlockType.BlockSize);
|
|
|
|
|
LPSmallBlockType.MaxSequentialFeedBlockAddress := Pointer(Cardinal(LPSmallBlockPool) + LBlockSize - LPSmallBlockType.BlockSize);
|
|
|
|
|
end;
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{Clear the user area of the block}
|
|
|
|
|
FillDWord(Pointer(Cardinal(Result) + (SizeOf(TFullDebugBlockHeader) + 4))^,
|
|
|
|
|
LPSmallBlockType.BlockSize - FullDebugBlockOverhead - 4,
|
|
|
|
|
{$ifndef CatchUseOfFreedInterfaces}DebugFillDWord{$else}Cardinal(@VMTBadInterface){$endif});
|
|
|
|
|
{Block was fed sequentially - we need to set a valid debug header}
|
|
|
|
|
PFullDebugBlockHeader(Result).HeaderCheckSum := Cardinal(Result);
|
|
|
|
|
PCardinal(Cardinal(Result) + SizeOf(TFullDebugBlockHeader))^ := not Cardinal(Result);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{Unlock the block type}
|
|
|
|
|
LPSmallBlockType.BlockTypeLocked := False;
|
|
|
|
|
{Set the block header}
|
|
|
|
|
PCardinal(Cardinal(Result) - BlockHeaderSize)^ := Cardinal(LPSmallBlockPool);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Medium block or Large block?}
|
|
|
|
|
if Cardinal(ASize) <= (MaximumMediumBlockSize - BlockHeaderSize) then
|
|
|
|
|
begin
|
|
|
|
|
{------------------------Allocate a medium block--------------------------}
|
|
|
|
|
{Get the block size and bin number for this block size. Block sizes are
|
|
|
|
|
rounded up to the next bin size.}
|
|
|
|
|
LBlockSize := ((Cardinal(ASize) + (MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset))
|
|
|
|
|
and -MediumBlockGranularity) + MediumBlockSizeOffset;
|
|
|
|
|
{Get the bin number}
|
|
|
|
|
LBinNumber := (LBlockSize - MinimumMediumBlockSize) div MediumBlockGranularity;
|
|
|
|
|
{Lock the medium blocks}
|
|
|
|
|
LockMediumBlocks;
|
|
|
|
|
{Calculate the bin group}
|
|
|
|
|
LBinGroupNumber := LBinNumber div 32;
|
|
|
|
|
{Is there a suitable block inside this group?}
|
|
|
|
|
LBinGroupMasked := MediumBlockBinBitmaps[LBinGroupNumber] and -(1 shl (LBinNumber and 31));
|
|
|
|
|
if LBinGroupMasked <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get the actual bin number}
|
|
|
|
|
LBinNumber := FindFirstSetBit(LBinGroupMasked) + LBinGroupNumber * 32;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{Try all groups greater than this group}
|
|
|
|
|
LBinGroupsMasked := MediumBlockBinGroupBitmap and -(2 shl LBinGroupNumber);
|
|
|
|
|
if LBinGroupsMasked <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{There is a suitable group with space: get the bin number}
|
|
|
|
|
LBinGroupNumber := FindFirstSetBit(LBinGroupsMasked);
|
|
|
|
|
{Get the bin in the group with free blocks}
|
|
|
|
|
LBinNumber := FindFirstSetBit(MediumBlockBinBitmaps[LBinGroupNumber])
|
|
|
|
|
+ LBinGroupNumber * 32;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
{There are no bins with a suitable block: Sequentially feed the required block}
|
|
|
|
|
LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
|
|
|
|
|
if LSequentialFeedFreeSize >= LBlockSize then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{In full debug mode a medium block must have enough bytes to fit
|
|
|
|
|
all the debug info, so we must make sure there are no tiny medium
|
|
|
|
|
blocks at the start of the pool.}
|
|
|
|
|
if LSequentialFeedFreeSize - LBlockSize < (FullDebugBlockOverhead + BlockHeaderSize) then
|
|
|
|
|
LBlockSize := LSequentialFeedFreeSize;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Block can be fed sequentially}
|
|
|
|
|
Result := Pointer(Cardinal(LastSequentiallyFedMediumBlock) - LBlockSize);
|
|
|
|
|
{Store the last sequentially fed block}
|
|
|
|
|
LastSequentiallyFedMediumBlock := Result;
|
|
|
|
|
{Store the remaining bytes}
|
|
|
|
|
MediumSequentialFeedBytesLeft := LSequentialFeedFreeSize - LBlockSize;
|
|
|
|
|
{Set the flags for the block}
|
|
|
|
|
PCardinal(Cardinal(Result) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Need to allocate a new sequential feed block}
|
|
|
|
|
Result := AllocNewSequentialFeedMediumPool(LBlockSize);
|
|
|
|
|
end;
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{Block was fed sequentially - we need to set a valid debug header}
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
|
|
|
|
PFullDebugBlockHeader(Result).HeaderCheckSum := Cardinal(Result);
|
|
|
|
|
PCardinal(Cardinal(Result) + SizeOf(TFullDebugBlockHeader))^ := not Cardinal(Result);
|
|
|
|
|
{Clear the user area of the block}
|
|
|
|
|
FillDWord(Pointer(Cardinal(Result) + SizeOf(TFullDebugBlockHeader) + 4)^,
|
|
|
|
|
LBlockSize - FullDebugBlockOverhead - 4,
|
|
|
|
|
{$ifndef CatchUseOfFreedInterfaces}DebugFillDWord{$else}Cardinal(@VMTBadInterface){$endif});
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Done}
|
|
|
|
|
MediumBlocksLocked := False;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{If we get here we have a valid LBinGroupNumber and LBinNumber:
|
|
|
|
|
Use the first block in the bin, splitting it if necessary}
|
|
|
|
|
{Get a pointer to the bin}
|
|
|
|
|
LPMediumBin := @MediumBlockBins[LBinNumber];
|
|
|
|
|
{Get the result}
|
|
|
|
|
Result := LPMediumBin.NextFreeBlock;
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{Check that this block is actually free and the next and previous blocks
|
|
|
|
|
are both in use (except in full debug mode).}
|
|
|
|
|
if ((PCardinal(Cardinal(Result) - BlockHeaderSize)^ and {$ifndef FullDebugMode}ExtractMediumAndLargeFlagsMask{$else}(IsMediumBlockFlag or IsFreeBlockFlag){$endif}) <> (IsFreeBlockFlag or IsMediumBlockFlag))
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
or ((PCardinal(Cardinal(Result) + (PCardinal(Cardinal(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and (ExtractMediumAndLargeFlagsMask - IsSmallBlockPoolInUseFlag)) <> (IsMediumBlockFlag or PreviousMediumBlockIsFreeFlag))
|
|
|
|
|
{$endif}
|
|
|
|
|
then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Remove the block from the bin containing it}
|
|
|
|
|
RemoveMediumFreeBlock(Result);
|
|
|
|
|
{Get the block size}
|
|
|
|
|
LAvailableBlockSize := PCardinal(Cardinal(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{Is it an exact fit or not?}
|
|
|
|
|
LSecondSplitSize := LAvailableBlockSize - LBlockSize;
|
|
|
|
|
if LSecondSplitSize <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Split the block in two}
|
|
|
|
|
LSecondSplit := PMediumFreeBlock(Cardinal(Result) + LBlockSize);
|
|
|
|
|
{Set the size of the second split}
|
|
|
|
|
PCardinal(Cardinal(LSecondSplit) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
|
|
|
|
|
{Store the size of the second split as the second last dword}
|
|
|
|
|
PCardinal(Cardinal(LSecondSplit) + LSecondSplitSize - 8)^ := LSecondSplitSize;
|
|
|
|
|
{Put the remainder in a bin if it is big enough}
|
|
|
|
|
if LSecondSplitSize >= MinimumMediumBlockSize then
|
|
|
|
|
InsertMediumBlockIntoBin(LSecondSplit, LSecondSplitSize);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$else}
|
|
|
|
|
{In full debug mode blocks are never split or coalesced}
|
|
|
|
|
LBlockSize := LAvailableBlockSize;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Mark this block as used in the block following it}
|
|
|
|
|
LNextMediumBlockHeader := Pointer(Cardinal(Result) + LBlockSize - BlockHeaderSize);
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{The next block must be in use}
|
|
|
|
|
if (LNextMediumBlockHeader^ and (ExtractMediumAndLargeFlagsMask - IsSmallBlockPoolInUseFlag)) <> (IsMediumBlockFlag or PreviousMediumBlockIsFreeFlag) then
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
LNextMediumBlockHeader^ :=
|
|
|
|
|
LNextMediumBlockHeader^ and (not PreviousMediumBlockIsFreeFlag);
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
end;
|
|
|
|
|
{Set the size and flags for this block}
|
|
|
|
|
PCardinal(Cardinal(Result) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag;
|
|
|
|
|
{$else}
|
|
|
|
|
{In full debug mode blocks are never split or coalesced}
|
|
|
|
|
Dec(PCardinal(Cardinal(Result) - BlockHeaderSize)^, IsFreeBlockFlag);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Unlock the medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Allocate a Large block}
|
|
|
|
|
if ASize > 0 then
|
|
|
|
|
Result := AllocateLargeBlock(ASize)
|
|
|
|
|
else
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$else}
|
|
|
|
|
{Replacement for SysGetMem (asm version)}
|
|
|
|
|
function FastGetMem(ASize: Integer): Pointer;
|
|
|
|
|
asm
|
|
|
|
|
{On entry:
|
|
|
|
|
eax = ASize}
|
|
|
|
|
{Since most allocations are for small blocks, determine the small block type
|
|
|
|
|
index so long}
|
|
|
|
|
lea edx, [eax + BlockHeaderSize - 1]
|
|
|
|
|
{$ifdef Align16Bytes}
|
|
|
|
|
shr edx, 4
|
|
|
|
|
{$else}
|
|
|
|
|
shr edx, 3
|
|
|
|
|
{$endif}
|
|
|
|
|
{Is it a small block?}
|
|
|
|
|
cmp eax, (MaximumSmallBlockSize - BlockHeaderSize)
|
|
|
|
|
{Save ebx}
|
|
|
|
|
push ebx
|
|
|
|
|
{Get the IsMultiThread variable so long}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
mov cl, IsMultiThread
|
|
|
|
|
{$endif}
|
|
|
|
|
{Is it a small block?}
|
|
|
|
|
ja @NotASmallBlock
|
|
|
|
|
{Do we need to lock the block type?}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
test cl, cl
|
|
|
|
|
{$endif}
|
|
|
|
|
{Get the small block type in ebx}
|
|
|
|
|
movzx eax, byte ptr [AllocSize2SmallBlockTypeIndX4 + edx]
|
|
|
|
|
lea ebx, [SmallBlockTypes + eax * 8]
|
|
|
|
|
{Do we need to lock the block type?}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
jnz @LockBlockTypeLoop
|
|
|
|
|
{$else}
|
|
|
|
|
jmp @LockBlockTypeLoop
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
{$endif}
|
|
|
|
|
@GotLockOnSmallBlockType:
|
|
|
|
|
{Find the next free block: Get the first pool with free blocks in edx}
|
|
|
|
|
mov edx, TSmallBlockType[ebx].NextPartiallyFreePool
|
|
|
|
|
{Get the first free block (or the next sequential feed address if edx = ebx)}
|
|
|
|
|
mov eax, TSmallBlockPoolHeader[edx].FirstFreeBlock
|
|
|
|
|
{Get the drop flags mask in ecx so long}
|
|
|
|
|
mov ecx, DropSmallFlagsMask
|
|
|
|
|
{Is there a pool with free blocks?}
|
|
|
|
|
cmp edx, ebx
|
|
|
|
|
je @TrySmallSequentialFeed
|
|
|
|
|
{Increment the number of used blocks}
|
|
|
|
|
add TSmallBlockPoolHeader[edx].BlocksInUse, 1
|
|
|
|
|
{Get the new first free block}
|
|
|
|
|
and ecx, [eax - 4]
|
|
|
|
|
{Set the new first free block}
|
|
|
|
|
mov TSmallBlockPoolHeader[edx].FirstFreeBlock, ecx
|
|
|
|
|
{Set the block header}
|
|
|
|
|
mov [eax - 4], edx
|
|
|
|
|
{Is the chunk now full?}
|
|
|
|
|
jz @RemoveSmallPool
|
|
|
|
|
{Unlock the block type}
|
|
|
|
|
mov TSmallBlockType[ebx].BlockTypeLocked, False
|
|
|
|
|
{Restore ebx}
|
|
|
|
|
pop ebx
|
|
|
|
|
{All done}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
{$endif}
|
|
|
|
|
nop
|
|
|
|
|
@TrySmallSequentialFeed:
|
|
|
|
|
{Try to feed a small block sequentially: Get the sequential feed block pool}
|
|
|
|
|
mov edx, TSmallBlockType[ebx].CurrentSequentialFeedPool
|
|
|
|
|
{Get the next sequential feed address so long}
|
|
|
|
|
movzx ecx, TSmallBlockType[ebx].BlockSize
|
|
|
|
|
add ecx, eax
|
|
|
|
|
{Can another block fit?}
|
|
|
|
|
cmp eax, TSmallBlockType[ebx].MaxSequentialFeedBlockAddress
|
|
|
|
|
ja @AllocateSmallBlockPool
|
|
|
|
|
{Increment the number of used blocks in the sequential feed pool}
|
|
|
|
|
add TSmallBlockPoolHeader[edx].BlocksInUse, 1
|
|
|
|
|
{Store the next sequential feed block address}
|
|
|
|
|
mov TSmallBlockType[ebx].NextSequentialFeedBlockAddress, ecx
|
|
|
|
|
{Unlock the block type}
|
|
|
|
|
mov TSmallBlockType[ebx].BlockTypeLocked, False
|
|
|
|
|
{Set the block header}
|
|
|
|
|
mov [eax - 4], edx
|
|
|
|
|
{Restore ebx}
|
|
|
|
|
pop ebx
|
|
|
|
|
{All done}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@RemoveSmallPool:
|
|
|
|
|
{Pool is full - remove it from the partially free list}
|
|
|
|
|
mov ecx, TSmallBlockPoolHeader[edx].NextPartiallyFreePool
|
|
|
|
|
mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, ebx
|
|
|
|
|
mov TSmallBlockType[ebx].NextPartiallyFreePool, ecx
|
|
|
|
|
{Unlock the block type}
|
|
|
|
|
mov TSmallBlockType[ebx].BlockTypeLocked, False
|
|
|
|
|
{Restore ebx}
|
|
|
|
|
pop ebx
|
|
|
|
|
{All done}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@LockBlockTypeLoop:
|
|
|
|
|
mov eax, $100
|
|
|
|
|
{Attempt to grab the block type}
|
|
|
|
|
lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
|
|
|
|
|
je @GotLockOnSmallBlockType
|
|
|
|
|
{Try the next size}
|
|
|
|
|
add ebx, Type(TSmallBlockType)
|
|
|
|
|
mov eax, $100
|
|
|
|
|
lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
|
|
|
|
|
je @GotLockOnSmallBlockType
|
|
|
|
|
{Try the next size (up to two sizes larger)}
|
|
|
|
|
add ebx, Type(TSmallBlockType)
|
|
|
|
|
mov eax, $100
|
|
|
|
|
lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
|
|
|
|
|
je @GotLockOnSmallBlockType
|
|
|
|
|
{Block type and two sizes larger are all locked - give up and sleep}
|
|
|
|
|
sub ebx, 2 * Type(TSmallBlockType)
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Couldn't grab the block type - sleep and try again}
|
|
|
|
|
push InitialSleepTime
|
|
|
|
|
call Sleep
|
|
|
|
|
{Try again}
|
|
|
|
|
mov eax, $100
|
|
|
|
|
{Attempt to grab the block type}
|
|
|
|
|
lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
|
|
|
|
|
je @GotLockOnSmallBlockType
|
|
|
|
|
{Couldn't grab the block type - sleep and try again}
|
|
|
|
|
push AdditionalSleepTime
|
|
|
|
|
call Sleep
|
|
|
|
|
{Try again}
|
|
|
|
|
jmp @LockBlockTypeLoop
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
nop
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$else}
|
|
|
|
|
{Pause instruction (improves performance on P4)}
|
|
|
|
|
rep nop
|
|
|
|
|
{Try again}
|
|
|
|
|
jmp @LockBlockTypeLoop
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
@AllocateSmallBlockPool:
|
|
|
|
|
{save additional registers}
|
|
|
|
|
push esi
|
|
|
|
|
push edi
|
|
|
|
|
{Do we need to lock the medium blocks?}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
cmp IsMultiThread, False
|
|
|
|
|
je @MediumBlocksLockedForPool
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
call LockMediumBlocks
|
2006-04-15 03:44:05 -04:00
|
|
|
|
@MediumBlocksLockedForPool:
|
|
|
|
|
{Are there any available blocks of a suitable size?}
|
|
|
|
|
movsx esi, TSmallBlockType[ebx].AllowedGroupsForBlockPoolBitmap
|
|
|
|
|
and esi, MediumBlockBinGroupBitmap
|
|
|
|
|
jz @NoSuitableMediumBlocks
|
|
|
|
|
{Get the bin group number with free blocks in eax}
|
|
|
|
|
bsf eax, esi
|
|
|
|
|
{Get the bin number in ecx}
|
|
|
|
|
lea esi, [eax * 8]
|
|
|
|
|
mov ecx, dword ptr [MediumBlockBinBitmaps + eax * 4]
|
|
|
|
|
bsf ecx, ecx
|
|
|
|
|
lea ecx, [ecx + esi * 4]
|
|
|
|
|
{Get a pointer to the bin in edi}
|
|
|
|
|
lea edi, [MediumBlockBins + ecx * 8]
|
|
|
|
|
{Get the free block in esi}
|
|
|
|
|
mov esi, TMediumFreeBlock[edi].NextFreeBlock
|
|
|
|
|
{Remove the first block from the linked list (LIFO)}
|
|
|
|
|
mov edx, TMediumFreeBlock[esi].NextFreeBlock
|
|
|
|
|
mov TMediumFreeBlock[edi].NextFreeBlock, edx
|
|
|
|
|
mov TMediumFreeBlock[edx].PreviousFreeBlock, edi
|
|
|
|
|
{Is this bin now empty?}
|
|
|
|
|
cmp edi, edx
|
|
|
|
|
jne @MediumBinNotEmpty
|
|
|
|
|
{eax = bin group number, ecx = bin number, edi = @bin, esi = free block, ebx = block type}
|
|
|
|
|
{Flag this bin as empty}
|
|
|
|
|
mov edx, -2
|
|
|
|
|
rol edx, cl
|
|
|
|
|
and dword ptr [MediumBlockBinBitmaps + eax * 4], edx
|
|
|
|
|
jnz @MediumBinNotEmpty
|
|
|
|
|
{Flag the group as empty}
|
|
|
|
|
btr MediumBlockBinGroupBitmap, eax
|
|
|
|
|
@MediumBinNotEmpty:
|
|
|
|
|
{esi = free block, ebx = block type}
|
|
|
|
|
{Get the size of the available medium block in edi}
|
|
|
|
|
mov edi, DropMediumAndLargeFlagsMask
|
|
|
|
|
and edi, [esi - 4]
|
|
|
|
|
cmp edi, MaximumSmallBlockPoolSize
|
|
|
|
|
jb @UseWholeBlock
|
|
|
|
|
{Split the block: get the size of the second part, new block size is the
|
|
|
|
|
optimal size}
|
|
|
|
|
mov edx, edi
|
|
|
|
|
movzx edi, TSmallBlockType[ebx].OptimalBlockPoolSize
|
|
|
|
|
sub edx, edi
|
|
|
|
|
{Split the block in two}
|
|
|
|
|
lea eax, [esi + edi]
|
|
|
|
|
lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
|
|
|
|
|
mov [eax - 4], ecx
|
|
|
|
|
{Store the size of the second split as the second last dword}
|
|
|
|
|
mov [eax + edx - 8], edx
|
|
|
|
|
{Put the remainder in a bin (it will be big enough)}
|
|
|
|
|
call InsertMediumBlockIntoBin
|
|
|
|
|
jmp @GotMediumBlock
|
|
|
|
|
{Align branch target}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef AssumeMultiThreaded}
|
|
|
|
|
nop
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
@NoSuitableMediumBlocks:
|
|
|
|
|
{Check the sequential feed medium block pool for space}
|
|
|
|
|
movzx ecx, TSmallBlockType[ebx].MinimumBlockPoolSize
|
|
|
|
|
mov edi, MediumSequentialFeedBytesLeft
|
|
|
|
|
cmp edi, ecx
|
|
|
|
|
jb @AllocateNewSequentialFeed
|
|
|
|
|
{Get the address of the last block that was fed}
|
|
|
|
|
mov esi, LastSequentiallyFedMediumBlock
|
|
|
|
|
{Enough sequential feed space: Will the remainder be usable?}
|
|
|
|
|
movzx ecx, TSmallBlockType[ebx].OptimalBlockPoolSize
|
|
|
|
|
lea edx, [ecx + MinimumMediumBlockSize]
|
|
|
|
|
cmp edi, edx
|
|
|
|
|
jb @NotMuchSpace
|
|
|
|
|
mov edi, ecx
|
|
|
|
|
@NotMuchSpace:
|
|
|
|
|
sub esi, edi
|
|
|
|
|
{Update the sequential feed parameters}
|
|
|
|
|
sub MediumSequentialFeedBytesLeft, edi
|
|
|
|
|
mov LastSequentiallyFedMediumBlock, esi
|
|
|
|
|
{Get the block pointer}
|
|
|
|
|
jmp @GotMediumBlock
|
|
|
|
|
{Align branch target}
|
|
|
|
|
@AllocateNewSequentialFeed:
|
|
|
|
|
{Need to allocate a new sequential feed medium block pool: use the
|
|
|
|
|
optimal size for this small block pool}
|
|
|
|
|
movzx eax, TSmallBlockType[ebx].OptimalBlockPoolSize
|
|
|
|
|
mov edi, eax
|
|
|
|
|
{Allocate the medium block pool}
|
|
|
|
|
call AllocNewSequentialFeedMediumPool
|
|
|
|
|
mov esi, eax
|
|
|
|
|
test eax, eax
|
|
|
|
|
jnz @GotMediumBlock
|
|
|
|
|
mov MediumBlocksLocked, al
|
|
|
|
|
mov TSmallBlockType[ebx].BlockTypeLocked, al
|
|
|
|
|
pop edi
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
@UseWholeBlock:
|
|
|
|
|
{esi = free block, ebx = block type, edi = block size}
|
|
|
|
|
{Mark this block as used in the block following it}
|
|
|
|
|
and byte ptr [esi + edi - 4], not PreviousMediumBlockIsFreeFlag
|
|
|
|
|
@GotMediumBlock:
|
|
|
|
|
{esi = free block, ebx = block type, edi = block size}
|
|
|
|
|
{Set the size and flags for this block}
|
|
|
|
|
lea ecx, [edi + IsMediumBlockFlag + IsSmallBlockPoolInUseFlag]
|
|
|
|
|
mov [esi - 4], ecx
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
xor eax, eax
|
|
|
|
|
mov MediumBlocksLocked, al
|
|
|
|
|
{Set up the block pool}
|
|
|
|
|
mov TSmallBlockPoolHeader[esi].BlockType, ebx
|
|
|
|
|
mov TSmallBlockPoolHeader[esi].FirstFreeBlock, eax
|
|
|
|
|
mov TSmallBlockPoolHeader[esi].BlocksInUse, 1
|
|
|
|
|
{Set it up for sequential block serving}
|
|
|
|
|
mov TSmallBlockType[ebx].CurrentSequentialFeedPool, esi
|
|
|
|
|
{Return the pointer to the first block}
|
|
|
|
|
lea eax, [esi + SmallBlockPoolHeaderSize]
|
|
|
|
|
movzx ecx, TSmallBlockType[ebx].BlockSize
|
|
|
|
|
lea edx, [eax + ecx]
|
|
|
|
|
mov TSmallBlockType[ebx].NextSequentialFeedBlockAddress, edx
|
|
|
|
|
add edi, esi
|
|
|
|
|
sub edi, ecx
|
|
|
|
|
mov TSmallBlockType[ebx].MaxSequentialFeedBlockAddress, edi
|
|
|
|
|
{Unlock the small block type}
|
|
|
|
|
mov TSmallBlockType[ebx].BlockTypeLocked, False
|
|
|
|
|
{Set the small block header}
|
|
|
|
|
mov [eax - 4], esi
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop edi
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
{Done}
|
|
|
|
|
ret
|
|
|
|
|
{-------------------Medium block allocation-------------------}
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
@NotASmallBlock:
|
|
|
|
|
cmp eax, (MaximumMediumBlockSize - BlockHeaderSize)
|
|
|
|
|
ja @IsALargeBlockRequest
|
|
|
|
|
{Get the bin size for this block size. Block sizes are
|
|
|
|
|
rounded up to the next bin size.}
|
|
|
|
|
lea ebx, [eax + MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset]
|
|
|
|
|
and ebx, -MediumBlockGranularity
|
|
|
|
|
add ebx, MediumBlockSizeOffset
|
|
|
|
|
{Do we need to lock the medium blocks?}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
test cl, cl
|
2008-07-25 09:23:00 -04:00
|
|
|
|
jz @MediumBlocksLocked
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
call LockMediumBlocks
|
2006-04-15 03:44:05 -04:00
|
|
|
|
@MediumBlocksLocked:
|
|
|
|
|
{Get the bin number in ecx and the group number in edx}
|
|
|
|
|
lea edx, [ebx - MinimumMediumBlockSize]
|
|
|
|
|
mov ecx, edx
|
|
|
|
|
shr edx, 8 + 5
|
|
|
|
|
shr ecx, 8
|
|
|
|
|
{Is there a suitable block inside this group?}
|
|
|
|
|
mov eax, -1
|
|
|
|
|
shl eax, cl
|
|
|
|
|
and eax, dword ptr [MediumBlockBinBitmaps + edx * 4]
|
|
|
|
|
jz @GroupIsEmpty
|
|
|
|
|
{Get the actual bin number}
|
|
|
|
|
and ecx, -32
|
|
|
|
|
bsf eax, eax
|
|
|
|
|
or ecx, eax
|
|
|
|
|
jmp @GotBinAndGroup
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
@GroupIsEmpty:
|
|
|
|
|
{Try all groups greater than this group}
|
|
|
|
|
mov eax, -2
|
|
|
|
|
mov ecx, edx
|
|
|
|
|
shl eax, cl
|
|
|
|
|
and eax, MediumBlockBinGroupBitmap
|
|
|
|
|
jz @TrySequentialFeedMedium
|
|
|
|
|
{There is a suitable group with space: get the bin number}
|
|
|
|
|
bsf edx, eax
|
|
|
|
|
{Get the bin in the group with free blocks}
|
|
|
|
|
mov eax, dword ptr [MediumBlockBinBitmaps + edx * 4]
|
|
|
|
|
bsf ecx, eax
|
|
|
|
|
mov eax, edx
|
|
|
|
|
shl eax, 5
|
|
|
|
|
or ecx, eax
|
|
|
|
|
jmp @GotBinAndGroup
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
@TrySequentialFeedMedium:
|
|
|
|
|
mov ecx, MediumSequentialFeedBytesLeft
|
|
|
|
|
{Block can be fed sequentially?}
|
|
|
|
|
sub ecx, ebx
|
|
|
|
|
jc @AllocateNewSequentialFeedForMedium
|
|
|
|
|
{Get the block address}
|
|
|
|
|
mov eax, LastSequentiallyFedMediumBlock
|
|
|
|
|
sub eax, ebx
|
|
|
|
|
mov LastSequentiallyFedMediumBlock, eax
|
|
|
|
|
{Store the remaining bytes}
|
|
|
|
|
mov MediumSequentialFeedBytesLeft, ecx
|
|
|
|
|
{Set the flags for the block}
|
|
|
|
|
or ebx, IsMediumBlockFlag
|
|
|
|
|
mov [eax - 4], ebx
|
|
|
|
|
jmp @MediumBlockGetDone
|
|
|
|
|
{Align branch target}
|
|
|
|
|
@AllocateNewSequentialFeedForMedium:
|
|
|
|
|
mov eax, ebx
|
|
|
|
|
call AllocNewSequentialFeedMediumPool
|
|
|
|
|
@MediumBlockGetDone:
|
|
|
|
|
mov MediumBlocksLocked, False
|
|
|
|
|
pop ebx
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
@GotBinAndGroup:
|
|
|
|
|
{ebx = block size, ecx = bin number, edx = group number}
|
|
|
|
|
push esi
|
|
|
|
|
push edi
|
|
|
|
|
{Get a pointer to the bin in edi}
|
|
|
|
|
lea edi, [MediumBlockBins + ecx * 8]
|
|
|
|
|
{Get the free block in esi}
|
|
|
|
|
mov esi, TMediumFreeBlock[edi].NextFreeBlock
|
|
|
|
|
{Remove the first block from the linked list (LIFO)}
|
|
|
|
|
mov eax, TMediumFreeBlock[esi].NextFreeBlock
|
|
|
|
|
mov TMediumFreeBlock[edi].NextFreeBlock, eax
|
|
|
|
|
mov TMediumFreeBlock[eax].PreviousFreeBlock, edi
|
|
|
|
|
{Is this bin now empty?}
|
|
|
|
|
cmp edi, eax
|
|
|
|
|
jne @MediumBinNotEmptyForMedium
|
|
|
|
|
{eax = bin group number, ecx = bin number, edi = @bin, esi = free block, ebx = block size}
|
|
|
|
|
{Flag this bin as empty}
|
|
|
|
|
mov eax, -2
|
|
|
|
|
rol eax, cl
|
|
|
|
|
and dword ptr [MediumBlockBinBitmaps + edx * 4], eax
|
|
|
|
|
jnz @MediumBinNotEmptyForMedium
|
|
|
|
|
{Flag the group as empty}
|
|
|
|
|
btr MediumBlockBinGroupBitmap, edx
|
|
|
|
|
@MediumBinNotEmptyForMedium:
|
|
|
|
|
{esi = free block, ebx = block size}
|
|
|
|
|
{Get the size of the available medium block in edi}
|
|
|
|
|
mov edi, DropMediumAndLargeFlagsMask
|
|
|
|
|
and edi, [esi - 4]
|
|
|
|
|
{Get the size of the second split in edx}
|
|
|
|
|
mov edx, edi
|
|
|
|
|
sub edx, ebx
|
|
|
|
|
jz @UseWholeBlockForMedium
|
|
|
|
|
{Split the block in two}
|
|
|
|
|
lea eax, [esi + ebx]
|
|
|
|
|
lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
|
|
|
|
|
mov [eax - 4], ecx
|
|
|
|
|
{Store the size of the second split as the second last dword}
|
|
|
|
|
mov [eax + edx - 8], edx
|
|
|
|
|
{Put the remainder in a bin}
|
|
|
|
|
cmp edx, MinimumMediumBlockSize
|
|
|
|
|
jb @GotMediumBlockForMedium
|
|
|
|
|
call InsertMediumBlockIntoBin
|
|
|
|
|
jmp @GotMediumBlockForMedium
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@UseWholeBlockForMedium:
|
|
|
|
|
{Mark this block as used in the block following it}
|
|
|
|
|
and byte ptr [esi + edi - 4], not PreviousMediumBlockIsFreeFlag
|
|
|
|
|
@GotMediumBlockForMedium:
|
|
|
|
|
{Set the size and flags for this block}
|
|
|
|
|
lea ecx, [ebx + IsMediumBlockFlag]
|
|
|
|
|
mov [esi - 4], ecx
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
mov MediumBlocksLocked, False
|
|
|
|
|
mov eax, esi
|
|
|
|
|
pop edi
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
ret
|
|
|
|
|
{-------------------Large block allocation-------------------}
|
|
|
|
|
{Align branch target}
|
|
|
|
|
@IsALargeBlockRequest:
|
|
|
|
|
pop ebx
|
|
|
|
|
test eax, eax
|
|
|
|
|
jns AllocateLargeBlock
|
|
|
|
|
xor eax, eax
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifndef ASMVersion}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Frees a medium block, returning 0 on success, -1 otherwise}
|
|
|
|
|
function FreeMediumBlock(APointer: Pointer): Integer;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LNextMediumBlock{$ifndef FullDebugMode}, LPreviousMediumBlock{$endif}: PMediumFreeBlock;
|
|
|
|
|
LNextMediumBlockSizeAndFlags: Cardinal;
|
|
|
|
|
LBlockSize{$ifndef FullDebugMode}, LPreviousMediumBlockSize{$endif}: Cardinal;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
LPPreviousMediumBlockPoolHeader, LPNextMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
{$endif}
|
|
|
|
|
LBlockHeader: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Get the block header}
|
|
|
|
|
LBlockHeader := PCardinal(Cardinal(APointer) - BlockHeaderSize)^;
|
|
|
|
|
{Get the medium block size}
|
|
|
|
|
LBlockSize := LBlockHeader and DropMediumAndLargeFlagsMask;
|
|
|
|
|
{Lock the medium blocks}
|
|
|
|
|
LockMediumBlocks;
|
|
|
|
|
{Can we combine this block with the next free block?}
|
|
|
|
|
LNextMediumBlock := PMediumFreeBlock(Cardinal(APointer) + LBlockSize);
|
|
|
|
|
LNextMediumBlockSizeAndFlags := PCardinal(Cardinal(LNextMediumBlock) - BlockHeaderSize)^;
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{Check that this block was flagged as in use in the next block}
|
|
|
|
|
if (LNextMediumBlockSizeAndFlags and PreviousMediumBlockIsFreeFlag) <> 0 then
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
if (LNextMediumBlockSizeAndFlags and IsFreeBlockFlag) <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Increase the size of this block}
|
|
|
|
|
Inc(LBlockSize, LNextMediumBlockSizeAndFlags and DropMediumAndLargeFlagsMask);
|
|
|
|
|
{Remove the next block as well}
|
|
|
|
|
if LNextMediumBlockSizeAndFlags >= MinimumMediumBlockSize then
|
|
|
|
|
RemoveMediumFreeBlock(LNextMediumBlock);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
{Reset the "previous in use" flag of the next block}
|
|
|
|
|
PCardinal(Cardinal(LNextMediumBlock) - BlockHeaderSize)^ := LNextMediumBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
end;
|
|
|
|
|
{Can we combine this block with the previous free block? We need to
|
|
|
|
|
re-read the flags since it could have changed before we could lock the
|
|
|
|
|
medium blocks.}
|
|
|
|
|
if (PCardinal(Cardinal(APointer) - BlockHeaderSize)^ and PreviousMediumBlockIsFreeFlag) <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get the size of the free block just before this one}
|
|
|
|
|
LPreviousMediumBlockSize := PCardinal(Cardinal(APointer) - 8)^;
|
|
|
|
|
{Get the start of the previous block}
|
|
|
|
|
LPreviousMediumBlock := PMediumFreeBlock(Cardinal(APointer) - LPreviousMediumBlockSize);
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{Check that the previous block is actually free}
|
|
|
|
|
if (PCardinal(Cardinal(LPreviousMediumBlock) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag) then
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{Set the new block size}
|
|
|
|
|
Inc(LBlockSize, LPreviousMediumBlockSize);
|
|
|
|
|
{This is the new current block}
|
|
|
|
|
APointer := LPreviousMediumBlock;
|
|
|
|
|
{Remove the previous block from the linked list}
|
|
|
|
|
if LPreviousMediumBlockSize >= MinimumMediumBlockSize then
|
|
|
|
|
RemoveMediumFreeBlock(LPreviousMediumBlock);
|
|
|
|
|
end;
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{Check that the previous block is currently flagged as in use}
|
|
|
|
|
if (PCardinal(Cardinal(APointer) - BlockHeaderSize)^ and PreviousMediumBlockIsFreeFlag) <> 0 then
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{Is the entire medium block pool free, and there are other free blocks
|
|
|
|
|
that can fit the largest possible medium block? -> free it. (Except in
|
|
|
|
|
full debug mode where medium pools are never freed.)}
|
|
|
|
|
if (LBlockSize <> (MediumBlockPoolSize - MediumBlockPoolHeaderSize)) then
|
|
|
|
|
begin
|
|
|
|
|
{Store the size of the block as well as the flags}
|
|
|
|
|
PCardinal(Cardinal(APointer) - BlockHeaderSize)^ := LBlockSize or (IsMediumBlockFlag or IsFreeBlockFlag);
|
|
|
|
|
{$else}
|
|
|
|
|
{Mark the block as free}
|
|
|
|
|
Inc(PCardinal(Cardinal(APointer) - BlockHeaderSize)^, IsFreeBlockFlag);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Store the trailing size marker}
|
|
|
|
|
PCardinal(Cardinal(APointer) + LBlockSize - 8)^ := LBlockSize;
|
|
|
|
|
{Insert this block back into the bins: Size check not required here,
|
|
|
|
|
since medium blocks that are in use are not allowed to be
|
|
|
|
|
shrunk smaller than MinimumMediumBlockSize}
|
|
|
|
|
InsertMediumBlockIntoBin(APointer, LBlockSize);
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{Check that this block is actually free and the next and previous blocks are both in use.}
|
|
|
|
|
if ((PCardinal(Cardinal(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag))
|
|
|
|
|
or ((PCardinal(Cardinal(APointer) + (PCardinal(Cardinal(APointer) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and IsFreeBlockFlag) <> 0) then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{All OK}
|
|
|
|
|
Result := 0;
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Should this become the new sequential feed?}
|
|
|
|
|
if MediumSequentialFeedBytesLeft <> MediumBlockPoolSize - MediumBlockPoolHeaderSize then
|
|
|
|
|
begin
|
|
|
|
|
{Bin the current sequential feed}
|
|
|
|
|
BinMediumSequentialFeedRemainder;
|
|
|
|
|
{Set this medium pool up as the new sequential feed pool:
|
|
|
|
|
Store the sequential feed pool trailer}
|
|
|
|
|
PCardinal(Cardinal(APointer) + LBlockSize - BlockHeaderSize)^ := IsMediumBlockFlag;
|
|
|
|
|
{Store the number of bytes available in the sequential feed chunk}
|
|
|
|
|
MediumSequentialFeedBytesLeft := MediumBlockPoolSize - MediumBlockPoolHeaderSize;
|
|
|
|
|
{Set the last sequentially fed block}
|
|
|
|
|
LastSequentiallyFedMediumBlock := Pointer(Cardinal(APointer) + LBlockSize);
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{Success}
|
|
|
|
|
Result := 0;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Remove this medium block pool from the linked list}
|
|
|
|
|
Dec(Cardinal(APointer), MediumBlockPoolHeaderSize);
|
|
|
|
|
LPPreviousMediumBlockPoolHeader := PMediumBlockPoolHeader(APointer).PreviousMediumBlockPoolHeader;
|
|
|
|
|
LPNextMediumBlockPoolHeader := PMediumBlockPoolHeader(APointer).NextMediumBlockPoolHeader;
|
|
|
|
|
LPPreviousMediumBlockPoolHeader.NextMediumBlockPoolHeader := LPNextMediumBlockPoolHeader;
|
|
|
|
|
LPNextMediumBlockPoolHeader.PreviousMediumBlockPoolHeader := LPPreviousMediumBlockPoolHeader;
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{Free the medium block pool}
|
|
|
|
|
if VirtualFree(APointer, 0, MEM_RELEASE) then
|
|
|
|
|
Result := 0
|
|
|
|
|
else
|
|
|
|
|
Result := -1;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Replacement for SysFreeMem (pascal version)}
|
|
|
|
|
function FastFreeMem(APointer: Pointer): Integer;
|
|
|
|
|
var
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LPSmallBlockPool{$ifndef FullDebugMode}, LPPreviousPool, LPNextPool{$endif},
|
|
|
|
|
LPOldFirstPool: PSmallBlockPoolHeader;
|
|
|
|
|
LPSmallBlockType: PSmallBlockType;
|
|
|
|
|
LOldFirstFreeBlock: Pointer;
|
|
|
|
|
LBlockHeader: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Get the small block header: Is it actually a small block?}
|
|
|
|
|
LBlockHeader := PCardinal(Cardinal(APointer) - BlockHeaderSize)^;
|
|
|
|
|
{Is it a small block that is in use?}
|
|
|
|
|
if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get a pointer to the block pool}
|
|
|
|
|
LPSmallBlockPool := PSmallBlockPoolHeader(LBlockHeader);
|
|
|
|
|
{Get the block type}
|
|
|
|
|
LPSmallBlockType := LPSmallBlockPool.BlockType;
|
|
|
|
|
{Lock the block type}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
if IsMultiThread then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
while (LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) <> 0) do
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(InitialSleepTime);
|
|
|
|
|
if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(AdditionalSleepTime);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{Get the old first free block}
|
|
|
|
|
LOldFirstFreeBlock := LPSmallBlockPool.FirstFreeBlock;
|
|
|
|
|
{Was the pool manager previously full?}
|
|
|
|
|
if LOldFirstFreeBlock = nil then
|
|
|
|
|
begin
|
|
|
|
|
{Insert this as the first partially free pool for the block size}
|
|
|
|
|
LPOldFirstPool := LPSmallBlockType.NextPartiallyFreePool;
|
|
|
|
|
LPSmallBlockPool.NextPartiallyFreePool := LPOldFirstPool;
|
|
|
|
|
LPOldFirstPool.PreviousPartiallyFreePool := LPSmallBlockPool;
|
|
|
|
|
LPSmallBlockPool.PreviousPartiallyFreePool := PSmallBlockPoolHeader(LPSmallBlockType);
|
|
|
|
|
LPSmallBlockType.NextPartiallyFreePool := LPSmallBlockPool;
|
|
|
|
|
end;
|
|
|
|
|
{Store the old first free block}
|
|
|
|
|
PCardinal(Cardinal(APointer) - BlockHeaderSize)^ := Cardinal(LOldFirstFreeBlock) or IsFreeBlockFlag;
|
|
|
|
|
{Store this as the new first free block}
|
|
|
|
|
LPSmallBlockPool.FirstFreeBlock := APointer;
|
|
|
|
|
{Decrement the number of allocated blocks}
|
|
|
|
|
Dec(LPSmallBlockPool.BlocksInUse);
|
|
|
|
|
{Small block pools are never freed in full debug mode. This increases the
|
|
|
|
|
likehood of success in catching objects still being used after being
|
|
|
|
|
destroyed.}
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{Is the entire pool now free? -> Free it.}
|
|
|
|
|
if LPSmallBlockPool.BlocksInUse = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get the previous and next chunk managers}
|
|
|
|
|
LPPreviousPool := LPSmallBlockPool.PreviousPartiallyFreePool;
|
|
|
|
|
LPNextPool := LPSmallBlockPool.NextPartiallyFreePool;
|
|
|
|
|
{Remove this manager}
|
|
|
|
|
LPPreviousPool.NextPartiallyFreePool := LPNextPool;
|
|
|
|
|
LPNextPool.PreviousPartiallyFreePool := LPPreviousPool;
|
|
|
|
|
{Is this the sequential feed pool? If so, stop sequential feeding}
|
|
|
|
|
if (LPSmallBlockType.CurrentSequentialFeedPool = LPSmallBlockPool) then
|
|
|
|
|
LPSmallBlockType.MaxSequentialFeedBlockAddress := nil;
|
|
|
|
|
{Unlock this block type}
|
|
|
|
|
LPSmallBlockType.BlockTypeLocked := False;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Free the block pool}
|
|
|
|
|
FreeMediumBlock(LPSmallBlockPool);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
{Unlock this block type}
|
|
|
|
|
LPSmallBlockType.BlockTypeLocked := False;
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{No error}
|
|
|
|
|
Result := 0;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Is this a medium block or a large block?}
|
|
|
|
|
if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Result := FreeMediumBlock(APointer);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Validate: Is this actually a Large block, or is it an attempt to free an
|
|
|
|
|
already freed small block?}
|
|
|
|
|
if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
|
|
|
|
|
Result := FreeLargeBlock(APointer)
|
|
|
|
|
else
|
|
|
|
|
Result := -1;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$else}
|
|
|
|
|
{Replacement for SysFreeMem (pascal version)}
|
|
|
|
|
function FastFreeMem(APointer: Pointer): Integer;
|
|
|
|
|
asm
|
|
|
|
|
{Get the block header in edx}
|
|
|
|
|
mov edx, [eax - 4]
|
|
|
|
|
{Is it a small block in use?}
|
|
|
|
|
test dl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
|
|
|
|
|
{Save the pointer in ecx}
|
|
|
|
|
mov ecx, eax
|
|
|
|
|
{Save ebx}
|
|
|
|
|
push ebx
|
|
|
|
|
{Get the IsMultiThread variable in bl}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
mov bl, IsMultiThread
|
|
|
|
|
{$endif}
|
|
|
|
|
{Is it a small block that is in use?}
|
|
|
|
|
jnz @NotSmallBlockInUse
|
|
|
|
|
{Do we need to lock the block type?}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
test bl, bl
|
|
|
|
|
{$endif}
|
|
|
|
|
{Get the small block type in ebx}
|
|
|
|
|
mov ebx, TSmallBlockPoolHeader[edx].BlockType
|
|
|
|
|
{Do we need to lock the block type?}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
jnz @LockBlockTypeLoop
|
|
|
|
|
{$else}
|
|
|
|
|
jmp @LockBlockTypeLoop
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
{$endif}
|
|
|
|
|
@GotLockOnSmallBlockType:
|
|
|
|
|
{Current state: edx = @SmallBlockPoolHeader, ecx = APointer, ebx = @SmallBlockType}
|
|
|
|
|
{Decrement the number of blocks in use}
|
|
|
|
|
sub TSmallBlockPoolHeader[edx].BlocksInUse, 1
|
|
|
|
|
{Get the old first free block}
|
|
|
|
|
mov eax, TSmallBlockPoolHeader[edx].FirstFreeBlock
|
|
|
|
|
{Is the pool now empty?}
|
|
|
|
|
jz @PoolIsNowEmpty
|
|
|
|
|
{Was the pool full?}
|
|
|
|
|
test eax, eax
|
|
|
|
|
{Store this as the new first free block}
|
|
|
|
|
mov TSmallBlockPoolHeader[edx].FirstFreeBlock, ecx
|
|
|
|
|
{Store the previous first free block as the block header}
|
|
|
|
|
lea eax, [eax + IsFreeBlockFlag]
|
|
|
|
|
mov [ecx - 4], eax
|
|
|
|
|
{Insert the pool back into the linked list if it was full}
|
|
|
|
|
jz @SmallPoolWasFull
|
|
|
|
|
{All ok}
|
|
|
|
|
xor eax, eax
|
|
|
|
|
{Unlock the block type}
|
|
|
|
|
mov TSmallBlockType[ebx].BlockTypeLocked, al
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop ebx
|
|
|
|
|
{Done}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
nop
|
|
|
|
|
{$endif}
|
|
|
|
|
@SmallPoolWasFull:
|
|
|
|
|
{Insert this as the first partially free pool for the block size}
|
|
|
|
|
mov ecx, TSmallBlockType[ebx].NextPartiallyFreePool
|
|
|
|
|
mov TSmallBlockPoolHeader[edx].PreviousPartiallyFreePool, ebx
|
|
|
|
|
mov TSmallBlockPoolHeader[edx].NextPartiallyFreePool, ecx
|
|
|
|
|
mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, edx
|
|
|
|
|
mov TSmallBlockType[ebx].NextPartiallyFreePool, edx
|
|
|
|
|
{Unlock the block type}
|
|
|
|
|
mov TSmallBlockType[ebx].BlockTypeLocked, False
|
|
|
|
|
{All ok}
|
|
|
|
|
xor eax, eax
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop ebx
|
|
|
|
|
{Done}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@PoolIsNowEmpty:
|
|
|
|
|
{Was this pool actually in the linked list of pools with space? If not, it
|
|
|
|
|
can only be the sequential feed pool (it is the only pool that may contain
|
|
|
|
|
only one block, i.e. other blocks have not been split off yet)}
|
|
|
|
|
test eax, eax
|
|
|
|
|
jz @IsSequentialFeedPool
|
|
|
|
|
{Pool is now empty: Remove it from the linked list and free it}
|
|
|
|
|
mov eax, TSmallBlockPoolHeader[edx].PreviousPartiallyFreePool
|
|
|
|
|
mov ecx, TSmallBlockPoolHeader[edx].NextPartiallyFreePool
|
|
|
|
|
{Remove this manager}
|
|
|
|
|
mov TSmallBlockPoolHeader[eax].NextPartiallyFreePool, ecx
|
|
|
|
|
mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, eax
|
|
|
|
|
{Zero out eax}
|
|
|
|
|
xor eax, eax
|
|
|
|
|
{Is this the sequential feed pool? If so, stop sequential feeding}
|
|
|
|
|
cmp TSmallBlockType[ebx].CurrentSequentialFeedPool, edx
|
|
|
|
|
jne @NotSequentialFeedPool
|
|
|
|
|
@IsSequentialFeedPool:
|
|
|
|
|
mov TSmallBlockType[ebx].MaxSequentialFeedBlockAddress, eax
|
|
|
|
|
@NotSequentialFeedPool:
|
|
|
|
|
{Unlock the block type}
|
|
|
|
|
mov TSmallBlockType[ebx].BlockTypeLocked, al
|
|
|
|
|
{Release this pool}
|
|
|
|
|
mov eax, edx
|
|
|
|
|
mov edx, [edx - 4]
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
mov bl, IsMultiThread
|
|
|
|
|
{$endif}
|
|
|
|
|
jmp @FreeMediumBlock
|
|
|
|
|
{Align branch target}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
{$endif}
|
|
|
|
|
nop
|
|
|
|
|
@LockBlockTypeLoop:
|
|
|
|
|
mov eax, $100
|
|
|
|
|
{Attempt to grab the block type}
|
|
|
|
|
lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
|
|
|
|
|
je @GotLockOnSmallBlockType
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Couldn't grab the block type - sleep and try again}
|
|
|
|
|
push ecx
|
|
|
|
|
push edx
|
|
|
|
|
push InitialSleepTime
|
|
|
|
|
call Sleep
|
|
|
|
|
pop edx
|
|
|
|
|
pop ecx
|
|
|
|
|
{Try again}
|
|
|
|
|
mov eax, $100
|
|
|
|
|
{Attempt to grab the block type}
|
|
|
|
|
lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
|
|
|
|
|
je @GotLockOnSmallBlockType
|
|
|
|
|
{Couldn't grab the block type - sleep and try again}
|
|
|
|
|
push ecx
|
|
|
|
|
push edx
|
|
|
|
|
push AdditionalSleepTime
|
|
|
|
|
call Sleep
|
|
|
|
|
pop edx
|
|
|
|
|
pop ecx
|
|
|
|
|
{Try again}
|
|
|
|
|
jmp @LockBlockTypeLoop
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$else}
|
|
|
|
|
{Pause instruction (improves performance on P4)}
|
|
|
|
|
rep nop
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Try again}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
jmp @LockBlockTypeLoop
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{---------------------Medium blocks------------------------------}
|
|
|
|
|
{Align branch target}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
@NotSmallBlockInUse:
|
|
|
|
|
{Not a small block in use: is it a medium or large block?}
|
|
|
|
|
test dl, IsFreeBlockFlag + IsLargeBlockFlag
|
|
|
|
|
jnz @NotASmallOrMediumBlock
|
|
|
|
|
@FreeMediumBlock:
|
|
|
|
|
{Drop the flags}
|
|
|
|
|
and edx, DropMediumAndLargeFlagsMask
|
|
|
|
|
{Free the large block pointed to by eax, header in edx, bl = IsMultiThread}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
{Do we need to lock the medium blocks?}
|
|
|
|
|
test bl, bl
|
|
|
|
|
{$endif}
|
|
|
|
|
{Block size in ebx}
|
|
|
|
|
mov ebx, edx
|
|
|
|
|
{Save registers}
|
|
|
|
|
push esi
|
|
|
|
|
{Pointer in esi}
|
|
|
|
|
mov esi, eax
|
|
|
|
|
{Do we need to lock the medium blocks?}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
jz @MediumBlocksLocked
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
call LockMediumBlocks
|
2006-04-15 03:44:05 -04:00
|
|
|
|
@MediumBlocksLocked:
|
|
|
|
|
{Can we combine this block with the next free block?}
|
|
|
|
|
test dword ptr [esi + ebx - 4], IsFreeBlockFlag
|
|
|
|
|
{Get the next block size and flags in ecx}
|
|
|
|
|
mov ecx, [esi + ebx - 4]
|
|
|
|
|
jnz @NextBlockIsFree
|
|
|
|
|
{Set the "PreviousIsFree" flag in the next block}
|
|
|
|
|
or ecx, PreviousMediumBlockIsFreeFlag
|
|
|
|
|
mov [esi + ebx - 4], ecx
|
|
|
|
|
@NextBlockChecked:
|
|
|
|
|
{Can we combine this block with the previous free block? We need to
|
|
|
|
|
re-read the flags since it could have changed before we could lock the
|
|
|
|
|
medium blocks.}
|
|
|
|
|
test byte ptr [esi - 4], PreviousMediumBlockIsFreeFlag
|
|
|
|
|
jnz @PreviousBlockIsFree
|
|
|
|
|
@PreviousBlockChecked:
|
|
|
|
|
{Is the entire medium block pool free, and there are other free blocks
|
|
|
|
|
that can fit the largest possible medium block -> free it.}
|
|
|
|
|
cmp ebx, (MediumBlockPoolSize - MediumBlockPoolHeaderSize)
|
|
|
|
|
je @EntireMediumPoolFree
|
|
|
|
|
@BinFreeMediumBlock:
|
|
|
|
|
{Store the size of the block as well as the flags}
|
|
|
|
|
lea eax, [ebx + IsMediumBlockFlag + IsFreeBlockFlag]
|
|
|
|
|
mov [esi - 4], eax
|
|
|
|
|
{Store the trailing size marker}
|
|
|
|
|
mov [esi + ebx - 8], ebx
|
|
|
|
|
{Insert this block back into the bins: Size check not required here,
|
|
|
|
|
since medium blocks that are in use are not allowed to be
|
|
|
|
|
shrunk smaller than MinimumMediumBlockSize}
|
|
|
|
|
mov eax, esi
|
|
|
|
|
mov edx, ebx
|
|
|
|
|
{Insert into bin}
|
|
|
|
|
call InsertMediumBlockIntoBin
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
mov MediumBlocksLocked, False;
|
|
|
|
|
{All OK}
|
|
|
|
|
xor eax, eax
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
{Return}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
@NextBlockIsFree:
|
|
|
|
|
{Get the next block address in eax}
|
|
|
|
|
lea eax, [esi + ebx]
|
|
|
|
|
{Increase the size of this block}
|
|
|
|
|
and ecx, DropMediumAndLargeFlagsMask
|
|
|
|
|
add ebx, ecx
|
|
|
|
|
{Was the block binned?}
|
|
|
|
|
cmp ecx, MinimumMediumBlockSize
|
|
|
|
|
jb @NextBlockChecked
|
|
|
|
|
call RemoveMediumFreeBlock
|
|
|
|
|
jmp @NextBlockChecked
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
@PreviousBlockIsFree:
|
|
|
|
|
{Get the size of the free block just before this one}
|
|
|
|
|
mov ecx, [esi - 8]
|
|
|
|
|
{Include the previous block}
|
|
|
|
|
sub esi, ecx
|
|
|
|
|
{Set the new block size}
|
|
|
|
|
add ebx, ecx
|
|
|
|
|
{Remove the previous block from the linked list}
|
|
|
|
|
cmp ecx, MinimumMediumBlockSize
|
|
|
|
|
jb @PreviousBlockChecked
|
|
|
|
|
mov eax, esi
|
|
|
|
|
call RemoveMediumFreeBlock
|
|
|
|
|
jmp @PreviousBlockChecked
|
|
|
|
|
{Align branch target}
|
|
|
|
|
@EntireMediumPoolFree:
|
|
|
|
|
{Should we make this the new sequential feed medium block pool? If the
|
|
|
|
|
current sequential feed pool is not entirely free, we make this the new
|
|
|
|
|
sequential feed pool.}
|
|
|
|
|
cmp MediumSequentialFeedBytesLeft, MediumBlockPoolSize - MediumBlockPoolHeaderSize
|
|
|
|
|
jne @MakeEmptyMediumPoolSequentialFeed
|
|
|
|
|
{Point esi to the medium block pool header}
|
|
|
|
|
sub esi, MediumBlockPoolHeaderSize
|
|
|
|
|
{Remove this medium block pool from the linked list}
|
|
|
|
|
mov eax, TMediumBlockPoolHeader[esi].PreviousMediumBlockPoolHeader
|
|
|
|
|
mov edx, TMediumBlockPoolHeader[esi].NextMediumBlockPoolHeader
|
|
|
|
|
mov TMediumBlockPoolHeader[eax].NextMediumBlockPoolHeader, edx
|
|
|
|
|
mov TMediumBlockPoolHeader[edx].PreviousMediumBlockPoolHeader, eax
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
mov MediumBlocksLocked, False;
|
|
|
|
|
{Free the medium block pool}
|
|
|
|
|
push MEM_RELEASE
|
|
|
|
|
push 0
|
|
|
|
|
push esi
|
|
|
|
|
call VirtualFree
|
|
|
|
|
{VirtualFree returns >0 if all is ok}
|
|
|
|
|
cmp eax, 1
|
|
|
|
|
{Return 0 on all ok}
|
|
|
|
|
sbb eax, eax
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@MakeEmptyMediumPoolSequentialFeed:
|
|
|
|
|
{Get a pointer to the end-marker block}
|
|
|
|
|
lea ebx, [esi + MediumBlockPoolSize - MediumBlockPoolHeaderSize]
|
|
|
|
|
{Bin the current sequential feed pool}
|
|
|
|
|
call BinMediumSequentialFeedRemainder
|
|
|
|
|
{Set this medium pool up as the new sequential feed pool:
|
|
|
|
|
Store the sequential feed pool trailer}
|
|
|
|
|
mov dword ptr [ebx - BlockHeaderSize], IsMediumBlockFlag
|
|
|
|
|
{Store the number of bytes available in the sequential feed chunk}
|
|
|
|
|
mov MediumSequentialFeedBytesLeft, MediumBlockPoolSize - MediumBlockPoolHeaderSize
|
|
|
|
|
{Set the last sequentially fed block}
|
|
|
|
|
mov LastSequentiallyFedMediumBlock, ebx
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
mov MediumBlocksLocked, False;
|
|
|
|
|
{Success}
|
|
|
|
|
xor eax, eax
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@NotASmallOrMediumBlock:
|
|
|
|
|
{Restore ebx}
|
|
|
|
|
pop ebx
|
|
|
|
|
{Is it in fact a large block?}
|
|
|
|
|
test dl, IsFreeBlockFlag + IsMediumBlockFlag
|
|
|
|
|
jz FreeLargeBlock
|
|
|
|
|
{Attempt to free an already free block}
|
|
|
|
|
mov eax, -1
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
{$ifndef ASMVersion}
|
|
|
|
|
{Replacement for SysReallocMem (pascal version)}
|
|
|
|
|
function FastReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
|
|
|
|
|
var
|
|
|
|
|
LBlockHeader, LBlockFlags, LOldAvailableSize, LNewAllocSize,
|
|
|
|
|
LNextBlockSizeAndFlags, LNextBlockSize, LNewAvailableSize, LMinimumUpsize,
|
|
|
|
|
LSecondSPlitSize, LNewBlockSize: Cardinal;
|
|
|
|
|
LPSmallBlockType: PSmallBlockType;
|
|
|
|
|
LPNextBlock, LPNextBlockHeader: Pointer;
|
|
|
|
|
|
|
|
|
|
{Upsizes a large block in-place. The following variables are assumed correct:
|
|
|
|
|
LBlockFlags, LOldAvailableSize, LPNextBlock, LNextBlockSizeAndFlags,
|
|
|
|
|
LNextBlockSize, LNewAvailableSize. Medium blocks must be locked on entry if
|
|
|
|
|
required.}
|
|
|
|
|
procedure MediumBlockInPlaceUpsize;
|
|
|
|
|
begin
|
|
|
|
|
{Remove the next block}
|
|
|
|
|
if LNextBlockSizeAndFlags >= MinimumMediumBlockSize then
|
|
|
|
|
RemoveMediumFreeBlock(LPNextBlock);
|
|
|
|
|
{Add 25% for medium block in-place upsizes}
|
|
|
|
|
LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
|
|
|
|
|
if Cardinal(ANewSize) < LMinimumUpsize then
|
|
|
|
|
LNewAllocSize := LMinimumUpsize
|
|
|
|
|
else
|
|
|
|
|
LNewAllocSize := ANewSize;
|
|
|
|
|
{Round up to the nearest block size granularity}
|
|
|
|
|
LNewBlockSize := ((LNewAllocSize + (BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)) and -MediumBlockGranularity) + MediumBlockSizeOffset;
|
|
|
|
|
{Calculate the size of the second split}
|
|
|
|
|
LSecondSplitSize := LNewAvailableSize + BlockHeaderSize - LNewBlockSize;
|
|
|
|
|
{Does it fit?}
|
|
|
|
|
if Integer(LSecondSplitSize) <= 0 then
|
|
|
|
|
begin
|
|
|
|
|
{The block size is the full available size plus header}
|
|
|
|
|
LNewBlockSize := LNewAvailableSize + BlockHeaderSize;
|
|
|
|
|
{Grab the whole block: Mark it as used in the block following it}
|
|
|
|
|
LPNextBlockHeader := Pointer(Cardinal(APointer) + LNewAvailableSize);
|
|
|
|
|
PCardinal(LPNextBlockHeader)^ :=
|
|
|
|
|
PCardinal(LPNextBlockHeader)^ and (not PreviousMediumBlockIsFreeFlag);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Split the block in two}
|
|
|
|
|
LPNextBlock := PMediumFreeBlock(Cardinal(APointer) + LNewBlockSize);
|
|
|
|
|
{Set the size of the second split}
|
|
|
|
|
PCardinal(Cardinal(LPNextBlock) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
|
|
|
|
|
{Store the size of the second split as the second last dword}
|
|
|
|
|
PCardinal(Cardinal(LPNextBlock) + LSecondSplitSize - 8)^ := LSecondSplitSize;
|
|
|
|
|
{Put the remainder in a bin if it is big enough}
|
|
|
|
|
if LSecondSplitSize >= MinimumMediumBlockSize then
|
|
|
|
|
InsertMediumBlockIntoBin(LPNextBlock, LSecondSplitSize);
|
|
|
|
|
end;
|
|
|
|
|
{Set the size and flags for this block}
|
|
|
|
|
PCardinal(Cardinal(APointer) - BlockHeaderSize)^ := LNewBlockSize or LBlockFlags;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{In-place downsize of a medium block. On entry ANewSize must be less than half
|
|
|
|
|
of LOldAvailableSize.}
|
|
|
|
|
procedure MediumBlockInPlaceDownsize;
|
|
|
|
|
begin
|
|
|
|
|
{Round up to the next medium block size}
|
|
|
|
|
LNewBlockSize := ((ANewSize + (BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)) and -MediumBlockGranularity) + MediumBlockSizeOffset;
|
|
|
|
|
{Get the size of the second split}
|
|
|
|
|
LSecondSplitSize := (LOldAvailableSize + BlockHeaderSize) - LNewBlockSize;
|
|
|
|
|
{Lock the medium blocks}
|
|
|
|
|
LockMediumBlocks;
|
|
|
|
|
{Set the new size}
|
|
|
|
|
PCardinal(Cardinal(APointer) - BlockHeaderSize)^ :=
|
|
|
|
|
(PCardinal(Cardinal(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask)
|
|
|
|
|
or LNewBlockSize;
|
|
|
|
|
{Is the next block in use?}
|
|
|
|
|
LPNextBlock := PCardinal(Cardinal(APointer) + LOldAvailableSize + BlockHeaderSize);
|
|
|
|
|
LNextBlockSizeAndFlags := PCardinal(Cardinal(LPNextBlock) - BlockHeaderSize)^;
|
|
|
|
|
if LNextBlockSizeAndFlags and IsFreeBlockFlag = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{The next block is in use: flag its previous block as free}
|
|
|
|
|
PCardinal(Cardinal(LPNextBlock) - BlockHeaderSize)^ :=
|
|
|
|
|
LNextBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{The next block is free: combine it}
|
|
|
|
|
LNextBlockSizeAndFlags := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
|
|
|
|
|
Inc(LSecondSplitSize, LNextBlockSizeAndFlags);
|
|
|
|
|
if LNextBlockSizeAndFlags >= MinimumMediumBlockSize then
|
|
|
|
|
RemoveMediumFreeBlock(LPNextBlock);
|
|
|
|
|
end;
|
|
|
|
|
{Set the split}
|
|
|
|
|
LPNextBlock := PCardinal(Cardinal(APointer) + LNewBlockSize);
|
|
|
|
|
{Store the free part's header}
|
|
|
|
|
PCardinal(Cardinal(LPNextBlock) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
|
|
|
|
|
{Store the trailing size field}
|
|
|
|
|
PCardinal(Cardinal(LPNextBlock) + LSecondSplitSize - 8)^ := LSecondSplitSize;
|
|
|
|
|
{Bin this free block}
|
|
|
|
|
if LSecondSplitSize >= MinimumMediumBlockSize then
|
|
|
|
|
InsertMediumBlockIntoBin(LPNextBlock, LSecondSplitSize);
|
|
|
|
|
{Unlock the medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
{Get the block header: Is it actually a small block?}
|
|
|
|
|
LBlockHeader := PCardinal(Cardinal(APointer) - BlockHeaderSize)^;
|
|
|
|
|
{Is it a small block that is in use?}
|
|
|
|
|
if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{-----------------------------------Small block-------------------------------------}
|
|
|
|
|
{The block header is a pointer to the block pool: Get the block type}
|
|
|
|
|
LPSmallBlockType := PSmallBlockPoolHeader(LBlockHeader).BlockType;
|
|
|
|
|
{Get the available size inside blocks of this type.}
|
|
|
|
|
LOldAvailableSize := LPSmallBlockType.BlockSize - BlockHeaderSize;
|
|
|
|
|
{Is it an upsize or a downsize?}
|
|
|
|
|
if LOldAvailableSize >= Cardinal(ANewSize) then
|
|
|
|
|
begin
|
|
|
|
|
{It's a downsize. Do we need to allocate a smaller block? Only if the new
|
|
|
|
|
block size is less than a quarter of the available size less
|
|
|
|
|
SmallBlockDownsizeCheckAdder bytes}
|
|
|
|
|
if (Cardinal(ANewSize) * 4 + SmallBlockDownsizeCheckAdder) >= LOldAvailableSize then
|
|
|
|
|
begin
|
|
|
|
|
{In-place downsize - return the pointer}
|
|
|
|
|
Result := APointer;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Allocate a smaller block}
|
|
|
|
|
Result := FastGetMem(ANewSize);
|
|
|
|
|
{Allocated OK?}
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
|
|
|
|
{Move the data across}
|
|
|
|
|
{$ifdef UseCustomVariableSizeMoveRoutines}
|
|
|
|
|
{$ifdef Align16Bytes}
|
|
|
|
|
MoveX16L4(APointer^, Result^, ANewSize);
|
|
|
|
|
{$else}
|
|
|
|
|
MoveX8L4(APointer^, Result^, ANewSize);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$else}
|
|
|
|
|
System.Move(APointer^, Result^, ANewSize);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Free the old pointer}
|
|
|
|
|
FastFreeMem(APointer);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{This pointer is being reallocated to a larger block and therefore it is
|
|
|
|
|
logical to assume that it may be enlarged again. Since reallocations are
|
|
|
|
|
expensive, there is a minimum upsize percentage to avoid unnecessary
|
|
|
|
|
future move operations.}
|
|
|
|
|
{Must grow with at least 100% + x bytes}
|
|
|
|
|
LNewAllocSize := LOldAvailableSize * 2 + SmallBlockUpsizeAdder;
|
|
|
|
|
{Still not large enough?}
|
|
|
|
|
if LNewAllocSize < Cardinal(ANewSize) then
|
|
|
|
|
LNewAllocSize := ANewSize;
|
|
|
|
|
{Allocate the new block}
|
|
|
|
|
Result := FastGetMem(LNewAllocSize);
|
|
|
|
|
{Allocated OK?}
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
|
|
|
|
{Do we need to store the requested size? Only large blocks store the
|
|
|
|
|
requested size.}
|
|
|
|
|
if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
|
|
|
|
|
PLargeBlockHeader(Cardinal(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
|
|
|
|
|
{Move the data across}
|
|
|
|
|
{$ifdef UseCustomFixedSizeMoveRoutines}
|
|
|
|
|
LPSmallBlockType.UpsizeMoveProcedure(APointer^, Result^, LOldAvailableSize);
|
|
|
|
|
{$else}
|
|
|
|
|
System.Move(APointer^, Result^, LOldAvailableSize);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Free the old pointer}
|
|
|
|
|
FastFreeMem(APointer);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Is this a medium block or a large block?}
|
|
|
|
|
if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{-------------------------------Medium block--------------------------------------}
|
|
|
|
|
{What is the available size in the block being reallocated?}
|
|
|
|
|
LOldAvailableSize := (LBlockHeader and DropMediumAndLargeFlagsMask);
|
|
|
|
|
{Get a pointer to the next block}
|
|
|
|
|
LPNextBlock := PCardinal(Cardinal(APointer) + LOldAvailableSize);
|
|
|
|
|
{Subtract the block header size from the old available size}
|
|
|
|
|
Dec(LOldAvailableSize, BlockHeaderSize);
|
|
|
|
|
{Is it an upsize or a downsize?}
|
|
|
|
|
if Cardinal(ANewSize) > LOldAvailableSize then
|
|
|
|
|
begin
|
|
|
|
|
{Can we do an in-place upsize?}
|
|
|
|
|
LNextBlockSizeAndFlags := PCardinal(Cardinal(LPNextBlock) - BlockHeaderSize)^;
|
|
|
|
|
{Is the next block free?}
|
|
|
|
|
if LNextBlockSizeAndFlags and IsFreeBlockFlag <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
LNextBlockSize := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
|
|
|
|
|
{The available size including the next block}
|
|
|
|
|
LNewAvailableSize := LOldAvailableSize + LNextBlockSize;
|
|
|
|
|
{Can the block fit?}
|
|
|
|
|
if Cardinal(ANewSize) <= LNewAvailableSize then
|
|
|
|
|
begin
|
|
|
|
|
{The next block is free and there is enough space to grow this
|
|
|
|
|
block in place.}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
if IsMultiThread then
|
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
{Multi-threaded application - lock medium blocks and re-read the
|
|
|
|
|
information on the blocks.}
|
|
|
|
|
LockMediumBlocks;
|
|
|
|
|
{Re-read the info for this block}
|
|
|
|
|
LBlockFlags := PCardinal(Cardinal(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask;
|
|
|
|
|
{Re-read the info for the next block}
|
|
|
|
|
LNextBlockSizeAndFlags := PCardinal(Cardinal(LPNextBlock) - BlockHeaderSize)^;
|
|
|
|
|
{Recalculate the next block size}
|
|
|
|
|
LNextBlockSize := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
|
|
|
|
|
{The available size including the next block}
|
|
|
|
|
LNewAvailableSize := LOldAvailableSize + LNextBlockSize;
|
|
|
|
|
{Is the next block still free and the size still sufficient?}
|
|
|
|
|
if (LNextBlockSizeAndFlags and IsFreeBlockFlag <> 0)
|
|
|
|
|
and (Cardinal(ANewSize) <= LNewAvailableSize) then
|
|
|
|
|
begin
|
|
|
|
|
{Upsize the block in-place}
|
|
|
|
|
MediumBlockInPlaceUpsize;
|
|
|
|
|
{Unlock the medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{Return the result}
|
|
|
|
|
Result := APointer;
|
|
|
|
|
{Done}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Couldn't use the block: Unlock the medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Extract the block flags}
|
|
|
|
|
LBlockFlags := ExtractMediumAndLargeFlagsMask and LBlockHeader;
|
|
|
|
|
{Upsize the block in-place}
|
|
|
|
|
MediumBlockInPlaceUpsize;
|
|
|
|
|
{Return the result}
|
|
|
|
|
Result := APointer;
|
|
|
|
|
{Done}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{Couldn't upsize in place. Grab a new block and move the data across:
|
|
|
|
|
If we have to reallocate and move medium blocks, we grow by at
|
|
|
|
|
least 25%}
|
|
|
|
|
LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
|
|
|
|
|
if Cardinal(ANewSize) < LMinimumUpsize then
|
|
|
|
|
LNewAllocSize := LMinimumUpsize
|
|
|
|
|
else
|
|
|
|
|
LNewAllocSize := ANewSize;
|
|
|
|
|
{Allocate the new block}
|
|
|
|
|
Result := FastGetMem(LNewAllocSize);
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
|
|
|
|
{If its a Large block - store the actual user requested size}
|
|
|
|
|
if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
|
|
|
|
|
PLargeBlockHeader(Cardinal(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
|
|
|
|
|
{Move the data across}
|
|
|
|
|
{$ifdef UseCustomVariableSizeMoveRoutines}
|
|
|
|
|
MoveX16L4(APointer^, Result^, LOldAvailableSize);
|
|
|
|
|
{$else}
|
|
|
|
|
System.Move(APointer^, Result^, LOldAvailableSize);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Free the old block}
|
|
|
|
|
FastFreeMem(APointer);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Must be less than half the current size or we don't bother resizing.}
|
|
|
|
|
if Cardinal(ANewSize * 2) >= LOldAvailableSize then
|
|
|
|
|
begin
|
|
|
|
|
Result := APointer;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{In-place downsize? Balance the cost of moving the data vs. the cost
|
|
|
|
|
of fragmenting the memory pool. Medium blocks in use may never be
|
|
|
|
|
smaller than MinimumMediumBlockSize.}
|
|
|
|
|
if ANewSize >= (MinimumMediumBlockSize - BlockHeaderSize) then
|
|
|
|
|
begin
|
|
|
|
|
MediumBlockInPlaceDownsize;
|
|
|
|
|
Result := APointer;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{The requested size is less than the minimum medium block size. If
|
|
|
|
|
the requested size is less than the threshold value (currently a
|
|
|
|
|
quarter of the minimum medium block size), move the data to a small
|
|
|
|
|
block, otherwise shrink the medium block to the minimum allowable
|
|
|
|
|
medium block size.}
|
|
|
|
|
if Cardinal(ANewSize) >= MediumInPlaceDownsizeLimit then
|
|
|
|
|
begin
|
|
|
|
|
{The request is for a size smaller than the minimum medium block
|
|
|
|
|
size, but not small enough to justify moving data: Reduce the
|
|
|
|
|
block size to the minimum medium block size}
|
|
|
|
|
ANewSize := MinimumMediumBlockSize - BlockHeaderSize;
|
|
|
|
|
{Is it already at the minimum medium block size?}
|
|
|
|
|
if LOldAvailableSize > Cardinal(ANewSize) then
|
|
|
|
|
MediumBlockInPlaceDownsize;
|
|
|
|
|
Result := APointer;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Allocate the new block}
|
|
|
|
|
Result := FastGetMem(ANewSize);
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
|
|
|
|
{Move the data across}
|
|
|
|
|
{$ifdef UseCustomVariableSizeMoveRoutines}
|
|
|
|
|
{$ifdef Align16Bytes}
|
|
|
|
|
MoveX16L4(APointer^, Result^, ANewSize);
|
|
|
|
|
{$else}
|
|
|
|
|
MoveX8L4(APointer^, Result^, ANewSize);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$else}
|
|
|
|
|
System.Move(APointer^, Result^, ANewSize);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Free the old block}
|
|
|
|
|
FastFreeMem(APointer);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Is this a valid large block?}
|
|
|
|
|
if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{-----------------------Large block------------------------------}
|
|
|
|
|
Result := ReallocateLargeBlock(APointer, ANewSize);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{-----------------------Invalid block------------------------------}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Bad pointer: probably an attempt to reallocate a free memory block.}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$else}
|
|
|
|
|
{Replacement for SysReallocMem (asm version)}
|
|
|
|
|
function FastReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
|
|
|
|
|
asm
|
|
|
|
|
{On entry: eax = APointer; edx = ANewSize}
|
|
|
|
|
{Get the block header: Is it actually a small block?}
|
|
|
|
|
mov ecx, [eax - 4]
|
|
|
|
|
{Is it a small block?}
|
|
|
|
|
test cl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
|
|
|
|
|
{Save ebx}
|
|
|
|
|
push ebx
|
|
|
|
|
{Save esi}
|
|
|
|
|
push esi
|
|
|
|
|
{Save the original pointer in esi}
|
|
|
|
|
mov esi, eax
|
|
|
|
|
{Is it a small block?}
|
|
|
|
|
jnz @NotASmallBlock
|
|
|
|
|
{-----------------------------------Small block-------------------------------------}
|
|
|
|
|
{Get the block type in ebx}
|
|
|
|
|
mov ebx, TSmallBlockPoolHeader[ecx].BlockType
|
|
|
|
|
{Get the available size inside blocks of this type.}
|
|
|
|
|
movzx ecx, TSmallBlockType[ebx].BlockSize
|
|
|
|
|
sub ecx, 4
|
|
|
|
|
{Is it an upsize or a downsize?}
|
|
|
|
|
cmp ecx, edx
|
|
|
|
|
jb @SmallUpsize
|
|
|
|
|
{It's a downsize. Do we need to allocate a smaller block? Only if the new
|
|
|
|
|
size is less than a quarter of the available size less
|
|
|
|
|
SmallBlockDownsizeCheckAdder bytes}
|
|
|
|
|
lea ebx, [edx * 4 + SmallBlockDownsizeCheckAdder]
|
|
|
|
|
cmp ebx, ecx
|
|
|
|
|
jb @NotSmallInPlaceDownsize
|
|
|
|
|
{In-place downsize - return the original pointer}
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
@NotSmallInPlaceDownsize:
|
|
|
|
|
{Save the requested size}
|
|
|
|
|
mov ebx, edx
|
|
|
|
|
{Allocate a smaller block}
|
|
|
|
|
mov eax, edx
|
|
|
|
|
call FastGetMem
|
|
|
|
|
{Allocated OK?}
|
|
|
|
|
test eax, eax
|
|
|
|
|
jz @SmallDownsizeDone
|
|
|
|
|
{Move data across: count in ecx}
|
|
|
|
|
mov ecx, ebx
|
|
|
|
|
{Destination in edx}
|
|
|
|
|
mov edx, eax
|
|
|
|
|
{Save the result in ebx}
|
|
|
|
|
mov ebx, eax
|
|
|
|
|
{Original pointer in eax}
|
|
|
|
|
mov eax, esi
|
|
|
|
|
{Move the data across}
|
|
|
|
|
{$ifdef UseCustomVariableSizeMoveRoutines}
|
|
|
|
|
{$ifdef Align16Bytes}
|
|
|
|
|
call MoveX16L4
|
|
|
|
|
{$else}
|
|
|
|
|
call MoveX8L4
|
|
|
|
|
{$endif}
|
|
|
|
|
{$else}
|
|
|
|
|
call System.Move
|
|
|
|
|
{$endif}
|
|
|
|
|
{Free the original pointer}
|
|
|
|
|
mov eax, esi
|
|
|
|
|
call FastFreeMem
|
|
|
|
|
{Return the pointer}
|
|
|
|
|
mov eax, ebx
|
|
|
|
|
@SmallDownsizeDone:
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@SmallUpsize:
|
|
|
|
|
{State: esi = APointer, edx = ANewSize, ecx = Current Block Size, ebx = Current Block Type}
|
|
|
|
|
{This pointer is being reallocated to a larger block and therefore it is
|
|
|
|
|
logical to assume that it may be enlarged again. Since reallocations are
|
|
|
|
|
expensive, there is a minimum upsize percentage to avoid unnecessary
|
|
|
|
|
future move operations.}
|
|
|
|
|
{Small blocks always grow with at least 100% + SmallBlockUpsizeAdder bytes}
|
|
|
|
|
lea ecx, [ecx + ecx + SmallBlockUpsizeAdder]
|
|
|
|
|
{save edi}
|
|
|
|
|
push edi
|
|
|
|
|
{Save the requested size in edi}
|
|
|
|
|
mov edi, edx
|
|
|
|
|
{New allocated size is the maximum of the requested size and the minimum
|
|
|
|
|
upsize}
|
|
|
|
|
xor eax, eax
|
|
|
|
|
sub ecx, edx
|
|
|
|
|
adc eax, -1
|
|
|
|
|
and eax, ecx
|
|
|
|
|
add eax, edx
|
|
|
|
|
{Allocate the new block}
|
|
|
|
|
call FastGetMem
|
|
|
|
|
{Allocated OK?}
|
|
|
|
|
test eax, eax
|
|
|
|
|
jz @SmallUpsizeDone
|
|
|
|
|
{Do we need to store the requested size? Only large blocks store the
|
|
|
|
|
requested size.}
|
|
|
|
|
cmp edi, MaximumMediumBlockSize - BlockHeaderSize
|
|
|
|
|
jbe @NotSmallUpsizeToLargeBlock
|
|
|
|
|
{Store the user requested size}
|
|
|
|
|
mov [eax - 8], edi
|
|
|
|
|
@NotSmallUpsizeToLargeBlock:
|
|
|
|
|
{Get the size to move across}
|
|
|
|
|
movzx ecx, TSmallBlockType[ebx].BlockSize
|
|
|
|
|
sub ecx, BlockHeaderSize
|
|
|
|
|
{Move to the new block}
|
|
|
|
|
mov edx, eax
|
|
|
|
|
{Save the result in edi}
|
|
|
|
|
mov edi, eax
|
|
|
|
|
{Move from the old block}
|
|
|
|
|
mov eax, esi
|
|
|
|
|
{Move the data across}
|
|
|
|
|
{$ifdef UseCustomFixedSizeMoveRoutines}
|
|
|
|
|
call TSmallBlockType[ebx].UpsizeMoveProcedure
|
|
|
|
|
{$else}
|
|
|
|
|
call System.Move
|
|
|
|
|
{$endif}
|
|
|
|
|
{Free the old pointer}
|
|
|
|
|
mov eax, esi
|
|
|
|
|
call FastFreeMem
|
|
|
|
|
{Done}
|
|
|
|
|
mov eax, edi
|
|
|
|
|
@SmallUpsizeDone:
|
|
|
|
|
pop edi
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
@NotASmallBlock:
|
|
|
|
|
{Is this a medium block or a large block?}
|
|
|
|
|
test cl, IsFreeBlockFlag + IsLargeBlockFlag
|
|
|
|
|
jnz @PossibleLargeBlock
|
|
|
|
|
{-------------------------------Medium block--------------------------------------}
|
|
|
|
|
{Status: ecx = Current Block Size + Flags, eax/esi = APointer,
|
|
|
|
|
edx = Requested Size}
|
|
|
|
|
mov ebx, ecx
|
|
|
|
|
{Drop the flags from the header}
|
|
|
|
|
and ecx, DropMediumAndLargeFlagsMask
|
|
|
|
|
{Save edi}
|
|
|
|
|
push edi
|
|
|
|
|
{Get a pointer to the next block in edi}
|
|
|
|
|
lea edi, [eax + ecx]
|
|
|
|
|
{Subtract the block header size from the old available size}
|
|
|
|
|
sub ecx, BlockHeaderSize
|
|
|
|
|
{Get the complete flags in ebx}
|
|
|
|
|
and ebx, ExtractMediumAndLargeFlagsMask
|
|
|
|
|
{Is it an upsize or a downsize?}
|
|
|
|
|
cmp edx, ecx
|
|
|
|
|
{Save ebp}
|
|
|
|
|
push ebp
|
|
|
|
|
{Is it an upsize or a downsize?}
|
|
|
|
|
ja @MediumBlockUpsize
|
|
|
|
|
{Status: ecx = Current Block Size - 4, bl = Current Block Flags,
|
|
|
|
|
edi = @Next Block, eax/esi = APointer, edx = Requested Size}
|
|
|
|
|
{Must be less than half the current size or we don't bother resizing.}
|
|
|
|
|
lea ebp, [edx + edx]
|
|
|
|
|
cmp ebp, ecx
|
|
|
|
|
jb @MediumMustDownsize
|
|
|
|
|
@MediumNoResize:
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop ebp
|
|
|
|
|
pop edi
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
{Return}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@MediumMustDownsize:
|
|
|
|
|
{In-place downsize? Balance the cost of moving the data vs. the cost of
|
|
|
|
|
fragmenting the memory pool. Medium blocks in use may never be smaller
|
|
|
|
|
than MinimumMediumBlockSize.}
|
|
|
|
|
cmp edx, MinimumMediumBlockSize - BlockHeaderSize
|
|
|
|
|
jae @MediumBlockInPlaceDownsize
|
|
|
|
|
{The requested size is less than the minimum medium block size. If the
|
|
|
|
|
requested size is less than the threshold value (currently a quarter of the
|
|
|
|
|
minimum medium block size), move the data to a small block, otherwise shrink
|
|
|
|
|
the medium block to the minimum allowable medium block size.}
|
|
|
|
|
cmp edx, MediumInPlaceDownsizeLimit
|
|
|
|
|
jb @MediumDownsizeRealloc
|
|
|
|
|
{The request is for a size smaller than the minimum medium block size, but
|
|
|
|
|
not small enough to justify moving data: Reduce the block size to the
|
|
|
|
|
minimum medium block size}
|
|
|
|
|
mov edx, MinimumMediumBlockSize - BlockHeaderSize
|
|
|
|
|
{Is it already at the minimum medium block size?}
|
|
|
|
|
cmp ecx, edx
|
|
|
|
|
jna @MediumNoResize
|
|
|
|
|
@MediumBlockInPlaceDownsize:
|
|
|
|
|
{Round up to the next medium block size}
|
|
|
|
|
lea ebp, [edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
|
|
|
|
|
and ebp, -MediumBlockGranularity;
|
|
|
|
|
add ebp, MediumBlockSizeOffset
|
|
|
|
|
{Get the size of the second split}
|
|
|
|
|
add ecx, BlockHeaderSize
|
|
|
|
|
sub ecx, ebp
|
|
|
|
|
{Lock the medium blocks}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
cmp IsMultiThread, False
|
|
|
|
|
je @DoMediumInPlaceDownsize
|
|
|
|
|
{$endif}
|
|
|
|
|
@DoMediumLockForDownsize:
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Lock the medium blocks (ecx *must* be preserved)}
|
|
|
|
|
call LockMediumBlocks
|
|
|
|
|
{Reread the flags - they may have changed before medium blocks could be
|
|
|
|
|
locked.}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
mov ebx, ExtractMediumAndLargeFlagsMask
|
|
|
|
|
and ebx, [esi - 4]
|
|
|
|
|
@DoMediumInPlaceDownsize:
|
|
|
|
|
{Set the new size}
|
|
|
|
|
or ebx, ebp
|
|
|
|
|
mov [esi - 4], ebx
|
|
|
|
|
{Get the second split size in ebx}
|
|
|
|
|
mov ebx, ecx
|
|
|
|
|
{Is the next block in use?}
|
|
|
|
|
mov edx, [edi - 4]
|
|
|
|
|
test dl, IsFreeBlockFlag
|
|
|
|
|
jnz @MediumDownsizeNextBlockFree
|
|
|
|
|
{The next block is in use: flag its previous block as free}
|
|
|
|
|
or edx, PreviousMediumBlockIsFreeFlag
|
|
|
|
|
mov [edi - 4], edx
|
|
|
|
|
jmp @MediumDownsizeDoSplit
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
2008-07-25 09:23:00 -04:00
|
|
|
|
nop
|
|
|
|
|
{$ifdef AssumeMultiThreaded}
|
|
|
|
|
nop
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
@MediumDownsizeNextBlockFree:
|
|
|
|
|
{The next block is free: combine it}
|
|
|
|
|
mov eax, edi
|
|
|
|
|
and edx, DropMediumAndLargeFlagsMask
|
|
|
|
|
add ebx, edx
|
|
|
|
|
add edi, edx
|
|
|
|
|
cmp edx, MinimumMediumBlockSize
|
|
|
|
|
jb @MediumDownsizeDoSplit
|
|
|
|
|
call RemoveMediumFreeBlock
|
|
|
|
|
@MediumDownsizeDoSplit:
|
|
|
|
|
{Store the trailing size field}
|
|
|
|
|
mov [edi - 8], ebx
|
|
|
|
|
{Store the free part's header}
|
|
|
|
|
lea eax, [ebx + IsMediumBlockFlag + IsFreeBlockFlag];
|
|
|
|
|
mov [esi + ebp - 4], eax
|
|
|
|
|
{Bin this free block}
|
|
|
|
|
cmp ebx, MinimumMediumBlockSize
|
|
|
|
|
jb @MediumBlockDownsizeDone
|
|
|
|
|
lea eax, [esi + ebp]
|
|
|
|
|
mov edx, ebx
|
|
|
|
|
call InsertMediumBlockIntoBin
|
|
|
|
|
@MediumBlockDownsizeDone:
|
|
|
|
|
{Unlock the medium blocks}
|
|
|
|
|
mov MediumBlocksLocked, False
|
|
|
|
|
{Result = old pointer}
|
|
|
|
|
mov eax, esi
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop ebp
|
|
|
|
|
pop edi
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
{Return}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
@MediumDownsizeRealloc:
|
|
|
|
|
{Save the requested size}
|
|
|
|
|
mov edi, edx
|
|
|
|
|
mov eax, edx
|
|
|
|
|
{Allocate the new block}
|
|
|
|
|
call FastGetMem
|
|
|
|
|
test eax, eax
|
|
|
|
|
jz @MediumBlockDownsizeExit
|
|
|
|
|
{Save the result}
|
|
|
|
|
mov ebp, eax
|
|
|
|
|
mov edx, eax
|
|
|
|
|
mov eax, esi
|
|
|
|
|
mov ecx, edi
|
|
|
|
|
{Move the data across}
|
|
|
|
|
{$ifdef UseCustomVariableSizeMoveRoutines}
|
|
|
|
|
{$ifdef Align16Bytes}
|
|
|
|
|
call MoveX16L4
|
|
|
|
|
{$else}
|
|
|
|
|
call MoveX8L4
|
|
|
|
|
{$endif}
|
|
|
|
|
{$else}
|
|
|
|
|
call System.Move
|
|
|
|
|
{$endif}
|
|
|
|
|
mov eax, esi
|
|
|
|
|
call FastFreeMem
|
|
|
|
|
{Return the result}
|
|
|
|
|
mov eax, ebp
|
|
|
|
|
@MediumBlockDownsizeExit:
|
|
|
|
|
pop ebp
|
|
|
|
|
pop edi
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
@MediumBlockUpsize:
|
|
|
|
|
{Status: ecx = Current Block Size - 4, bl = Current Block Flags,
|
|
|
|
|
edi = @Next Block, eax/esi = APointer, edx = Requested Size}
|
|
|
|
|
{Can we do an in-place upsize?}
|
|
|
|
|
mov eax, [edi - 4]
|
|
|
|
|
test al, IsFreeBlockFlag
|
|
|
|
|
jz @CannotUpsizeMediumBlockInPlace
|
|
|
|
|
{Get the total available size including the next block}
|
|
|
|
|
and eax, DropMediumAndLargeFlagsMask
|
|
|
|
|
{ebp = total available size including the next block (excluding the header)}
|
|
|
|
|
lea ebp, [eax + ecx]
|
|
|
|
|
{Can the block fit?}
|
|
|
|
|
cmp edx, ebp
|
|
|
|
|
ja @CannotUpsizeMediumBlockInPlace
|
|
|
|
|
{The next block is free and there is enough space to grow this
|
|
|
|
|
block in place.}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
cmp IsMultiThread, False
|
|
|
|
|
je @DoMediumInPlaceUpsize
|
|
|
|
|
{$endif}
|
|
|
|
|
@DoMediumLockForUpsize:
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Lock the medium blocks (ecx and edx *must* be preserved}
|
|
|
|
|
call LockMediumBlocks
|
|
|
|
|
{Re-read the info for this block (since it may have changed before the medium
|
|
|
|
|
blocks could be locked)}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
mov ebx, ExtractMediumAndLargeFlagsMask
|
|
|
|
|
and ebx, [esi - 4]
|
|
|
|
|
{Re-read the info for the next block}
|
|
|
|
|
mov eax, [edi - 4]
|
|
|
|
|
{Next block still free?}
|
|
|
|
|
test al, IsFreeBlockFlag
|
|
|
|
|
jz @NextMediumBlockChanged
|
|
|
|
|
{Recalculate the next block size}
|
|
|
|
|
and eax, DropMediumAndLargeFlagsMask
|
|
|
|
|
{The available size including the next block}
|
|
|
|
|
lea ebp, [eax + ecx]
|
|
|
|
|
{Can the block still fit?}
|
|
|
|
|
cmp edx, ebp
|
|
|
|
|
ja @NextMediumBlockChanged
|
|
|
|
|
@DoMediumInPlaceUpsize:
|
|
|
|
|
{Is the next block binnable?}
|
|
|
|
|
cmp eax, MinimumMediumBlockSize
|
|
|
|
|
{Remove the next block}
|
|
|
|
|
jb @MediumInPlaceNoNextRemove
|
|
|
|
|
mov eax, edi
|
|
|
|
|
push ecx
|
|
|
|
|
push edx
|
|
|
|
|
call RemoveMediumFreeBlock
|
|
|
|
|
pop edx
|
|
|
|
|
pop ecx
|
|
|
|
|
@MediumInPlaceNoNextRemove:
|
|
|
|
|
{Medium blocks grow a minimum of 25% in in-place upsizes}
|
|
|
|
|
mov eax, ecx
|
|
|
|
|
shr eax, 2
|
|
|
|
|
add eax, ecx
|
|
|
|
|
{Get the maximum of the requested size and the minimum growth size}
|
|
|
|
|
xor edi, edi
|
|
|
|
|
sub eax, edx
|
|
|
|
|
adc edi, -1
|
|
|
|
|
and eax, edi
|
|
|
|
|
{Round up to the nearest block size granularity}
|
|
|
|
|
lea eax, [eax + edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
|
|
|
|
|
and eax, -MediumBlockGranularity
|
|
|
|
|
add eax, MediumBlockSizeOffset
|
|
|
|
|
{Calculate the size of the second split}
|
|
|
|
|
lea edx, [ebp + BlockHeaderSize]
|
|
|
|
|
sub edx, eax
|
|
|
|
|
{Does it fit?}
|
|
|
|
|
ja @MediumInPlaceUpsizeSplit
|
|
|
|
|
{Grab the whole block: Mark it as used in the block following it}
|
|
|
|
|
and dword ptr [esi + ebp], not PreviousMediumBlockIsFreeFlag
|
|
|
|
|
{The block size is the full available size plus header}
|
|
|
|
|
add ebp, 4
|
|
|
|
|
{Upsize done}
|
|
|
|
|
jmp @MediumUpsizeInPlaceDone
|
|
|
|
|
{Align branch target}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
nop
|
2006-04-15 03:44:05 -04:00
|
|
|
|
nop
|
|
|
|
|
nop
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
@MediumInPlaceUpsizeSplit:
|
|
|
|
|
{Store the size of the second split as the second last dword}
|
|
|
|
|
mov [esi + ebp - 4], edx
|
|
|
|
|
{Set the second split header}
|
|
|
|
|
lea edi, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
|
|
|
|
|
mov [esi + eax - 4], edi
|
|
|
|
|
mov ebp, eax
|
|
|
|
|
cmp edx, MinimumMediumBlockSize
|
|
|
|
|
jb @MediumUpsizeInPlaceDone
|
|
|
|
|
add eax, esi
|
|
|
|
|
call InsertMediumBlockIntoBin
|
|
|
|
|
@MediumUpsizeInPlaceDone:
|
|
|
|
|
{Set the size and flags for this block}
|
|
|
|
|
or ebp, ebx
|
|
|
|
|
mov [esi - 4], ebp
|
|
|
|
|
{Unlock the medium blocks}
|
|
|
|
|
mov MediumBlocksLocked, False
|
|
|
|
|
{Result = old pointer}
|
|
|
|
|
mov eax, esi
|
|
|
|
|
@MediumBlockResizeDone2:
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop ebp
|
|
|
|
|
pop edi
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
{Return}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target for "@CannotUpsizeMediumBlockInPlace"}
|
|
|
|
|
nop
|
|
|
|
|
nop
|
|
|
|
|
@NextMediumBlockChanged:
|
|
|
|
|
{The next medium block changed while the medium blocks were being locked}
|
|
|
|
|
mov MediumBlocksLocked, False
|
|
|
|
|
@CannotUpsizeMediumBlockInPlace:
|
|
|
|
|
{Couldn't upsize in place. Grab a new block and move the data across:
|
|
|
|
|
If we have to reallocate and move medium blocks, we grow by at
|
|
|
|
|
least 25%}
|
|
|
|
|
mov eax, ecx
|
|
|
|
|
shr eax, 2
|
|
|
|
|
add eax, ecx
|
|
|
|
|
{Get the maximum of the requested size and the minimum growth size}
|
|
|
|
|
xor edi, edi
|
|
|
|
|
sub eax, edx
|
|
|
|
|
adc edi, -1
|
|
|
|
|
and eax, edi
|
|
|
|
|
add eax, edx
|
|
|
|
|
{Save the size to allocate}
|
|
|
|
|
mov ebp, eax
|
|
|
|
|
{Save the size to move across}
|
|
|
|
|
mov edi, ecx
|
|
|
|
|
{Get the block}
|
|
|
|
|
push edx
|
|
|
|
|
call FastGetMem
|
|
|
|
|
pop edx
|
|
|
|
|
{Success?}
|
|
|
|
|
test eax, eax
|
|
|
|
|
jz @MediumBlockResizeDone2
|
|
|
|
|
{If it's a Large block - store the actual user requested size}
|
|
|
|
|
cmp ebp, MaximumMediumBlockSize - BlockHeaderSize
|
|
|
|
|
jbe @MediumUpsizeNotLarge
|
|
|
|
|
mov [eax - 8], edx
|
|
|
|
|
@MediumUpsizeNotLarge:
|
|
|
|
|
{Save the result}
|
|
|
|
|
mov ebp, eax
|
|
|
|
|
{Move the data across}
|
|
|
|
|
mov edx, eax
|
|
|
|
|
mov eax, esi
|
|
|
|
|
mov ecx, edi
|
|
|
|
|
{$ifdef UseCustomVariableSizeMoveRoutines}
|
|
|
|
|
call MoveX16L4
|
|
|
|
|
{$else}
|
|
|
|
|
call System.Move
|
|
|
|
|
{$endif}
|
|
|
|
|
{Free the old block}
|
|
|
|
|
mov eax, esi
|
|
|
|
|
call FastFreeMem
|
|
|
|
|
{Restore the result}
|
|
|
|
|
mov eax, ebp
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop ebp
|
|
|
|
|
pop edi
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
{Return}
|
|
|
|
|
ret
|
|
|
|
|
{Align branch target}
|
|
|
|
|
nop
|
|
|
|
|
@PossibleLargeBlock:
|
|
|
|
|
{-----------------------Large block------------------------------}
|
|
|
|
|
{Restore registers}
|
|
|
|
|
pop esi
|
|
|
|
|
pop ebx
|
|
|
|
|
{Is this a valid large block?}
|
|
|
|
|
test cl, IsFreeBlockFlag + IsMediumBlockFlag
|
|
|
|
|
jz ReallocateLargeBlock
|
|
|
|
|
{-----------------------Invalid block------------------------------}
|
|
|
|
|
xor eax, eax
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Allocates a block and fills it with zeroes}
|
|
|
|
|
{$ifndef ASMVersion}
|
|
|
|
|
function FastAllocMem(ASize: Cardinal): Pointer;
|
|
|
|
|
begin
|
|
|
|
|
Result := FastGetMem(ASize);
|
|
|
|
|
{Large blocks are already zero filled}
|
|
|
|
|
if (Result <> nil) and (ASize <= (MaximumMediumBlockSize - BlockHeaderSize)) then
|
|
|
|
|
FillChar(Result^, ASize, 0);
|
|
|
|
|
end;
|
|
|
|
|
{$else}
|
|
|
|
|
function FastAllocMem(ASize: Cardinal): Pointer;
|
|
|
|
|
asm
|
|
|
|
|
push ebx
|
|
|
|
|
{Get the size rounded down to the previous multiple of 4 into ebx}
|
|
|
|
|
lea ebx, [eax - 1]
|
|
|
|
|
and ebx, -4
|
|
|
|
|
{Get the block}
|
|
|
|
|
call FastGetMem
|
|
|
|
|
{Could a block be allocated? ecx = 0 if yes, $ffffffff if no}
|
|
|
|
|
cmp eax, 1
|
|
|
|
|
sbb ecx, ecx
|
|
|
|
|
{Point edx to the last dword}
|
|
|
|
|
lea edx, [eax + ebx]
|
|
|
|
|
{ebx = $ffffffff if no block could be allocated, otherwise size rounded down
|
|
|
|
|
to previous multiple of 4}
|
|
|
|
|
or ebx, ecx
|
|
|
|
|
{Large blocks are already zero filled}
|
|
|
|
|
cmp ebx, MaximumMediumBlockSize - BlockHeaderSize
|
|
|
|
|
jae @Done
|
|
|
|
|
{Make the counter negative based}
|
|
|
|
|
neg ebx
|
|
|
|
|
{Load zero into st(0)}
|
|
|
|
|
fldz
|
|
|
|
|
{Clear groups of 8 bytes. Block sizes are always four less than a multiple
|
|
|
|
|
of 8, with a minimum of 12 bytes}
|
|
|
|
|
@FillLoop:
|
|
|
|
|
fst qword ptr [edx + ebx]
|
|
|
|
|
add ebx, 8
|
|
|
|
|
js @FillLoop
|
|
|
|
|
{Clear the last four bytes}
|
|
|
|
|
mov [edx], ecx
|
|
|
|
|
{Clear st(0)}
|
|
|
|
|
ffree st(0)
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Correct the stack top}
|
|
|
|
|
fincstp
|
2006-04-15 03:44:05 -04:00
|
|
|
|
@Done:
|
|
|
|
|
pop ebx
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{-----------------Post Uninstall GetMem/FreeMem/ReallocMem-------------------}
|
|
|
|
|
|
|
|
|
|
{$ifdef DetectMMOperationsAfterUninstall}
|
|
|
|
|
|
|
|
|
|
function InvalidGetMem(ASize: Integer): Pointer;
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LErrorMessageTitle: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(InvalidGetMemMsg);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(InvalidGetMemMsg, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function InvalidFreeMem(APointer: Pointer): Integer;
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LErrorMessageTitle: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(InvalidFreeMemMsg);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(InvalidFreeMemMsg, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
Result := -1;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function InvalidReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LErrorMessageTitle: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(InvalidReallocMemMsg);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(InvalidReallocMemMsg, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function InvalidAllocMem(ASize: Cardinal): Pointer;
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LErrorMessageTitle: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(InvalidAllocMemMsg);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(InvalidAllocMemMsg, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function InvalidRegisterAndUnRegisterMemoryLeak(APointer: Pointer): Boolean;
|
|
|
|
|
begin
|
|
|
|
|
Result := False;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{-----------------Full Debug Mode Memory Manager Interface--------------------}
|
|
|
|
|
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
|
|
|
|
|
procedure DeleteEventLog;
|
|
|
|
|
begin
|
|
|
|
|
{Delete the file}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
DeleteFileA(MMLogFileName);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure AppendEventLog(ABuffer: Pointer; ACount: Cardinal);
|
|
|
|
|
var
|
|
|
|
|
LFileHandle, LBytesWritten: Cardinal;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LEventHeader: array[0..1023] of AnsiChar;
|
|
|
|
|
LMsgPtr: PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LSystemTime: TSystemTime;
|
|
|
|
|
begin
|
|
|
|
|
{Append the file}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LFileHandle := CreateFileA(MMLogFileName, GENERIC_READ or GENERIC_WRITE,
|
2006-04-15 03:44:05 -04:00
|
|
|
|
0, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
|
|
|
|
if LFileHandle <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Seek to the end of the file}
|
|
|
|
|
SetFilePointer(LFileHandle, 0, nil, FILE_END);
|
|
|
|
|
{Set the separator}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(CRLF, @LEventHeader[0], length(CRLF));
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(EventSeparator, LMsgPtr, length(EventSeparator));
|
|
|
|
|
{Set the date & time}
|
|
|
|
|
GetLocalTime(LSystemTime);
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LSystemTime.wYear, LMsgPtr);
|
|
|
|
|
LMsgPtr^ := '/';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LSystemTime.wMonth, LMsgPtr);
|
|
|
|
|
LMsgPtr^ := '/';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LSystemTime.wDay, LMsgPtr);
|
|
|
|
|
LMsgPtr^ := ' ';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LSystemTime.wHour, LMsgPtr);
|
|
|
|
|
LMsgPtr^ := ':';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
if LSystemTime.wMinute < 10 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr^ := '0';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LSystemTime.wMinute, LMsgPtr);
|
|
|
|
|
LMsgPtr^ := ':';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
if LSystemTime.wSecond < 10 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr^ := '0';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LSystemTime.WSecond, LMsgPtr);
|
|
|
|
|
{Write the header}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(EventSeparator, LMsgPtr, length(EventSeparator));
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(CRLF, LMsgPtr, length(CRLF));
|
|
|
|
|
WriteFile(LFileHandle, LEventHeader[0], Cardinal(LMsgPtr) - Cardinal(@LEventHeader[0]), LBytesWritten, nil);
|
|
|
|
|
{Write the data}
|
|
|
|
|
WriteFile(LFileHandle, ABuffer^, ACount, LBytesWritten, nil);
|
|
|
|
|
{Close the file}
|
|
|
|
|
CloseHandle(LFileHandle);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Sets the default log filename}
|
|
|
|
|
procedure SetDefaultMMLogFileName;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
const
|
|
|
|
|
LogFileExtAnsi: PAnsiChar = LogFileExtension;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LModuleNameLength: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Get the name of the application}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LModuleNameLength := AppendModuleFileName(@MMLogFileName[0]);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Replace the last few characters}
|
|
|
|
|
if LModuleNameLength > 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Change the filename}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
System.Move(LogFileExtAnsi^, MMLogFileName[LModuleNameLength - 4], StrLen(LogFileExtAnsi));
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Specify the full path and name for the filename to be used for logging memory
|
|
|
|
|
errors, etc. If ALogFileName is nil or points to an empty string it will
|
|
|
|
|
revert to the default log file name.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
procedure SetMMLogFileName(ALogFileName: PAnsiChar = nil);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
i: integer;
|
|
|
|
|
begin
|
|
|
|
|
if (ALogFileName <> nil) and (ALogFileName^ <> #0) then
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
for i := 0 to StrLen(MMLogFileName) - 2 do
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
MMLogFileName[i] := ALogFileName^;
|
|
|
|
|
if MMlogFileName[i] = #0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Inc(ALogFileName);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
SetDefaultMMLogFileName;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Returns the current "allocation group". Whenever a GetMem request is serviced
|
|
|
|
|
in FullDebugMode, the current "allocation group" is stored in the block header.
|
|
|
|
|
This may help with debugging. Note that if a block is subsequently reallocated
|
|
|
|
|
that it keeps its original "allocation group" and "allocation number" (all
|
|
|
|
|
allocations are also numbered sequentially).}
|
|
|
|
|
function GetCurrentAllocationGroup: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
Result := AllocationGroupStack[AllocationGroupStackTop];
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Allocation groups work in a stack like fashion. Group numbers are pushed onto
|
|
|
|
|
and popped off the stack. Note that the stack size is limited, so every push
|
|
|
|
|
should have a matching pop.}
|
|
|
|
|
procedure PushAllocationGroup(ANewCurrentAllocationGroup: Cardinal);
|
|
|
|
|
begin
|
|
|
|
|
if AllocationGroupStackTop < AllocationGroupStackSize - 1 then
|
|
|
|
|
begin
|
|
|
|
|
Inc(AllocationGroupStackTop);
|
|
|
|
|
AllocationGroupStack[AllocationGroupStackTop] := ANewCurrentAllocationGroup;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Raise a runtime error if the stack overflows}
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure PopAllocationGroup;
|
|
|
|
|
begin
|
|
|
|
|
if AllocationGroupStackTop > 0 then
|
|
|
|
|
begin
|
|
|
|
|
Dec(AllocationGroupStackTop);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Raise a runtime error if the stack underflows}
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Sums all the dwords starting at the given address.}
|
|
|
|
|
function SumCardinals(AStartValue: Cardinal; APointer: PCardinal; ACount: Cardinal): Cardinal;
|
|
|
|
|
asm
|
|
|
|
|
{On entry: eax = AStartValue, edx = APointer; ecx = ACount}
|
|
|
|
|
add edx, ecx
|
|
|
|
|
neg ecx
|
|
|
|
|
@AddLoop:
|
|
|
|
|
add eax, [edx + ecx]
|
2008-07-25 09:23:00 -04:00
|
|
|
|
add ecx, 4
|
2006-04-15 03:44:05 -04:00
|
|
|
|
js @AddLoop
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Sums all the dwords starting at the given address for the fill pattern.
|
|
|
|
|
Returns true if they are all valid}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function CheckFillPattern(APointer: PCardinal; ACount: Cardinal): Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
asm
|
|
|
|
|
{On entry: eax = APointer; edx = ACount}
|
|
|
|
|
add eax, edx
|
|
|
|
|
neg edx
|
|
|
|
|
@CheckLoop:
|
|
|
|
|
cmp dword ptr [eax + edx], DebugFillDWord
|
|
|
|
|
jne @Done
|
|
|
|
|
add edx, 4
|
|
|
|
|
js @CheckLoop
|
|
|
|
|
@Done:
|
|
|
|
|
sete al
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Calculates the checksum for the debug header. Adds all dwords in the debug
|
|
|
|
|
header to the start address of the block.}
|
|
|
|
|
function CalculateHeaderCheckSum(APointer: PFullDebugBlockHeader): Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
Result := SumCardinals(Cardinal(APointer),
|
|
|
|
|
PCardinal(Cardinal(APointer) + 8),
|
|
|
|
|
SizeOf(TFullDebugBlockHeader) - 8 - 4);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure UpdateHeaderAndFooterCheckSums(APointer: PFullDebugBlockHeader);
|
|
|
|
|
var
|
|
|
|
|
LHeaderCheckSum: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
LHeaderCheckSum := CalculateHeaderCheckSum(APointer);
|
|
|
|
|
APointer.HeaderCheckSum := LHeaderCheckSum;
|
|
|
|
|
PCardinal(Cardinal(APointer) + SizeOf(TFullDebugBlockHeader) + APointer.UserSize)^ := not LHeaderCheckSum;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function LogCurrentStackTrace(ASkipFrames: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LCurrentStackTrace: TStackTrace;
|
|
|
|
|
begin
|
|
|
|
|
{Get the current call stack}
|
|
|
|
|
GetStackTrace(@LCurrentStackTrace[0], StackTraceDepth, ASkipFrames);
|
|
|
|
|
{List it}
|
|
|
|
|
Result := AppendStringToBuffer(CurrentStackTraceMsg, ABuffer, length(CurrentStackTraceMsg));
|
|
|
|
|
Result := LogStackTrace(@LCurrentStackTrace, StackTraceDepth, Result);
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function LogMemoryDump(APointer: PFullDebugBlockHeader; ABuffer: PAnsiChar): PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LByteNum, LVal: Cardinal;
|
|
|
|
|
LDataPtr: PByte;
|
|
|
|
|
begin
|
|
|
|
|
Result := AppendStringToBuffer(MemoryDumpMsg, ABuffer, Length(MemoryDumpMsg));
|
|
|
|
|
Result := CardinalToHexBuf(Cardinal(APointer) + SizeOf(TFullDebugBlockHeader), Result);
|
|
|
|
|
Result^ := ':';
|
|
|
|
|
Inc(Result);
|
|
|
|
|
{Add the bytes}
|
|
|
|
|
LDataPtr := PByte(Cardinal(APointer) + SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
for LByteNum := 0 to 255 do
|
|
|
|
|
begin
|
|
|
|
|
if LByteNum and 31 = 0 then
|
|
|
|
|
begin
|
|
|
|
|
Result^ := #13;
|
|
|
|
|
Inc(Result);
|
|
|
|
|
Result^ := #10;
|
|
|
|
|
Inc(Result);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result^ := ' ';
|
|
|
|
|
Inc(Result);
|
|
|
|
|
end;
|
|
|
|
|
{Set the hex data}
|
|
|
|
|
LVal := LDataPtr^;
|
|
|
|
|
Result^ := HexTable[LVal shr 4];
|
|
|
|
|
Inc(Result);
|
|
|
|
|
Result^ := HexTable[LVal and $f];
|
|
|
|
|
Inc(Result);
|
|
|
|
|
{Next byte}
|
|
|
|
|
Inc(LDataPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Dump ASCII}
|
|
|
|
|
LDataPtr := PByte(Cardinal(APointer) + SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
for LByteNum := 0 to 255 do
|
|
|
|
|
begin
|
|
|
|
|
if LByteNum and 31 = 0 then
|
|
|
|
|
begin
|
|
|
|
|
Result^ := #13;
|
|
|
|
|
Inc(Result);
|
|
|
|
|
Result^ := #10;
|
|
|
|
|
Inc(Result);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result^ := ' ';
|
|
|
|
|
Inc(Result);
|
|
|
|
|
Result^ := ' ';
|
|
|
|
|
Inc(Result);
|
|
|
|
|
end;
|
|
|
|
|
{Set the hex data}
|
|
|
|
|
LVal := LDataPtr^;
|
|
|
|
|
if LVal < 32 then
|
|
|
|
|
Result^ := '.'
|
|
|
|
|
else
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Result^ := AnsiChar(LVal);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Inc(Result);
|
|
|
|
|
{Next byte}
|
|
|
|
|
Inc(LDataPtr);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure LogBlockError(APointer: PFullDebugBlockHeader; AOperation: TBlockOperation; LHeaderValid, LFooterValid: Boolean);
|
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMsgPtr: PAnsiChar;
|
|
|
|
|
LErrorMessage: array[0..32767] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifndef NoMessageBoxes}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LErrorMessageTitle: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
LClass: TClass;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LCppObjectTypeName: PAnsiChar;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{Display the error header and the operation type.}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(ErrorMsgHeader, @LErrorMessage[0], Length(ErrorMsgHeader));
|
|
|
|
|
case AOperation of
|
|
|
|
|
boGetMem: LMsgPtr := AppendStringToBuffer(GetMemMsg, LMsgPtr, Length(GetMemMsg));
|
|
|
|
|
boFreeMem: LMsgPtr := AppendStringToBuffer(FreeMemMsg, LMsgPtr, Length(FreeMemMsg));
|
|
|
|
|
boReallocMem: LMsgPtr := AppendStringToBuffer(ReallocMemMsg, LMsgPtr, Length(ReallocMemMsg));
|
|
|
|
|
boBlockCheck: LMsgPtr := AppendStringToBuffer(BlockCheckMsg, LMsgPtr, Length(BlockCheckMsg));
|
|
|
|
|
end;
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(OperationMsg, LMsgPtr, Length(OperationMsg));
|
|
|
|
|
{Is the header still intact?}
|
|
|
|
|
if LHeaderValid then
|
|
|
|
|
begin
|
|
|
|
|
{Is the footer still valid?}
|
|
|
|
|
if LFooterValid then
|
|
|
|
|
begin
|
|
|
|
|
{A freed block has been modified, or a double free has occurred}
|
|
|
|
|
if AOperation <= boGetMem then
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(FreeModifiedErrorMsg, LMsgPtr, Length(FreeModifiedErrorMsg))
|
|
|
|
|
else
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(DoubleFreeErrorMsg, LMsgPtr, Length(DoubleFreeErrorMsg));
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(BlockFooterCorruptedMsg, LMsgPtr, Length(BlockFooterCorruptedMsg))
|
|
|
|
|
end;
|
|
|
|
|
{Set the block size message}
|
|
|
|
|
if AOperation <= boGetMem then
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(PreviousBlockSizeMsg, LMsgPtr, Length(PreviousBlockSizeMsg))
|
|
|
|
|
else
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(CurrentBlockSizeMsg, LMsgPtr, Length(CurrentBlockSizeMsg));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(APointer.UserSize, LMsgPtr);
|
|
|
|
|
{The header is still intact - display info about the this/previous allocation}
|
|
|
|
|
if APointer.AllocationStackTrace[0] <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
if AOperation <= boGetMem then
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(StackTraceAtPrevAllocMsg, LMsgPtr, Length(StackTraceAtPrevAllocMsg))
|
|
|
|
|
else
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(StackTraceAtAllocMsg, LMsgPtr, Length(StackTraceAtAllocMsg));
|
|
|
|
|
LMsgPtr := LogStackTrace(@APointer.AllocationStackTrace, StackTraceDepth, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Get the class this block was used for previously}
|
|
|
|
|
LClass := GetObjectClass(@APointer.PreviouslyUsedByClass);
|
|
|
|
|
if (LClass <> nil) and (Cardinal(LClass) <> Cardinal(@FreedObjectVMT.VMTMethods[0])) then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(PreviousObjectClassMsg, LMsgPtr, Length(PreviousObjectClassMsg));
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
if (LClass = nil) and Assigned(GetCppVirtObjTypeNameByVTablePtrFunc) then
|
|
|
|
|
begin
|
|
|
|
|
LCppObjectTypeName := GetCppVirtObjTypeNameByVTablePtrFunc(Pointer(APointer.PreviouslyUsedByClass), 0);
|
|
|
|
|
if Assigned(LCppObjectTypeName) then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(PreviousObjectClassMsg, LMsgPtr, Length(PreviousObjectClassMsg));
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName));
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Get the current class for this block}
|
|
|
|
|
if (AOperation > boGetMem) and (not LFooterValid) then
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMsgPtr := AppendStringToBuffer(CurrentObjectClassMsg, LMsgPtr, Length(CurrentObjectClassMsg));
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LClass := GetObjectClass(Pointer(Cardinal(APointer) + SizeOf(TFullDebugBlockHeader)));
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if Cardinal(LClass) = Cardinal(@FreedObjectVMT.VMTMethods[0]) then
|
|
|
|
|
LClass := nil;
|
|
|
|
|
{$ifndef CheckCppObjectTypeEnabled}
|
|
|
|
|
LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
if (LClass = nil) and Assigned(GetCppVirtObjTypeNameFunc) then
|
|
|
|
|
begin
|
|
|
|
|
LCppObjectTypeName := GetCppVirtObjTypeNameFunc(Pointer(Cardinal(APointer) + SizeOf(TFullDebugBlockHeader)),
|
|
|
|
|
APointer.UserSize);
|
|
|
|
|
if LCppObjectTypeName <> nil then
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName))
|
|
|
|
|
else
|
|
|
|
|
LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
|
|
|
|
|
end
|
2006-04-15 03:44:05 -04:00
|
|
|
|
else
|
2008-07-25 09:23:00 -04:00
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Log the allocation group}
|
|
|
|
|
if APointer.AllocationGroup > 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(CurrentAllocationGroupMsg, LMsgPtr, Length(CurrentAllocationGroupMsg));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(APointer.AllocationGroup, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Log the allocation number}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(CurrentAllocationNumberMsg, LMsgPtr, Length(CurrentAllocationNumberMsg));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(APointer.AllocationNumber, LMsgPtr);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Log the allocation group}
|
|
|
|
|
if APointer.AllocationGroup > 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(PreviousAllocationGroupMsg, LMsgPtr, Length(PreviousAllocationGroupMsg));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(APointer.AllocationGroup, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Log the allocation number}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(PreviousAllocationNumberMsg, LMsgPtr, Length(PreviousAllocationNumberMsg));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(APointer.AllocationNumber, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Get the call stack for the previous free}
|
|
|
|
|
if APointer.FreeStackTrace[0] <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(StackTraceAtFreeMsg, LMsgPtr, Length(StackTraceAtFreeMsg));
|
|
|
|
|
LMsgPtr := LogStackTrace(@APointer.FreeStackTrace, StackTraceDepth, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Header has been corrupted}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedMsg, LMsgPtr, Length(BlockHeaderCorruptedMsg));
|
|
|
|
|
end;
|
|
|
|
|
{Add the current stack trace}
|
|
|
|
|
LMsgPtr := LogCurrentStackTrace(3 + ord(AOperation <> boGetMem) + ord(AOperation = boReallocMem), LMsgPtr);
|
|
|
|
|
{Add the memory dump}
|
|
|
|
|
LMsgPtr := LogMemoryDump(APointer, LMsgPtr);
|
|
|
|
|
{Trailing CRLF}
|
|
|
|
|
LMsgPtr^ := #13;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := #10;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
{Trailing #0}
|
|
|
|
|
LMsgPtr^ := #0;
|
|
|
|
|
{$ifdef LogErrorsToFile}
|
|
|
|
|
{Log the error}
|
|
|
|
|
AppendEventLog(@LErrorMessage[0], Cardinal(LMsgPtr) - Cardinal(@LErrorMessage[0]));
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(LErrorMessage);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{Show the message}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(LErrorMessage, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Logs the stack traces for a memory leak to file}
|
|
|
|
|
procedure LogMemoryLeakOrAllocatedBlock(APointer: PFullDebugBlockHeader; IsALeak: Boolean);
|
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LHeaderValid: Boolean;
|
|
|
|
|
LMsgPtr: PAnsiChar;
|
|
|
|
|
LErrorMessage: array[0..32767] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LClass: TClass;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LCppObjectTypeName: PAnsiChar;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{Display the error header and the operation type.}
|
|
|
|
|
if IsALeak then
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(LeakLogHeader, @LErrorMessage[0], Length(LeakLogHeader))
|
|
|
|
|
else
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(BlockScanLogHeader, @LErrorMessage[0], Length(BlockScanLogHeader));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(GetAvailableSpaceInBlock(APointer) - FullDebugBlockOverhead, LMsgPtr);
|
|
|
|
|
{Is the debug info surrounding the block valid?}
|
|
|
|
|
LHeaderValid := CalculateHeaderCheckSum(APointer) = APointer.HeaderCheckSum;
|
|
|
|
|
{Is the header still intact?}
|
|
|
|
|
if LHeaderValid then
|
|
|
|
|
begin
|
|
|
|
|
{The header is still intact - display info about this/previous allocation}
|
|
|
|
|
if APointer.AllocationStackTrace[0] <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(StackTraceAtAllocMsg, LMsgPtr, Length(StackTraceAtAllocMsg));
|
|
|
|
|
LMsgPtr := LogStackTrace(@APointer.AllocationStackTrace, StackTraceDepth, LMsgPtr);
|
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMsgPtr := AppendStringToBuffer(CurrentObjectClassMsg, LMsgPtr, Length(CurrentObjectClassMsg));
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Get the current class for this block}
|
|
|
|
|
LClass := GetObjectClass(Pointer(Cardinal(APointer) + SizeOf(TFullDebugBlockHeader)));
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if Cardinal(LClass) = Cardinal(@FreedObjectVMT.VMTMethods[0]) then
|
|
|
|
|
LClass := nil;
|
|
|
|
|
{$ifndef CheckCppObjectTypeEnabled}
|
|
|
|
|
LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
if (LClass = nil) and Assigned(GetCppVirtObjTypeNameFunc) then
|
|
|
|
|
begin
|
|
|
|
|
LCppObjectTypeName := GetCppVirtObjTypeNameFunc(Pointer(Cardinal(APointer) + SizeOf(TFullDebugBlockHeader)),
|
|
|
|
|
APointer.UserSize);
|
|
|
|
|
if LCppObjectTypeName <> nil then
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName))
|
|
|
|
|
else
|
|
|
|
|
LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Log the allocation group}
|
|
|
|
|
if APointer.AllocationGroup > 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(CurrentAllocationGroupMsg, LMsgPtr, Length(CurrentAllocationGroupMsg));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(APointer.AllocationGroup, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Log the allocation number}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(CurrentAllocationNumberMsg, LMsgPtr, Length(CurrentAllocationNumberMsg));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(APointer.AllocationNumber, LMsgPtr);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Header has been corrupted}
|
|
|
|
|
LMsgPtr^ := '.';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := ' ';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedMsg, LMsgPtr, Length(BlockHeaderCorruptedMsg));
|
|
|
|
|
end;
|
|
|
|
|
{Add the memory dump}
|
|
|
|
|
LMsgPtr := LogMemoryDump(APointer, LMsgPtr);
|
|
|
|
|
{Trailing CRLF}
|
|
|
|
|
LMsgPtr^ := #13;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := #10;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
{Trailing #0}
|
|
|
|
|
LMsgPtr^ := #0;
|
|
|
|
|
{Log the error}
|
|
|
|
|
AppendEventLog(@LErrorMessage[0], Cardinal(LMsgPtr) - Cardinal(@LErrorMessage[0]));
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Checks that a free block is unmodified}
|
|
|
|
|
function CheckFreeBlockUnmodified(APBlock: PFullDebugBlockHeader; ABlockSize: Cardinal;
|
|
|
|
|
AOperation: TBlockOperation): Boolean;
|
|
|
|
|
var
|
|
|
|
|
LHeaderCheckSum: Cardinal;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LHeaderValid, LFooterValid{$ifndef CatchUseOfFreedInterfaces}, LBlockUnmodified{$endif}: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
LHeaderCheckSum := CalculateHeaderCheckSum(APBlock);
|
|
|
|
|
LHeaderValid := LHeaderCheckSum = PFullDebugBlockHeader(APBlock).HeaderCheckSum;
|
|
|
|
|
{Is the footer itself still in place}
|
|
|
|
|
LFooterValid := LHeaderValid
|
|
|
|
|
and (PCardinal(Cardinal(APBlock) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(APBlock).UserSize)^ = (not LHeaderCheckSum));
|
|
|
|
|
{$ifndef CatchUseOfFreedInterfaces}
|
|
|
|
|
if LFooterValid then
|
|
|
|
|
begin
|
|
|
|
|
{Clear the old footer}
|
|
|
|
|
PCardinal(Cardinal(APBlock) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(APBlock).UserSize)^ := DebugFillDWord;
|
|
|
|
|
{Check that all the filler bytes are valid inside the block, except for the four byte "dummy" class header}
|
|
|
|
|
LBlockUnmodified := CheckFillPattern(PCardinal(Cardinal(APBlock) + SizeOf(TFullDebugBlockHeader) + 4),
|
|
|
|
|
ABlockSize - (BlockHeaderSize + FullDebugBlockOverhead));
|
|
|
|
|
{Reset the old footer}
|
|
|
|
|
PCardinal(Cardinal(APBlock) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(APBlock).UserSize)^ := not LHeaderCheckSum;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
LBlockUnmodified := False;
|
|
|
|
|
{$endif}
|
|
|
|
|
if (not LHeaderValid) or (not LFooterValid){$ifndef CatchUseOfFreedInterfaces}or (not LBlockUnmodified){$endif} then
|
|
|
|
|
begin
|
|
|
|
|
LogBlockError(APBlock, AOperation, LHeaderValid, LFooterValid);
|
|
|
|
|
Result := False;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
Result := True;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function DebugGetMem(ASize: Integer): Pointer;
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Scan the entire memory pool first?}
|
|
|
|
|
if FullDebugModeScanMemoryPoolBeforeEveryOperation then
|
|
|
|
|
ScanMemoryPoolForCorruptions;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{We need extra space for (a) The debug header, (b) the block debug trailer
|
|
|
|
|
and (c) the trailing block size pointer for free blocks}
|
|
|
|
|
Result := FastGetMem(ASize + FullDebugBlockOverhead);
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
|
|
|
|
if CheckFreeBlockUnmodified(Result, GetAvailableSpaceInBlock(Result) + 4, boGetMem) then
|
|
|
|
|
begin
|
|
|
|
|
{Set the allocation call stack}
|
|
|
|
|
GetStackTrace(@PFullDebugBlockHeader(Result).AllocationStackTrace, StackTraceDepth, 1);
|
|
|
|
|
{Block is now in use}
|
|
|
|
|
PFullDebugBlockHeader(Result).BlockInUse := True;
|
|
|
|
|
{Set the group number}
|
|
|
|
|
PFullDebugBlockHeader(Result).AllocationGroup := AllocationGroupStack[AllocationGroupStackTop];
|
|
|
|
|
{Set the allocation number}
|
|
|
|
|
Inc(CurrentAllocationNumber);
|
|
|
|
|
PFullDebugBlockHeader(Result).AllocationNumber := CurrentAllocationNumber;
|
|
|
|
|
{Clear the previous block trailer}
|
|
|
|
|
PCardinal(Cardinal(Result) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(Result).UserSize)^ := DebugFillDWord;
|
|
|
|
|
{Set the user size for the block}
|
|
|
|
|
PFullDebugBlockHeader(Result).UserSize := ASize;
|
|
|
|
|
{Set the checksums}
|
|
|
|
|
UpdateHeaderAndFooterCheckSums(Result);
|
|
|
|
|
{Return the start of the actual block}
|
|
|
|
|
Result := Pointer(Cardinal(Result) + SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function CheckBlockBeforeFreeOrRealloc(APointer: PFullDebugBlockHeader; AOperation: TBlockOperation): Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LHeaderValid, LFooterValid: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{Is the debug info surrounding the block valid?}
|
|
|
|
|
LHeaderValid := CalculateHeaderCheckSum(APointer) = APointer.HeaderCheckSum;
|
|
|
|
|
LFooterValid := LHeaderValid
|
|
|
|
|
and (APointer.HeaderCheckSum = (not PCardinal(Cardinal(APointer) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(APointer).UserSize)^));
|
|
|
|
|
if LHeaderValid and LFooterValid and APointer.BlockInUse then
|
|
|
|
|
begin
|
|
|
|
|
Result := True;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Log the error}
|
|
|
|
|
LogBlockError(APointer, AOperation, LHeaderValid, LFooterValid);
|
|
|
|
|
{Return an error}
|
|
|
|
|
Result := False;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function DebugFreeMem(APointer: Pointer): Integer;
|
|
|
|
|
var
|
|
|
|
|
LActualBlock: PFullDebugBlockHeader;
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Scan the entire memory pool first?}
|
|
|
|
|
if FullDebugModeScanMemoryPoolBeforeEveryOperation then
|
|
|
|
|
ScanMemoryPoolForCorruptions;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Get a pointer to the start of the actual block}
|
|
|
|
|
LActualBlock := PFullDebugBlockHeader(Cardinal(APointer)
|
|
|
|
|
- SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
{Is the debug info surrounding the block valid?}
|
|
|
|
|
if CheckBlockBeforeFreeOrRealloc(LActualBlock, boFreeMem) then
|
|
|
|
|
begin
|
|
|
|
|
{Get the class the block was used for}
|
|
|
|
|
LActualBlock.PreviouslyUsedByClass := PCardinal(APointer)^;
|
|
|
|
|
{Set the free call stack}
|
|
|
|
|
GetStackTrace(@LActualBlock.FreeStackTrace, StackTraceDepth, 1);
|
|
|
|
|
{Block is now free}
|
|
|
|
|
LActualBlock.BlockInUse := False;
|
|
|
|
|
{Clear the user area of the block}
|
|
|
|
|
FillDWord(APointer^, LActualBlock.UserSize,
|
|
|
|
|
{$ifndef CatchUseOfFreedInterfaces}DebugFillDWord{$else}Cardinal(@VMTBadInterface){$endif});
|
|
|
|
|
{Set a pointer to the dummy VMT}
|
|
|
|
|
PCardinal(APointer)^ := Cardinal(@FreedObjectVMT.VMTMethods[0]);
|
|
|
|
|
{Recalculate the checksums}
|
|
|
|
|
UpdateHeaderAndFooterCheckSums(LActualBlock);
|
|
|
|
|
{Free the actual block}
|
|
|
|
|
Result := FastFreeMem(LActualBlock);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result := -1;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{In debug mode we never do an in-place resize, data is always moved. This
|
|
|
|
|
increases the likelihood of catching memory overwrite bugs.}
|
|
|
|
|
function DebugReallocMem(APointer: Pointer; ANewSize: Integer): Pointer;
|
|
|
|
|
var
|
|
|
|
|
LMoveSize, LBlockSpace: Cardinal;
|
|
|
|
|
LActualBlock, LNewActualBlock: PFullDebugBlockHeader;
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Scan the entire memory pool first?}
|
|
|
|
|
if FullDebugModeScanMemoryPoolBeforeEveryOperation then
|
|
|
|
|
ScanMemoryPoolForCorruptions;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Get a pointer to the start of the actual block}
|
|
|
|
|
LActualBlock := PFullDebugBlockHeader(Cardinal(APointer)
|
|
|
|
|
- SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
{Is the debug info surrounding the block valid?}
|
|
|
|
|
if CheckBlockBeforeFreeOrRealloc(LActualBlock, boReallocMem) then
|
|
|
|
|
begin
|
|
|
|
|
{Get the current block size}
|
|
|
|
|
LBlockSpace := GetAvailableSpaceInBlock(LActualBlock);
|
|
|
|
|
{Can the block fit? We need space for the debug overhead and the block header
|
|
|
|
|
of the next block}
|
|
|
|
|
if LBlockSpace < (Cardinal(ANewSize) + FullDebugBlockOverhead) then
|
|
|
|
|
begin
|
|
|
|
|
{Get a new block of the requested size}
|
|
|
|
|
Result := DebugGetMem(ANewSize);
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
begin
|
|
|
|
|
{How many bytes to move?}
|
|
|
|
|
LMoveSize := LActualBlock.UserSize;
|
|
|
|
|
if LMoveSize > Cardinal(ANewSize) then
|
|
|
|
|
LMoveSize := ANewSize;
|
|
|
|
|
{Move the data across}
|
|
|
|
|
System.Move(APointer^, Result^, LMoveSize);
|
|
|
|
|
{Keep the old group and allocation numbers}
|
|
|
|
|
LNewActualBlock := PFullDebugBlockHeader(Cardinal(Result)
|
|
|
|
|
- SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
LNewActualBlock.AllocationGroup := LActualBlock.AllocationGroup;
|
|
|
|
|
LNewActualBlock.AllocationNumber := LActualBlock.AllocationNumber;
|
|
|
|
|
{This was not a new allocation number - decrement the allocation number
|
|
|
|
|
that was incremented in the DebugGetMem call}
|
|
|
|
|
Dec(CurrentAllocationNumber);
|
|
|
|
|
{Recalculate the header and footer checksums}
|
|
|
|
|
UpdateHeaderAndFooterCheckSums(LNewActualBlock);
|
|
|
|
|
{Free the old block}
|
|
|
|
|
DebugFreeMem(APointer);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Clear all data after the new end of the block up to the old end of the
|
|
|
|
|
block, including the trailer}
|
|
|
|
|
FillDWord(Pointer(Cardinal(APointer) + Cardinal(ANewSize) + 4)^,
|
|
|
|
|
Integer(LActualBlock.UserSize) - ANewSize,
|
|
|
|
|
{$ifndef CatchUseOfFreedInterfaces}DebugFillDWord{$else}Cardinal(@VMTBadInterface){$endif});
|
|
|
|
|
{Update the user size}
|
|
|
|
|
LActualBlock.UserSize := ANewSize;
|
|
|
|
|
{Set the new checksums}
|
|
|
|
|
UpdateHeaderAndFooterCheckSums(LActualBlock);
|
|
|
|
|
{Return the old pointer}
|
|
|
|
|
Result := APointer;
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result := nil;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Allocates a block and fills it with zeroes}
|
|
|
|
|
function DebugAllocMem(ASize: Cardinal): Pointer;
|
|
|
|
|
begin
|
|
|
|
|
Result := DebugGetMem(ASize);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Clear the block}
|
|
|
|
|
if Result <> nil then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
FillChar(Result^, ASize, 0);
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Raises a runtime error if a memory corruption was encountered.}
|
|
|
|
|
procedure RaiseMemoryCorruptionError;
|
|
|
|
|
begin
|
|
|
|
|
{Disable exhaustive checking in order to prevent recursive exceptions.}
|
|
|
|
|
FullDebugModeScanMemoryPoolBeforeEveryOperation := False;
|
|
|
|
|
{Raise the runtime error}
|
|
|
|
|
System.Error(reOutOfMemory);
|
|
|
|
|
end;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Subroutine for InternalScanMemoryPool: Checks the given small block pool for
|
|
|
|
|
allocated blocks}
|
|
|
|
|
procedure InternalScanSmallBlockPool(APSmallBlockPool: PSmallBlockPoolHeader;
|
|
|
|
|
AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
|
|
|
|
|
var
|
|
|
|
|
LCurPtr, LEndPtr: Pointer;
|
|
|
|
|
begin
|
|
|
|
|
{Get the first and last pointer for the pool}
|
|
|
|
|
GetFirstAndLastSmallBlockInPool(APSmallBlockPool, LCurPtr, LEndPtr);
|
|
|
|
|
{Step through all blocks}
|
|
|
|
|
while Cardinal(LCurPtr) <= Cardinal(LEndPtr) do
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Is this block in use? If so, is the debug info intact?}
|
|
|
|
|
if ((PCardinal(Cardinal(LCurPtr) - 4)^ and IsFreeBlockFlag) = 0) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if CheckBlockBeforeFreeOrRealloc(LCurPtr, boBlockCheck) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if (PFullDebugBlockHeader(LCurPtr).AllocationGroup >= AFirstAllocationGroupToLog)
|
2006-04-15 03:44:05 -04:00
|
|
|
|
and (PFullDebugBlockHeader(LCurPtr).AllocationGroup <= ALastAllocationGroupToLog) then
|
|
|
|
|
begin
|
|
|
|
|
LogMemoryLeakOrAllocatedBlock(LCurPtr, False);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
2008-07-25 09:23:00 -04:00
|
|
|
|
RaiseMemoryCorruptionError;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Check that the block has not been modified since being freed}
|
|
|
|
|
if not CheckFreeBlockUnmodified(LCurPtr, APSmallBlockPool.BlockType.BlockSize, boBlockCheck) then
|
|
|
|
|
RaiseMemoryCorruptionError;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Next block}
|
|
|
|
|
Inc(Cardinal(LCurPtr), APSmallBlockPool.BlockType.BlockSize);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
end;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Subroutine for LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions:
|
|
|
|
|
Scans the memory pool for corruptions and optionally logs allocated blocks
|
|
|
|
|
in the allocation group range.}
|
|
|
|
|
procedure InternalScanMemoryPool(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
|
|
|
|
|
var
|
|
|
|
|
LPLargeBlock: PLargeBlockHeader;
|
|
|
|
|
LPMediumBlock: Pointer;
|
|
|
|
|
LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
LMediumBlockHeader: Cardinal;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{Step through all the medium block pools}
|
|
|
|
|
LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
|
|
|
|
|
while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
|
|
|
|
|
begin
|
|
|
|
|
LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
|
|
|
|
|
while LPMediumBlock <> nil do
|
|
|
|
|
begin
|
|
|
|
|
LMediumBlockHeader := PCardinal(Cardinal(LPMediumBlock) - 4)^;
|
|
|
|
|
{Is the block in use?}
|
|
|
|
|
if LMediumBlockHeader and IsFreeBlockFlag = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Block is in use: Is it a medium block or small block pool?}
|
|
|
|
|
if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get all the leaks for the small block pool}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
InternalScanSmallBlockPool(LPMediumBlock, AFirstAllocationGroupToLog, ALastAllocationGroupToLog);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if CheckBlockBeforeFreeOrRealloc(LPMediumBlock, boBlockCheck) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if (PFullDebugBlockHeader(LPMediumBlock).AllocationGroup >= AFirstAllocationGroupToLog)
|
|
|
|
|
and (PFullDebugBlockHeader(LPMediumBlock).AllocationGroup <= ALastAllocationGroupToLog) then
|
|
|
|
|
begin
|
|
|
|
|
LogMemoryLeakOrAllocatedBlock(LPMediumBlock, False);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
RaiseMemoryCorruptionError;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Check that the block has not been modified since being freed}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if not CheckFreeBlockUnmodified(LPMediumBlock, LMediumBlockHeader and DropMediumAndLargeFlagsMask, boBlockCheck) then
|
|
|
|
|
RaiseMemoryCorruptionError;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Next medium block}
|
|
|
|
|
LPMediumBlock := NextMediumBlock(LPMediumBlock);
|
|
|
|
|
end;
|
|
|
|
|
{Get the next medium block pool}
|
|
|
|
|
LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
|
|
|
|
|
end;
|
|
|
|
|
{Scan large blocks}
|
|
|
|
|
LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
|
|
|
|
|
while (LPLargeBlock <> @LargeBlocksCircularList) do
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if CheckBlockBeforeFreeOrRealloc(Pointer(Cardinal(LPLargeBlock) + LargeBlockHeaderSize), boBlockCheck) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if (PFullDebugBlockHeader(Cardinal(LPLargeBlock) + LargeBlockHeaderSize).AllocationGroup >= AFirstAllocationGroupToLog)
|
|
|
|
|
and (PFullDebugBlockHeader(Cardinal(LPLargeBlock) + LargeBlockHeaderSize).AllocationGroup <= ALastAllocationGroupToLog) then
|
|
|
|
|
begin
|
|
|
|
|
LogMemoryLeakOrAllocatedBlock(Pointer(Cardinal(LPLargeBlock) + LargeBlockHeaderSize), False);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
RaiseMemoryCorruptionError;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Get the next large block}
|
|
|
|
|
LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Logs detail about currently allocated memory blocks for the specified range of
|
|
|
|
|
allocation groups. if ALastAllocationGroupToLog is less than
|
|
|
|
|
AFirstAllocationGroupToLog or it is zero, then all allocation groups are
|
|
|
|
|
logged. This routine also checks the memory pool for consistency at the same
|
|
|
|
|
time, raising an "Out of Memory" error if the check fails.}
|
|
|
|
|
procedure LogAllocatedBlocksToFile(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
|
|
|
|
|
begin
|
|
|
|
|
{Validate input}
|
|
|
|
|
if (ALastAllocationGroupToLog = 0) or (ALastAllocationGroupToLog < AFirstAllocationGroupToLog) then
|
|
|
|
|
begin
|
|
|
|
|
{Bad input: log all groups}
|
|
|
|
|
AFirstAllocationGroupToLog := 0;
|
|
|
|
|
ALastAllocationGroupToLog := $ffffffff;
|
|
|
|
|
end;
|
|
|
|
|
{Scan the memory pool, logging allocated blocks in the requested range.}
|
|
|
|
|
InternalScanMemoryPool(AFirstAllocationGroupToLog, ALastAllocationGroupToLog);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Scans the memory pool for any corruptions. If a corruption is encountered an "Out of Memory" exception is
|
|
|
|
|
raised.}
|
|
|
|
|
procedure ScanMemoryPoolForCorruptions;
|
|
|
|
|
begin
|
|
|
|
|
{Scan the memory pool for corruptions, but don't log any allocated blocks}
|
|
|
|
|
InternalScanMemoryPool($ffffffff, 0);
|
|
|
|
|
end;
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{-----------------------Invalid Virtual Method Calls-------------------------}
|
|
|
|
|
|
|
|
|
|
{ TFreedObject }
|
|
|
|
|
|
|
|
|
|
{Used to determine the index of the virtual method call on the freed object.
|
|
|
|
|
Do not change this without updating MaxFakeVMTEntries. Currently 200.}
|
|
|
|
|
procedure TFreedObject.GetVirtualMethodIndex;
|
|
|
|
|
asm
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
|
|
|
|
|
|
|
|
|
|
jmp TFreedObject.VirtualMethodError
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure TFreedObject.VirtualMethodError;
|
|
|
|
|
var
|
|
|
|
|
LVMOffset: Integer;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMsgPtr: PAnsiChar;
|
|
|
|
|
LErrorMessage: array[0..32767] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifndef NoMessageBoxes}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LErrorMessageTitle: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
LClass: TClass;
|
|
|
|
|
LActualBlock: PFullDebugBlockHeader;
|
|
|
|
|
begin
|
|
|
|
|
{Get the offset of the virtual method}
|
|
|
|
|
LVMOffset := (MaxFakeVMTEntries - VMIndex) * 4 + vmtParent + 4;
|
|
|
|
|
{Reset the index for the next error}
|
|
|
|
|
VMIndex := 0;
|
|
|
|
|
{Get the address of the actual block}
|
|
|
|
|
LActualBlock := PFullDebugBlockHeader(Cardinal(Self) - SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
{Display the error header}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(VirtualMethodErrorHeader, @LErrorMessage[0], Length(VirtualMethodErrorHeader));
|
|
|
|
|
{Is the debug info surrounding the block valid?}
|
|
|
|
|
if CalculateHeaderCheckSum(LActualBlock) = LActualBlock.HeaderCheckSum then
|
|
|
|
|
begin
|
|
|
|
|
{Get the class this block was used for previously}
|
|
|
|
|
LClass := GetObjectClass(@LActualBlock.PreviouslyUsedByClass);
|
|
|
|
|
if (LClass <> nil) and (Cardinal(LClass) <> Cardinal(@FreedObjectVMT.VMTMethods[0])) then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(FreedObjectClassMsg, LMsgPtr, Length(FreedObjectClassMsg));
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Get the virtual method name}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(VirtualMethodName, LMsgPtr, Length(VirtualMethodName));
|
|
|
|
|
if LVMOffset < 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(StandardVirtualMethodNames[LVMOffset div 4], LMsgPtr, Length(StandardVirtualMethodNames[LVMOffset div 4]));
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(VirtualMethodOffset, LMsgPtr, Length(VirtualMethodOffset));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LVMOffset, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Virtual method address}
|
|
|
|
|
if (LClass <> nil) and (Cardinal(LClass) <> Cardinal(@FreedObjectVMT.VMTMethods[0])) then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(VirtualMethodAddress, LMsgPtr, Length(VirtualMethodAddress));
|
|
|
|
|
LMsgPtr := CardinalToHexBuf(PCardinal(Integer(LClass) + LVMOffset)^, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Log the allocation group}
|
|
|
|
|
if LActualBlock.AllocationGroup > 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(PreviousAllocationGroupMsg, LMsgPtr, Length(PreviousAllocationGroupMsg));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LActualBlock.AllocationGroup, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Log the allocation number}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(PreviousAllocationNumberMsg, LMsgPtr, Length(PreviousAllocationNumberMsg));
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LActualBlock.AllocationNumber, LMsgPtr);
|
|
|
|
|
{The header is still intact - display info about the this/previous allocation}
|
|
|
|
|
if LActualBlock.AllocationStackTrace[0] <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(StackTraceAtObjectAllocMsg, LMsgPtr, Length(StackTraceAtObjectAllocMsg));
|
|
|
|
|
LMsgPtr := LogStackTrace(@LActualBlock.AllocationStackTrace, StackTraceDepth, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Get the call stack for the previous free}
|
|
|
|
|
if LActualBlock.FreeStackTrace[0] <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(StackTraceAtObjectFreeMsg, LMsgPtr, Length(StackTraceAtObjectFreeMsg));
|
|
|
|
|
LMsgPtr := LogStackTrace(@LActualBlock.FreeStackTrace, StackTraceDepth, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Header has been corrupted}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedNoHistoryMsg, LMsgPtr, Length(BlockHeaderCorruptedNoHistoryMsg));
|
|
|
|
|
end;
|
|
|
|
|
{Add the current stack trace}
|
|
|
|
|
LMsgPtr := LogCurrentStackTrace(2, LMsgPtr);
|
|
|
|
|
{Add the pointer address}
|
|
|
|
|
LMsgPtr := LogMemoryDump(LActualBlock, LMsgPtr);
|
|
|
|
|
{Trailing CRLF}
|
|
|
|
|
LMsgPtr^ := #13;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := #10;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
{Trailing #0}
|
|
|
|
|
LMsgPtr^ := #0;
|
|
|
|
|
{$ifdef LogErrorsToFile}
|
|
|
|
|
{Log the error}
|
|
|
|
|
AppendEventLog(@LErrorMessage[0], Cardinal(LMsgPtr) - Cardinal(@LErrorMessage[0]));
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(LErrorMessage);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
{Show the message}
|
|
|
|
|
AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(LErrorMessage, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{Raise an access violation}
|
|
|
|
|
RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nil);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{$ifdef CatchUseOfFreedInterfaces}
|
|
|
|
|
procedure TFreedObject.InterfaceError;
|
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMsgPtr: PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifndef NoMessageBoxes}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LErrorMessageTitle: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LErrorMessage: array[0..4000] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{Display the error header}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(InterfaceErrorHeader, @LErrorMessage[0], Length(InterfaceErrorHeader));
|
|
|
|
|
{Add the current stack trace}
|
|
|
|
|
LMsgPtr := LogCurrentStackTrace(2, LMsgPtr);
|
|
|
|
|
{Trailing CRLF}
|
|
|
|
|
LMsgPtr^ := #13;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := #10;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
{Trailing #0}
|
|
|
|
|
LMsgPtr^ := #0;
|
|
|
|
|
{$ifdef LogErrorsToFile}
|
|
|
|
|
{Log the error}
|
|
|
|
|
AppendEventLog(@LErrorMessage[0], Cardinal(LMsgPtr) - Cardinal(@LErrorMessage[0]));
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(LErrorMessage);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
{Show the message}
|
|
|
|
|
AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(LErrorMessage, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{Raise an access violation}
|
|
|
|
|
RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nil);
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{----------------------------Memory Leak Checking-----------------------------}
|
|
|
|
|
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
|
|
|
|
|
{Adds a leak to the specified list}
|
|
|
|
|
function UpdateExpectedLeakList(APLeakList: PPExpectedMemoryLeak;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
APNewEntry: PExpectedMemoryLeak; AExactSizeMatch: Boolean = True): Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LPInsertAfter, LPNewEntry: PExpectedMemoryLeak;
|
|
|
|
|
begin
|
|
|
|
|
{Default to error}
|
|
|
|
|
Result := False;
|
|
|
|
|
{Find the insertion spot}
|
|
|
|
|
LPInsertAfter := APLeakList^;
|
|
|
|
|
while (LPInsertAfter <> nil) do
|
|
|
|
|
begin
|
|
|
|
|
{Too big?}
|
|
|
|
|
if (LPInsertAfter.LeakSize > APNewEntry.LeakSize) then
|
|
|
|
|
begin
|
|
|
|
|
LPInsertAfter := LPInsertAfter.PreviousLeak;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Find a matching entry. If an exact size match is not required and the leak
|
|
|
|
|
is larger than the current entry, use it if the expected size of the next
|
|
|
|
|
entry is too large.}
|
|
|
|
|
if (Cardinal(LPInsertAfter.LeakAddress) = Cardinal(APNewEntry.LeakAddress))
|
2008-07-25 09:23:00 -04:00
|
|
|
|
and ((Cardinal(LPInsertAfter.LeakedClass) = Cardinal(APNewEntry.LeakedClass))
|
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
or (LPInsertAfter.LeakedCppTypeIdPtr = APNewEntry.LeakedCppTypeIdPtr)
|
|
|
|
|
{$endif}
|
|
|
|
|
)
|
2006-04-15 03:44:05 -04:00
|
|
|
|
and ((LPInsertAfter.LeakSize = APNewEntry.LeakSize)
|
|
|
|
|
or ((not AExactSizeMatch)
|
|
|
|
|
and (LPInsertAfter.LeakSize < APNewEntry.LeakSize)
|
|
|
|
|
and ((LPInsertAfter.NextLeak = nil)
|
|
|
|
|
or (LPInsertAfter.NextLeak.LeakSize > APNewEntry.LeakSize))
|
|
|
|
|
)) then
|
|
|
|
|
begin
|
|
|
|
|
if Integer(LPInsertAfter.LeakCount + APNewEntry.LeakCount) >= 0 then
|
|
|
|
|
begin
|
|
|
|
|
Inc(LPInsertAfter.LeakCount, APNewEntry.LeakCount);
|
|
|
|
|
{Is the count now 0?}
|
|
|
|
|
if LPInsertAfter.LeakCount = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Delete the entry}
|
|
|
|
|
if LPInsertAfter.NextLeak <> nil then
|
|
|
|
|
LPInsertAfter.NextLeak.PreviousLeak := LPInsertAfter.PreviousLeak;
|
|
|
|
|
if LPInsertAfter.PreviousLeak <> nil then
|
|
|
|
|
LPInsertAfter.PreviousLeak.NextLeak := LPInsertAfter.NextLeak
|
|
|
|
|
else
|
|
|
|
|
APLeakList^ := LPInsertAfter.NextLeak;
|
|
|
|
|
{Insert it as the first free slot}
|
|
|
|
|
LPInsertAfter.NextLeak := ExpectedMemoryLeaks.FirstFreeSlot;
|
|
|
|
|
ExpectedMemoryLeaks.FirstFreeSlot := LPInsertAfter;
|
|
|
|
|
end;
|
|
|
|
|
Result := True;
|
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Next entry}
|
|
|
|
|
if LPInsertAfter.NextLeak <> nil then
|
|
|
|
|
LPInsertAfter := LPInsertAfter.NextLeak
|
|
|
|
|
else
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
if APNewEntry.LeakCount > 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get a position for the entry}
|
|
|
|
|
LPNewEntry := ExpectedMemoryLeaks.FirstFreeSlot;
|
|
|
|
|
if LPNewEntry <> nil then
|
|
|
|
|
begin
|
|
|
|
|
ExpectedMemoryLeaks.FirstFreeSlot := LPNewEntry.NextLeak;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
if (ExpectedMemoryLeaks.EntriesUsed < length(ExpectedMemoryLeaks.ExpectedLeaks)) then
|
|
|
|
|
begin
|
|
|
|
|
LPNewEntry := @ExpectedMemoryLeaks.ExpectedLeaks[ExpectedMemoryLeaks.EntriesUsed];
|
|
|
|
|
Inc(ExpectedMemoryLeaks.EntriesUsed);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{No more space}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{Set the entry}
|
|
|
|
|
LPNewEntry^ := APNewEntry^;
|
|
|
|
|
{Insert it into the list}
|
|
|
|
|
LPNewEntry.PreviousLeak := LPInsertAfter;
|
|
|
|
|
if LPInsertAfter <> nil then
|
|
|
|
|
begin
|
|
|
|
|
LPNewEntry.NextLeak := LPInsertAfter.NextLeak;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if LPNewEntry.NextLeak <> nil then
|
|
|
|
|
LPNewEntry.NextLeak.PreviousLeak := LPNewEntry;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LPInsertAfter.NextLeak := LPNewEntry;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
LPNewEntry.NextLeak := APLeakList^;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if LPNewEntry.NextLeak <> nil then
|
|
|
|
|
LPNewEntry.NextLeak.PreviousLeak := LPNewEntry;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
APLeakList^ := LPNewEntry;
|
|
|
|
|
end;
|
|
|
|
|
Result := True;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Locks the expected leaks. Returns false if the list could not be allocated.}
|
|
|
|
|
function LockExpectedMemoryLeaksList: Boolean;
|
|
|
|
|
begin
|
|
|
|
|
{Lock the expected leaks list}
|
|
|
|
|
{$ifndef AssumeMultiThreaded}
|
|
|
|
|
if IsMultiThread then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
while LockCmpxchg(0, 1, @ExpectedMemoryLeaksListLocked) <> 0 do
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifndef NeverSleepOnThreadContention}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(InitialSleepTime);
|
|
|
|
|
if LockCmpxchg(0, 1, @ExpectedMemoryLeaksListLocked) = 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Sleep(AdditionalSleepTime);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{Allocate the list if it does not exist}
|
|
|
|
|
if ExpectedMemoryLeaks = nil then
|
|
|
|
|
ExpectedMemoryLeaks := VirtualAlloc(nil, ExpectedMemoryLeaksListSize, MEM_COMMIT, PAGE_READWRITE);
|
|
|
|
|
{Done}
|
|
|
|
|
Result := ExpectedMemoryLeaks <> nil;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Registers expected memory leaks. Returns true on success. The list of leaked
|
|
|
|
|
blocks is limited, so failure is possible if the list is full.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LNewEntry: TExpectedMemoryLeak;
|
|
|
|
|
begin
|
|
|
|
|
{Fill out the structure}
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
LNewEntry.LeakAddress := ALeakedPointer;
|
|
|
|
|
{$else}
|
|
|
|
|
LNewEntry.LeakAddress := Pointer(Cardinal(ALeakedPointer) - SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
{$endif}
|
|
|
|
|
LNewEntry.LeakedClass := nil;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LNewEntry.LeakedCppTypeIdPtr := nil;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LNewEntry.LeakSize := 0;
|
|
|
|
|
LNewEntry.LeakCount := 1;
|
|
|
|
|
{Add it to the correct list}
|
|
|
|
|
Result := LockExpectedMemoryLeaksList
|
|
|
|
|
and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LNewEntry);
|
|
|
|
|
ExpectedMemoryLeaksListLocked := False;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LNewEntry: TExpectedMemoryLeak;
|
|
|
|
|
begin
|
|
|
|
|
{Fill out the structure}
|
|
|
|
|
LNewEntry.LeakAddress := nil;
|
|
|
|
|
LNewEntry.LeakedClass := ALeakedObjectClass;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LNewEntry.LeakedCppTypeIdPtr := nil;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LNewEntry.LeakSize := ALeakedObjectClass.InstanceSize;
|
|
|
|
|
LNewEntry.LeakCount := ACount;
|
|
|
|
|
{Add it to the correct list}
|
|
|
|
|
Result := LockExpectedMemoryLeaksList
|
|
|
|
|
and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LNewEntry);
|
|
|
|
|
ExpectedMemoryLeaksListLocked := False;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
function RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer = 1): Boolean; overload;
|
|
|
|
|
var
|
|
|
|
|
LNewEntry: TExpectedMemoryLeak;
|
|
|
|
|
begin
|
|
|
|
|
{Fill out the structure}
|
|
|
|
|
if Assigned(GetCppVirtObjSizeByTypeIdPtrFunc) then
|
|
|
|
|
begin
|
|
|
|
|
//Return 0 if not a proper type
|
|
|
|
|
LNewEntry.LeakSize := GetCppVirtObjSizeByTypeIdPtrFunc(ALeakedCppVirtObjTypeIdPtr);
|
|
|
|
|
if LNewEntry.LeakSize > 0 then
|
|
|
|
|
begin
|
|
|
|
|
LNewEntry.LeakAddress := nil;
|
|
|
|
|
LNewEntry.LeakedClass := nil;
|
|
|
|
|
LNewEntry.LeakedCppTypeIdPtr := ALeakedCppVirtObjTypeIdPtr;
|
|
|
|
|
LNewEntry.LeakCount := ACount;
|
|
|
|
|
{Add it to the correct list}
|
|
|
|
|
Result := LockExpectedMemoryLeaksList
|
|
|
|
|
and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LNewEntry);
|
|
|
|
|
ExpectedMemoryLeaksListLocked := False;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result := False;
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
Result := False;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
function RegisterExpectedMemoryLeak(ALeakedBlockSize: Integer; ACount: Integer = 1): Boolean; overload;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LNewEntry: TExpectedMemoryLeak;
|
|
|
|
|
begin
|
|
|
|
|
{Fill out the structure}
|
|
|
|
|
LNewEntry.LeakAddress := nil;
|
|
|
|
|
LNewEntry.LeakedClass := nil;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LNewEntry.LeakedCppTypeIdPtr := nil;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LNewEntry.LeakSize := ALeakedBlockSize;
|
|
|
|
|
LNewEntry.LeakCount := ACount;
|
|
|
|
|
{Add it to the correct list}
|
|
|
|
|
Result := LockExpectedMemoryLeaksList
|
|
|
|
|
and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryBySizeOnly, @LNewEntry);
|
|
|
|
|
ExpectedMemoryLeaksListLocked := False;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
LNewEntry: TExpectedMemoryLeak;
|
|
|
|
|
begin
|
|
|
|
|
{Fill out the structure}
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
LNewEntry.LeakAddress := ALeakedPointer;
|
|
|
|
|
{$else}
|
|
|
|
|
LNewEntry.LeakAddress := Pointer(Cardinal(ALeakedPointer) - SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
{$endif}
|
|
|
|
|
LNewEntry.LeakedClass := nil;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LNewEntry.LeakedCppTypeIdPtr := nil;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LNewEntry.LeakSize := 0;
|
|
|
|
|
LNewEntry.LeakCount := -1;
|
|
|
|
|
{Remove it from the list}
|
|
|
|
|
Result := LockExpectedMemoryLeaksList
|
|
|
|
|
and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LNewEntry);
|
|
|
|
|
ExpectedMemoryLeaksListLocked := False;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
Result := RegisterExpectedMemoryLeak(ALeakedObjectClass, - ACount);
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
function UnregisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer = 1): Boolean; overload;
|
|
|
|
|
begin
|
|
|
|
|
Result := RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr, - ACount);
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
function UnregisterExpectedMemoryLeak(ALeakedBlockSize: Integer; ACount: Integer = 1): Boolean; overload;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
Result := RegisterExpectedMemoryLeak(ALeakedBlockSize, - ACount);
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Returns a list of all expected memory leaks}
|
|
|
|
|
function GetRegisteredMemoryLeaks: TRegisteredMemoryLeaks;
|
|
|
|
|
|
|
|
|
|
procedure AddEntries(AEntry: PExpectedMemoryLeak);
|
|
|
|
|
var
|
|
|
|
|
LInd: integer;
|
|
|
|
|
begin
|
|
|
|
|
while AEntry <> nil do
|
|
|
|
|
begin
|
|
|
|
|
LInd := length(Result);
|
|
|
|
|
SetLength(Result, LInd + 1);
|
|
|
|
|
{Add the entry}
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
Result[LInd].LeakAddress := AEntry.LeakAddress;
|
|
|
|
|
{$else}
|
|
|
|
|
Result[LInd].LeakAddress := Pointer(Cardinal(AEntry.LeakAddress) + SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
{$endif}
|
|
|
|
|
Result[LInd].LeakedClass := AEntry.LeakedClass;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
Result[LInd].LeakedCppTypeIdPtr := AEntry.LeakedCppTypeIdPtr;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
Result[LInd].LeakSize := AEntry.LeakSize;
|
|
|
|
|
Result[LInd].LeakCount := AEntry.LeakCount;
|
|
|
|
|
{Next entry}
|
|
|
|
|
AEntry := AEntry.NextLeak;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
SetLength(Result, 0);
|
|
|
|
|
if (ExpectedMemoryLeaks <> nil) and LockExpectedMemoryLeaksList then
|
|
|
|
|
begin
|
|
|
|
|
{Add all entries}
|
|
|
|
|
AddEntries(ExpectedMemoryLeaks.FirstEntryByAddress);
|
|
|
|
|
AddEntries(ExpectedMemoryLeaks.FirstEntryByClass);
|
|
|
|
|
AddEntries(ExpectedMemoryLeaks.FirstEntryBySizeOnly);
|
|
|
|
|
{Unlock the list}
|
|
|
|
|
ExpectedMemoryLeaksListLocked := False;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$else}
|
|
|
|
|
{$ifdef BDS2006AndUp}
|
|
|
|
|
function NoOpRegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean;
|
|
|
|
|
begin
|
|
|
|
|
{Do nothing. Used when memory leak reporting is disabled under Delphi 2006 and later.}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
function NoOpUnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean;
|
|
|
|
|
begin
|
|
|
|
|
{Do nothing. Used when memory leak reporting is disabled under Delphi 2006 and later.}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Checks blocks for modification after free and also for memory
|
|
|
|
|
leaks}
|
|
|
|
|
procedure CheckBlocksOnShutdown(ACheckForLeakedBlocks: Boolean);
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
type
|
|
|
|
|
{Leaked class type}
|
|
|
|
|
TLeakedClass = packed record
|
|
|
|
|
ClassPointer: TClass;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
CppTypeIdPtr: Pointer;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
NumLeaks: Cardinal;
|
|
|
|
|
end;
|
|
|
|
|
TLeakedClasses = array[0..255] of TLeakedClass;
|
|
|
|
|
PLeakedClasses = ^TLeakedClasses;
|
|
|
|
|
{Leak statistics for a small block type}
|
|
|
|
|
TSmallBlockLeaks = array[0..NumSmallBlockTypes - 1] of TLeakedClasses;
|
|
|
|
|
{A leaked medium or large block}
|
|
|
|
|
TMediumAndLargeBlockLeaks = array[0..4095] of Cardinal;
|
|
|
|
|
{$endif}
|
|
|
|
|
var
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
{The leaked classes for small blocks}
|
|
|
|
|
LSmallBlockLeaks: TSmallBlockLeaks;
|
|
|
|
|
LLeakType: TMemoryLeakType;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LLeakedCppTypeIdPtr: Pointer;
|
|
|
|
|
LCppTypeName: PAnsiChar;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LMediumAndLargeBlockLeaks: TMediumAndLargeBlockLeaks;
|
|
|
|
|
LNumMediumAndLargeLeaks: Integer;
|
|
|
|
|
LPLargeBlock: PLargeBlockHeader;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LLeakMessage: array[0..32767] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifndef NoMessageBoxes}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMessageTitleBuffer: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMsgPtr: PAnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LExpectedLeaksOnly, LSmallLeakHeaderAdded, LBlockSizeHeaderAdded: Boolean;
|
|
|
|
|
LBlockTypeInd, LMediumBlockSize, LLargeBlockSize,
|
|
|
|
|
LClassInd, LPreviousBlockSize, LThisBlockSize, LBlockInd: Cardinal;
|
|
|
|
|
{$endif}
|
|
|
|
|
LPMediumBlock: Pointer;
|
|
|
|
|
LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
LMediumBlockHeader: Cardinal;
|
|
|
|
|
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
{Tries to account for a memory leak. Returns true if the leak is expected and
|
|
|
|
|
removes the leak from the list}
|
|
|
|
|
function GetMemoryLeakType(AAddress: Pointer; ASpaceInsideBlock: Cardinal): TMemoryLeakType;
|
|
|
|
|
var
|
|
|
|
|
LLeak: TExpectedMemoryLeak;
|
|
|
|
|
begin
|
|
|
|
|
{Default to not found}
|
|
|
|
|
Result := mltUnexpectedLeak;
|
|
|
|
|
if ExpectedMemoryLeaks <> nil then
|
|
|
|
|
begin
|
|
|
|
|
{Check by pointer address}
|
|
|
|
|
LLeak.LeakAddress := AAddress;
|
|
|
|
|
LLeak.LeakedClass := nil;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LLeak.LeakedCppTypeIdPtr := nil;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LLeak.LeakSize := 0;
|
|
|
|
|
LLeak.LeakCount := -1;
|
|
|
|
|
if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LLeak, False) then
|
|
|
|
|
begin
|
|
|
|
|
Result := mltExpectedLeakRegisteredByPointer;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Check by class}
|
|
|
|
|
LLeak.LeakAddress := nil;
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
LLeak.LeakedClass := TClass(PCardinal(Cardinal(AAddress)+ SizeOf(TFullDebugBlockHeader))^);
|
|
|
|
|
{$else}
|
|
|
|
|
LLeak.LeakedClass := TClass(PCardinal(AAddress)^);
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
if Assigned(GetCppVirtObjTypeIdPtrFunc) then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
LLeak.LeakedCppTypeIdPtr := GetCppVirtObjTypeIdPtrFunc(Pointer(Cardinal(AAddress)
|
|
|
|
|
+ SizeOf(TFullDebugBlockHeader)), ASpaceInsideBlock);
|
|
|
|
|
{$else}
|
|
|
|
|
LLeak.LeakedCppTypeIdPtr := GetCppVirtObjTypeIdPtrFunc(AAddress, ASpaceInsideBlock);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
LLeakedCppTypeIdPtr := LLeak.LeakedCppTypeIdPtr;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LLeak.LeakSize := ASpaceInsideBlock;
|
|
|
|
|
if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LLeak, False) then
|
|
|
|
|
begin
|
|
|
|
|
Result := mltExpectedLeakRegisteredByClass;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Check by size: the block must be large enough to hold the leak}
|
|
|
|
|
LLeak.LeakedClass := nil;
|
|
|
|
|
if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryBySizeOnly, @LLeak, False) then
|
|
|
|
|
Result := mltExpectedLeakRegisteredBySize;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Checks the small block pool for leaks.}
|
|
|
|
|
procedure CheckSmallBlockPoolForLeaks(APSmallBlockPool: PSmallBlockPoolHeader);
|
|
|
|
|
var
|
|
|
|
|
LLeakedClass: TClass;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LLeakedCppObjectTypeId: Pointer;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LSmallBlockLeakType: TMemoryLeakType;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LCharInd, LClassIndex, LStringLength, LElemSize, LStringMemReq: Integer;
|
|
|
|
|
LPAnsiStr: PAnsiChar;
|
|
|
|
|
LPUniStr: PWideChar;
|
|
|
|
|
LPossibleString: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LCurPtr, LEndPtr, LDataPtr: Pointer;
|
|
|
|
|
LBlockTypeIndex: Cardinal;
|
|
|
|
|
LPLeakedClasses: PLeakedClasses;
|
|
|
|
|
LSmallBlockSize: Cardinal;
|
|
|
|
|
begin
|
|
|
|
|
{Get the useable size inside a block}
|
|
|
|
|
LSmallBlockSize := APSmallBlockPool.BlockType.BlockSize - BlockHeaderSize;
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
Dec(LSmallBlockSize, FullDebugBlockOverhead);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Get the block type index}
|
|
|
|
|
LBlockTypeIndex := (Cardinal(APSmallBlockPool.BlockType) - Cardinal(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
|
|
|
|
|
LPLeakedClasses := @LSmallBlockLeaks[LBlockTypeIndex];
|
|
|
|
|
{Get the first and last pointer for the pool}
|
|
|
|
|
GetFirstAndLastSmallBlockInPool(APSmallBlockPool, LCurPtr, LEndPtr);
|
|
|
|
|
{Step through all blocks}
|
|
|
|
|
while Cardinal(LCurPtr) <= Cardinal(LEndPtr) do
|
|
|
|
|
begin
|
|
|
|
|
{Is this block in use? If so, is the debug info intact?}
|
|
|
|
|
if ((PCardinal(Cardinal(LCurPtr) - 4)^ and IsFreeBlockFlag) = 0) then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
if CheckBlockBeforeFreeOrRealloc(LCurPtr, boBlockCheck) then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LLeakedCppTypeIdPtr := nil;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Get the leak type}
|
|
|
|
|
LSmallBlockLeakType := GetMemoryLeakType(LCurPtr, LSmallBlockSize);
|
|
|
|
|
{$ifdef LogMemoryLeakDetailToFile}
|
|
|
|
|
{$ifdef HideExpectedLeaksRegisteredByPointer}
|
|
|
|
|
if LSmallBlockLeakType <> mltExpectedLeakRegisteredByPointer then
|
|
|
|
|
{$endif}
|
|
|
|
|
LogMemoryLeakOrAllocatedBlock(LCurPtr, True);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Only expected leaks?}
|
|
|
|
|
LExpectedLeaksOnly := LExpectedLeaksOnly and (LSmallBlockLeakType <> mltUnexpectedLeak);
|
|
|
|
|
{$ifdef HideExpectedLeaksRegisteredByPointer}
|
|
|
|
|
if LSmallBlockLeakType <> mltExpectedLeakRegisteredByPointer then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{Get a pointer to the user data}
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
LDataPtr := LCurPtr;
|
|
|
|
|
{$else}
|
|
|
|
|
LDataPtr := Pointer(Cardinal(LCurPtr) + SizeOf(TFullDebugBlockHeader));
|
|
|
|
|
{$endif}
|
|
|
|
|
{Default to an unknown block}
|
|
|
|
|
LClassIndex := 0;
|
|
|
|
|
{Get the class contained by the block}
|
|
|
|
|
LLeakedClass := GetObjectClass(LDataPtr);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Not a Delphi class? -> is it perhaps a string or C++ object type?}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
if LLeakedClass = nil then
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
LLeakedCppObjectTypeId := LLeakedCppTypeIdPtr;
|
|
|
|
|
if (LLeakedCppObjectTypeId = nil) and (ExpectedMemoryLeaks = nil) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if Assigned(GetCppVirtObjTypeIdPtrFunc) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LLeakedCppObjectTypeId := GetCppVirtObjTypeIdPtrFunc(LDataPtr, LSmallBlockSize);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
if Assigned(LLeakedCppObjectTypeId) then
|
|
|
|
|
begin
|
|
|
|
|
LClassIndex := 3;
|
|
|
|
|
while LClassIndex <= High(TLeakedClasses) do
|
|
|
|
|
begin
|
|
|
|
|
if (Pointer(LPLeakedClasses[LClassIndex].CppTypeIdPtr) = LLeakedCppObjectTypeId)
|
|
|
|
|
or ((LPLeakedClasses[LClassIndex].CppTypeIdPtr = nil)
|
|
|
|
|
and (LPLeakedClasses[LClassIndex].ClassPointer = nil)) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Inc(LClassIndex);
|
|
|
|
|
end;
|
|
|
|
|
if LClassIndex <= High(TLeakedClasses) then
|
|
|
|
|
Pointer(LPLeakedClasses[LClassIndex].CppTypeIdPtr) := LLeakedCppObjectTypeId
|
|
|
|
|
else
|
|
|
|
|
LClassIndex := 0;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
{Reference count < 256}
|
|
|
|
|
if PStrRec(LDataPtr).refCnt < 256 then
|
|
|
|
|
begin
|
|
|
|
|
{Get the string length and element size}
|
|
|
|
|
LStringLength := PStrRec(LDataPtr).length;
|
|
|
|
|
{In anticipation of Tiburon: Will be 2 for UnicodeString}
|
|
|
|
|
LElemSize := 1;
|
|
|
|
|
{Valid element size?}
|
|
|
|
|
if (LElemSize = 1) or (LElemSize = 2) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Calculate the amount of memory required for the string}
|
|
|
|
|
LStringMemReq := (LStringLength + 1) * LElemSize + SizeOf(StrRec);
|
|
|
|
|
{Does the string fit?}
|
|
|
|
|
if (LStringLength > 0)
|
|
|
|
|
and (LStringMemReq <= (APSmallBlockPool.BlockType.BlockSize - (BlockHeaderSize {$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif}))) then
|
|
|
|
|
begin
|
|
|
|
|
{It is possibly a string}
|
|
|
|
|
LPossibleString := True;
|
|
|
|
|
{Check for no characters < #32. If there are, then it is
|
|
|
|
|
probably not a string.}
|
|
|
|
|
if LElemSize = 1 then
|
|
|
|
|
begin
|
|
|
|
|
{Check that all characters are >= #32}
|
|
|
|
|
LPAnsiStr := PAnsiChar(Cardinal(LDataPtr) + SizeOf(StrRec));
|
|
|
|
|
for LCharInd := 1 to LStringLength do
|
|
|
|
|
begin
|
|
|
|
|
LPossibleString := LPossibleString and (LPAnsiStr^ >= #32);
|
|
|
|
|
Inc(LPAnsiStr);
|
|
|
|
|
end;
|
|
|
|
|
{Must have a trailing #0}
|
|
|
|
|
if LPossibleString and (LPAnsiStr^ = #0) then
|
|
|
|
|
begin
|
|
|
|
|
LClassIndex := 1;
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Check that all characters are >= #32}
|
|
|
|
|
LPUniStr := PWideChar(Cardinal(LDataPtr) + SizeOf(StrRec));
|
|
|
|
|
for LCharInd := 1 to LStringLength do
|
|
|
|
|
begin
|
|
|
|
|
LPossibleString := LPossibleString and (LPUniStr^ >= #32);
|
|
|
|
|
Inc(LPUniStr);
|
|
|
|
|
end;
|
|
|
|
|
{Must have a trailing #0}
|
|
|
|
|
if LPossibleString and (LPUniStr^ = #0) then
|
|
|
|
|
begin
|
|
|
|
|
LClassIndex := 2;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LClassIndex := 3;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
while LClassIndex <= High(TLeakedClasses) do
|
|
|
|
|
begin
|
|
|
|
|
if (LPLeakedClasses[LClassIndex].ClassPointer = LLeakedClass)
|
2008-07-25 09:23:00 -04:00
|
|
|
|
or ((LPLeakedClasses[LClassIndex].ClassPointer = nil)
|
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
and (LPLeakedClasses[LClassIndex].CppTypeIdPtr = nil)
|
|
|
|
|
{$endif}
|
|
|
|
|
) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
Inc(LClassIndex);
|
|
|
|
|
end;
|
|
|
|
|
if LClassIndex <= High(TLeakedClasses) then
|
|
|
|
|
LPLeakedClasses[LClassIndex].ClassPointer := LLeakedClass
|
|
|
|
|
else
|
|
|
|
|
LClassIndex := 0;
|
|
|
|
|
end;
|
|
|
|
|
{Add to the number of leaks for the class}
|
|
|
|
|
Inc(LPLeakedClasses[LClassIndex].NumLeaks);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef CheckUseOfFreedBlocksOnShutdown}
|
|
|
|
|
{Check that the block has not been modified since being freed}
|
|
|
|
|
CheckFreeBlockUnmodified(LCurPtr, APSmallBlockPool.BlockType.BlockSize, boBlockCheck);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{Next block}
|
|
|
|
|
Inc(Cardinal(LCurPtr), APSmallBlockPool.BlockType.BlockSize);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
{Clear the leak arrays}
|
|
|
|
|
FillChar(LSmallBlockLeaks, SizeOf(LSmallBlockLeaks), 0);
|
|
|
|
|
FillChar(LMediumAndLargeBlockLeaks, SizeOf(LMediumAndLargeBlockLeaks), 0);
|
|
|
|
|
{Step through all the medium block pools}
|
|
|
|
|
LNumMediumAndLargeLeaks := 0;
|
|
|
|
|
{No unexpected leaks so far}
|
|
|
|
|
LExpectedLeaksOnly := True;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Step through all the medium block pools}
|
|
|
|
|
LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
|
|
|
|
|
while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
|
|
|
|
|
begin
|
|
|
|
|
LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
|
|
|
|
|
while LPMediumBlock <> nil do
|
|
|
|
|
begin
|
|
|
|
|
LMediumBlockHeader := PCardinal(Cardinal(LPMediumBlock) - 4)^;
|
|
|
|
|
{Is the block in use?}
|
|
|
|
|
if LMediumBlockHeader and IsFreeBlockFlag = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
if ACheckForLeakedBlocks then
|
|
|
|
|
begin
|
|
|
|
|
if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get all the leaks for the small block pool}
|
|
|
|
|
CheckSmallBlockPoolForLeaks(LPMediumBlock);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
if (LNumMediumAndLargeLeaks < length(LMediumAndLargeBlockLeaks))
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
and CheckBlockBeforeFreeOrRealloc(LPMediumBlock, boBlockCheck)
|
|
|
|
|
{$endif}
|
|
|
|
|
then
|
|
|
|
|
begin
|
|
|
|
|
LMediumBlockSize := (LMediumBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
Dec(LMediumBlockSize, FullDebugBlockOverhead);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Get the leak type}
|
|
|
|
|
LLeakType := GetMemoryLeakType(LPMediumBlock, LMediumBlockSize);
|
|
|
|
|
{Is it an expected leak?}
|
|
|
|
|
LExpectedLeaksOnly := LExpectedLeaksOnly and (LLeakType <> mltUnexpectedLeak);
|
|
|
|
|
{$ifdef LogMemoryLeakDetailToFile}
|
|
|
|
|
{$ifdef HideExpectedLeaksRegisteredByPointer}
|
|
|
|
|
if LLeakType <> mltExpectedLeakRegisteredByPointer then
|
|
|
|
|
{$endif}
|
|
|
|
|
LogMemoryLeakOrAllocatedBlock(LPMediumBlock, True);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef HideExpectedLeaksRegisteredByPointer}
|
|
|
|
|
if LLeakType <> mltExpectedLeakRegisteredByPointer then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{Add the leak to the list}
|
|
|
|
|
LMediumAndLargeBlockLeaks[LNumMediumAndLargeLeaks] := LMediumBlockSize;
|
|
|
|
|
Inc(LNumMediumAndLargeLeaks);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef CheckUseOfFreedBlocksOnShutdown}
|
|
|
|
|
{Check that the block has not been modified since being freed}
|
|
|
|
|
CheckFreeBlockUnmodified(LPMediumBlock, LMediumBlockHeader and DropMediumAndLargeFlagsMask, boBlockCheck);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{Next medium block}
|
|
|
|
|
LPMediumBlock := NextMediumBlock(LPMediumBlock);
|
|
|
|
|
end;
|
|
|
|
|
{Get the next medium block pool}
|
|
|
|
|
LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
|
|
|
|
|
end;
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
if ACheckForLeakedBlocks then
|
|
|
|
|
begin
|
|
|
|
|
{Get all leaked large blocks}
|
|
|
|
|
LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
|
|
|
|
|
while (LPLargeBlock <> @LargeBlocksCircularList) do
|
|
|
|
|
begin
|
|
|
|
|
if (LNumMediumAndLargeLeaks < length(LMediumAndLargeBlockLeaks))
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
and CheckBlockBeforeFreeOrRealloc(Pointer(Cardinal(LPLargeBlock) + LargeBlockHeaderSize), boBlockCheck)
|
|
|
|
|
{$endif}
|
|
|
|
|
then
|
|
|
|
|
begin
|
|
|
|
|
LLargeBlockSize := (LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask) - BlockHeaderSize - LargeBlockHeaderSize;
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
Dec(LLargeBlockSize, FullDebugBlockOverhead);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Get the leak type}
|
|
|
|
|
LLeakType := GetMemoryLeakType(Pointer(Cardinal(LPLargeBlock) + LargeBlockHeaderSize), LLargeBlockSize);
|
|
|
|
|
{Is it an expected leak?}
|
|
|
|
|
LExpectedLeaksOnly := LExpectedLeaksOnly and (LLeakType <> mltUnexpectedLeak);
|
|
|
|
|
{$ifdef LogMemoryLeakDetailToFile}
|
|
|
|
|
{$ifdef HideExpectedLeaksRegisteredByPointer}
|
|
|
|
|
if LLeakType <> mltExpectedLeakRegisteredByPointer then
|
|
|
|
|
{$endif}
|
|
|
|
|
LogMemoryLeakOrAllocatedBlock(Pointer(Cardinal(LPLargeBlock) + LargeBlockHeaderSize), True);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef HideExpectedLeaksRegisteredByPointer}
|
|
|
|
|
if LLeakType <> mltExpectedLeakRegisteredByPointer then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{Add the leak}
|
|
|
|
|
LMediumAndLargeBlockLeaks[LNumMediumAndLargeLeaks] := LLargeBlockSize;
|
|
|
|
|
Inc(LNumMediumAndLargeLeaks);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{Get the next large block}
|
|
|
|
|
LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
|
|
|
|
|
end;
|
|
|
|
|
{Display the leak message if required}
|
|
|
|
|
if not LExpectedLeaksOnly then
|
|
|
|
|
begin
|
|
|
|
|
{Small leak header has not been added}
|
|
|
|
|
LSmallLeakHeaderAdded := False;
|
|
|
|
|
LPreviousBlockSize := 0;
|
|
|
|
|
{Set up the leak message header so long}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(LeakMessageHeader, @LLeakMessage[0], length(LeakMessageHeader));
|
|
|
|
|
{Step through all the small block types}
|
|
|
|
|
for LBlockTypeInd := 0 to NumSmallBlockTypes - 1 do
|
|
|
|
|
begin
|
|
|
|
|
LThisBlockSize := SmallBlockTypes[LBlockTypeInd].BlockSize - BlockHeaderSize;
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
Dec(LThisBlockSize, FullDebugBlockOverhead);
|
|
|
|
|
if Integer(LThisBlockSize) < 0 then
|
|
|
|
|
LThisBlockSize := 0;
|
|
|
|
|
{$endif}
|
|
|
|
|
LBlockSizeHeaderAdded := False;
|
|
|
|
|
{Any leaks?}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
for LClassInd := High(LSmallBlockLeaks[LBlockTypeInd]) downto 0 do
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{Is there still space in the message buffer? Reserve space for the message
|
|
|
|
|
footer.}
|
|
|
|
|
if LMsgPtr > @LLeakMessage[high(LLeakMessage) - 2048] then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Check the count}
|
|
|
|
|
if LSmallBlockLeaks[LBlockTypeInd][LClassInd].NumLeaks > 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Need to add the header?}
|
|
|
|
|
if not LSmallLeakHeaderAdded then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(SmallLeakDetail, LMsgPtr, Length(SmallLeakDetail));
|
|
|
|
|
LSmallLeakHeaderAdded := True;
|
|
|
|
|
end;
|
|
|
|
|
{Need to add the size header?}
|
|
|
|
|
if not LBlockSizeHeaderAdded then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr^ := #13;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := #10;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LPreviousBlockSize + 1, LMsgPtr);
|
|
|
|
|
LMsgPtr^ := ' ';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := '-';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := ' ';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LThisBlockSize, LMsgPtr);
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(BytesMessage, LMsgPtr, Length(BytesMessage));
|
|
|
|
|
LBlockSizeHeaderAdded := True;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr^ := ',';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := ' ';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Show the count}
|
|
|
|
|
case LClassInd of
|
|
|
|
|
{Unknown}
|
|
|
|
|
0:
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(UnknownClassNameMsg, LMsgPtr, Length(UnknownClassNameMsg));
|
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{AnsiString}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
1:
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
|
|
|
|
|
end;
|
|
|
|
|
{UnicodeString}
|
|
|
|
|
2:
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Classes}
|
|
|
|
|
else
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
if LSmallBlockLeaks[LBlockTypeInd][LClassInd].CppTypeIdPtr <> nil then
|
|
|
|
|
begin
|
|
|
|
|
if Assigned(GetCppVirtObjTypeNameByTypeIdPtrFunc) then
|
|
|
|
|
begin
|
|
|
|
|
LCppTypeName := GetCppVirtObjTypeNameByTypeIdPtrFunc(LSmallBlockLeaks[LBlockTypeInd][LClassInd].CppTypeIdPtr);
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(LCppTypeName, LMsgPtr, StrLen(LCppTypeName));
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
LMsgPtr := AppendClassNameToBuffer(LSmallBlockLeaks[LBlockTypeInd][LClassInd].ClassPointer, LMsgPtr);
|
|
|
|
|
{$ifdef CheckCppObjectTypeEnabled}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{Add the count}
|
|
|
|
|
LMsgPtr^ := ' ';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := 'x';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := ' ';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LSmallBlockLeaks[LBlockTypeInd][LClassInd].NumLeaks, LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
LPreviousBlockSize := LThisBlockSize;
|
|
|
|
|
end;
|
|
|
|
|
{Add the medium/large block leak message}
|
|
|
|
|
if LNumMediumAndLargeLeaks > 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Any non-small leaks?}
|
|
|
|
|
if LSmallLeakHeaderAdded then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr^ := #13;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := #10;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := #13;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := #10;
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
{Add the medium/large block leak message}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(LargeLeakDetail, LMsgPtr, Length(LargeLeakDetail));
|
|
|
|
|
{List all the blocks}
|
|
|
|
|
for LBlockInd := 0 to LNumMediumAndLargeLeaks - 1 do
|
|
|
|
|
begin
|
|
|
|
|
if LBlockInd <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
LMsgPtr^ := ',';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
LMsgPtr^ := ' ';
|
|
|
|
|
Inc(LMsgPtr);
|
|
|
|
|
end;
|
|
|
|
|
LMsgPtr := CardinalToStrBuf(LMediumAndLargeBlockLeaks[LBlockInd], LMsgPtr);
|
|
|
|
|
{Is there still space in the message buffer? Reserve space for the
|
|
|
|
|
message footer.}
|
|
|
|
|
if LMsgPtr > @LLeakMessage[high(LLeakMessage) - 2048] then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Break;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$ifdef LogErrorsToFile}
|
|
|
|
|
{Set the message footer}
|
|
|
|
|
LMsgPtr := AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
|
|
|
|
|
{Append the message to the memory errors file}
|
|
|
|
|
AppendEventLog(@LLeakMessage[0], Cardinal(LMsgPtr) - Cardinal(@LLeakMessage[1]));
|
|
|
|
|
{$else}
|
|
|
|
|
{Set the message footer}
|
|
|
|
|
AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(LLeakMessage);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
{Show the message}
|
|
|
|
|
AppendStringToModuleName(LeakMessageTitle, LMessageTitleBuffer);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(LLeakMessage, LMessageTitleBuffer);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Returns statistics about the current state of the memory manager}
|
|
|
|
|
procedure GetMemoryManagerState(var AMemoryManagerState: TMemoryManagerState);
|
|
|
|
|
var
|
|
|
|
|
LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
LPMediumBlock: Pointer;
|
|
|
|
|
LInd: Integer;
|
|
|
|
|
LBlockTypeIndex, LMediumBlockSize, LMediumBlockHeader, LLargeBlockSize: Cardinal;
|
|
|
|
|
LPLargeBlock: PLargeBlockHeader;
|
|
|
|
|
begin
|
|
|
|
|
{Clear the structure}
|
|
|
|
|
FillChar(AMemoryManagerState, SizeOf(AMemoryManagerState), 0);
|
|
|
|
|
{Set the small block size stats}
|
|
|
|
|
for LInd := 0 to NumSmallBlockTypes - 1 do
|
|
|
|
|
begin
|
|
|
|
|
AMemoryManagerState.SmallBlockTypeStates[LInd].InternalBlockSize :=
|
|
|
|
|
SmallBlockTypes[LInd].BlockSize;
|
|
|
|
|
AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize :=
|
|
|
|
|
SmallBlockTypes[LInd].BlockSize - BlockHeaderSize{$ifdef FullDebugMode} - FullDebugBlockOverhead{$endif};
|
|
|
|
|
if Integer(AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize) < 0 then
|
|
|
|
|
AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize := 0;
|
|
|
|
|
end;
|
|
|
|
|
{Lock all small block types}
|
|
|
|
|
LockAllSmallBlockTypes;
|
|
|
|
|
{Lock the medium blocks}
|
|
|
|
|
LockMediumBlocks;
|
|
|
|
|
{Step through all the medium block pools}
|
|
|
|
|
LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
|
|
|
|
|
while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
|
|
|
|
|
begin
|
|
|
|
|
{Add to the medium block used space}
|
|
|
|
|
Inc(AMemoryManagerState.ReservedMediumBlockAddressSpace, MediumBlockPoolSize);
|
|
|
|
|
LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
|
|
|
|
|
while LPMediumBlock <> nil do
|
|
|
|
|
begin
|
|
|
|
|
LMediumBlockHeader := PCardinal(Cardinal(LPMediumBlock) - 4)^;
|
|
|
|
|
{Is the block in use?}
|
|
|
|
|
if LMediumBlockHeader and IsFreeBlockFlag = 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get the block size}
|
|
|
|
|
LMediumBlockSize := LMediumBlockHeader and DropMediumAndLargeFlagsMask;
|
|
|
|
|
if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get the block type index}
|
|
|
|
|
LBlockTypeIndex := (Cardinal(PSmallBlockPoolHeader(LPMediumBlock).BlockType) - Cardinal(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
|
|
|
|
|
{Subtract from medium block usage}
|
|
|
|
|
Dec(AMemoryManagerState.ReservedMediumBlockAddressSpace, LMediumBlockSize);
|
|
|
|
|
{Add it to the reserved space for the block size}
|
|
|
|
|
Inc(AMemoryManagerState.SmallBlockTypeStates[LBlockTypeIndex].ReservedAddressSpace, LMediumBlockSize);
|
|
|
|
|
{Add the usage for the pool}
|
|
|
|
|
Inc(AMemoryManagerState.SmallBlockTypeStates[LBlockTypeIndex].AllocatedBlockCount,
|
|
|
|
|
PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
Dec(LMediumBlockSize, FullDebugBlockOverhead);
|
|
|
|
|
{$endif}
|
|
|
|
|
Inc(AMemoryManagerState.AllocatedMediumBlockCount);
|
|
|
|
|
Inc(AMemoryManagerState.TotalAllocatedMediumBlockSize, LMediumBlockSize - BlockHeaderSize);
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{Next medium block}
|
|
|
|
|
LPMediumBlock := NextMediumBlock(LPMediumBlock);
|
|
|
|
|
end;
|
|
|
|
|
{Get the next medium block pool}
|
|
|
|
|
LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
|
|
|
|
|
end;
|
|
|
|
|
{Unlock medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{Unlock all the small block types}
|
|
|
|
|
for LInd := 0 to NumSmallBlockTypes - 1 do
|
|
|
|
|
SmallBlockTypes[LInd].BlockTypeLocked := False;
|
|
|
|
|
{Step through all the large blocks}
|
|
|
|
|
LockLargeBlocks;
|
|
|
|
|
LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
|
|
|
|
|
while (LPLargeBlock <> @LargeBlocksCircularList) do
|
|
|
|
|
begin
|
|
|
|
|
LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
|
|
|
|
|
Inc(AMemoryManagerState.AllocatedLargeBlockCount);
|
|
|
|
|
Inc(AMemoryManagerState.ReservedLargeBlockAddressSpace, LLargeBlockSize);
|
|
|
|
|
Inc(AMemoryManagerState.TotalAllocatedLargeBlockSize, LPLargeBlock.UserAllocatedSize);
|
|
|
|
|
{Get the next large block}
|
|
|
|
|
LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
|
|
|
|
|
end;
|
|
|
|
|
LargeBlocksLocked := False;
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Returns a summary of the information returned by GetMemoryManagerState}
|
|
|
|
|
procedure GetMemoryManagerUsageSummary(
|
|
|
|
|
var AMemoryManagerUsageSummary: TMemoryManagerUsageSummary);
|
|
|
|
|
var
|
|
|
|
|
LMMS: TMemoryManagerState;
|
|
|
|
|
LAllocatedBytes, LReservedBytes: Cardinal;
|
|
|
|
|
LSBTIndex: Integer;
|
|
|
|
|
begin
|
|
|
|
|
{Get the memory manager state}
|
|
|
|
|
GetMemoryManagerState(LMMS);
|
|
|
|
|
{Add up the totals}
|
|
|
|
|
LAllocatedBytes := LMMS.TotalAllocatedMediumBlockSize
|
|
|
|
|
+ LMMS.TotalAllocatedLargeBlockSize;
|
|
|
|
|
LReservedBytes := LMMS.ReservedMediumBlockAddressSpace
|
|
|
|
|
+ LMMS.ReservedLargeBlockAddressSpace;
|
|
|
|
|
for LSBTIndex := 0 to NumSmallBlockTypes - 1 do
|
|
|
|
|
begin
|
|
|
|
|
Inc(LAllocatedBytes, LMMS.SmallBlockTypeStates[LSBTIndex].UseableBlockSize
|
|
|
|
|
* LMMS.SmallBlockTypeStates[LSBTIndex].AllocatedBlockCount);
|
|
|
|
|
Inc(LReservedBytes, LMMS.SmallBlockTypeStates[LSBTIndex].ReservedAddressSpace);
|
|
|
|
|
end;
|
|
|
|
|
{Set the structure values}
|
|
|
|
|
AMemoryManagerUsageSummary.AllocatedBytes := LAllocatedBytes;
|
|
|
|
|
AMemoryManagerUsageSummary.OverheadBytes := LReservedBytes - LAllocatedBytes;
|
|
|
|
|
if LReservedBytes > 0 then
|
|
|
|
|
begin
|
|
|
|
|
AMemoryManagerUsageSummary.EfficiencyPercentage :=
|
|
|
|
|
LAllocatedBytes / LReservedBytes * 100;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
AMemoryManagerUsageSummary.EfficiencyPercentage := 100;
|
|
|
|
|
end;
|
|
|
|
|
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifndef Linux}
|
|
|
|
|
{Gets the state of every 64K block in the 4GB address space}
|
|
|
|
|
procedure GetMemoryMap(var AMemoryMap: TMemoryMap);
|
|
|
|
|
var
|
|
|
|
|
LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
LPLargeBlock: PLargeBlockHeader;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LLargeBlockSize, LChunkIndex, LInd, LNextChunk: Cardinal;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LMBI: TMemoryBasicInformation;
|
|
|
|
|
begin
|
|
|
|
|
{Clear the map}
|
|
|
|
|
FillChar(AMemoryMap, SizeOf(AMemoryMap), ord(csUnallocated));
|
|
|
|
|
{Step through all the medium block pools}
|
|
|
|
|
LockMediumBlocks;
|
|
|
|
|
LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
|
|
|
|
|
while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
|
|
|
|
|
begin
|
|
|
|
|
{Add to the medium block used space}
|
|
|
|
|
LChunkIndex := Cardinal(LPMediumBlockPoolHeader) shr 16;
|
|
|
|
|
for LInd := 0 to (MediumBlockPoolSize - 1) shr 16 do
|
|
|
|
|
AMemoryMap[LChunkIndex + LInd] := csAllocated;
|
|
|
|
|
{Get the next medium block pool}
|
|
|
|
|
LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
|
|
|
|
|
end;
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{Step through all the large blocks}
|
|
|
|
|
LockLargeBlocks;
|
|
|
|
|
LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
|
|
|
|
|
while (LPLargeBlock <> @LargeBlocksCircularList) do
|
|
|
|
|
begin
|
|
|
|
|
LChunkIndex := Cardinal(LPLargeBlock) shr 16;
|
|
|
|
|
LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
|
|
|
|
|
for LInd := 0 to (LLargeBlockSize - 1) shr 16 do
|
|
|
|
|
AMemoryMap[LChunkIndex + LInd] := csAllocated;
|
|
|
|
|
{Get the next large block}
|
|
|
|
|
LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
|
|
|
|
|
end;
|
|
|
|
|
LargeBlocksLocked := False;
|
|
|
|
|
{Fill in the rest of the map}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LInd := 0;
|
|
|
|
|
while LInd <= 65535 do
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{If the chunk is not allocated by this MM, what is its status?}
|
|
|
|
|
if AMemoryMap[LInd] = csUnallocated then
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Query the address space starting at the chunk boundary}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
VirtualQuery(Pointer(LInd * 65536), LMBI, SizeOf(LMBI));
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Get the chunk number after the region}
|
|
|
|
|
LNextChunk := (LMBI.RegionSize - 1) shr 16 + LInd + 1;
|
|
|
|
|
{Validate}
|
|
|
|
|
if LNextChunk > 65536 then
|
|
|
|
|
LNextChunk := 65536;
|
|
|
|
|
{Set the status of all the chunks in the region}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
if LMBI.State = MEM_COMMIT then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
begin
|
|
|
|
|
FillChar(AMemoryMap[LInd], LNextChunk - LInd, csSysAllocated);
|
|
|
|
|
end
|
2006-04-15 03:44:05 -04:00
|
|
|
|
else
|
2008-07-25 09:23:00 -04:00
|
|
|
|
begin
|
2006-04-15 03:44:05 -04:00
|
|
|
|
if LMBI.State = MEM_RESERVE then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
FillChar(AMemoryMap[LInd], LNextChunk - LInd, csSysReserved);
|
|
|
|
|
end;
|
|
|
|
|
{Point to the start of the next chunk}
|
|
|
|
|
LInd := LNextChunk;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Next chunk}
|
|
|
|
|
Inc(LInd);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
{Returns summarised information about the state of the memory manager. (For
|
|
|
|
|
backward compatibility.)}
|
|
|
|
|
function FastGetHeapStatus: THeapStatus;
|
|
|
|
|
var
|
|
|
|
|
LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
LPMediumBlock: Pointer;
|
|
|
|
|
LBlockTypeIndex, LMediumBlockSize, LMediumBlockHeader, LLargeBlockSize,
|
|
|
|
|
LSmallBlockUsage, LSmallBlockOverhead: Cardinal;
|
|
|
|
|
LInd: Integer;
|
|
|
|
|
LPLargeBlock: PLargeBlockHeader;
|
|
|
|
|
begin
|
|
|
|
|
{Clear the structure}
|
|
|
|
|
FillChar(Result, SizeOf(Result), 0);
|
|
|
|
|
{Lock all small block types}
|
|
|
|
|
LockAllSmallBlockTypes;
|
|
|
|
|
{Lock the medium blocks}
|
|
|
|
|
LockMediumBlocks;
|
|
|
|
|
{Step through all the medium block pools}
|
|
|
|
|
LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
|
|
|
|
|
while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
|
|
|
|
|
begin
|
|
|
|
|
{Add to the total and committed address space}
|
|
|
|
|
Inc(Result.TotalAddrSpace, ((MediumBlockPoolSize + $ffff) and $ffff0000));
|
|
|
|
|
Inc(Result.TotalCommitted, ((MediumBlockPoolSize + $ffff) and $ffff0000));
|
|
|
|
|
{Add the medium block pool overhead}
|
|
|
|
|
Inc(Result.Overhead, (((MediumBlockPoolSize + $ffff) and $ffff0000)
|
|
|
|
|
- MediumBlockPoolSize + MediumBlockPoolHeaderSize));
|
|
|
|
|
{Get the first medium block in the pool}
|
|
|
|
|
LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
|
|
|
|
|
while LPMediumBlock <> nil do
|
|
|
|
|
begin
|
|
|
|
|
{Get the block header}
|
|
|
|
|
LMediumBlockHeader := PCardinal(Cardinal(LPMediumBlock) - 4)^;
|
|
|
|
|
{Get the block size}
|
|
|
|
|
LMediumBlockSize := LMediumBlockHeader and DropMediumAndLargeFlagsMask;
|
|
|
|
|
{Is the block in use?}
|
|
|
|
|
if LMediumBlockHeader and IsFreeBlockFlag = 0 then
|
|
|
|
|
begin
|
|
|
|
|
if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
{Get the block type index}
|
|
|
|
|
LBlockTypeIndex := (Cardinal(PSmallBlockPoolHeader(LPMediumBlock).BlockType) - Cardinal(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
|
|
|
|
|
{Get the usage in the block}
|
|
|
|
|
LSmallBlockUsage := PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse
|
|
|
|
|
* SmallBlockTypes[LBlockTypeIndex].BlockSize;
|
|
|
|
|
{Get the total overhead for all the small blocks}
|
|
|
|
|
LSmallBlockOverhead := PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse
|
|
|
|
|
* (BlockHeaderSize{$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif});
|
|
|
|
|
{Add to the totals}
|
|
|
|
|
Inc(Result.FreeSmall, LMediumBlockSize - LSmallBlockUsage - BlockHeaderSize);
|
|
|
|
|
Inc(Result.Overhead, LSmallBlockOverhead + BlockHeaderSize);
|
|
|
|
|
Inc(Result.TotalAllocated, LSmallBlockUsage - LSmallBlockOverhead);
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
Dec(LMediumBlockSize, FullDebugBlockOverhead);
|
|
|
|
|
Inc(Result.Overhead, FullDebugBlockOverhead);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Add to the result}
|
|
|
|
|
Inc(Result.TotalAllocated, LMediumBlockSize - BlockHeaderSize);
|
|
|
|
|
Inc(Result.Overhead, BlockHeaderSize);
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{The medium block is free}
|
|
|
|
|
Inc(Result.FreeBig, LMediumBlockSize);
|
|
|
|
|
end;
|
|
|
|
|
{Next medium block}
|
|
|
|
|
LPMediumBlock := NextMediumBlock(LPMediumBlock);
|
|
|
|
|
end;
|
|
|
|
|
{Get the next medium block pool}
|
|
|
|
|
LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
|
|
|
|
|
end;
|
|
|
|
|
{Add the sequential feed unused space}
|
|
|
|
|
Inc(Result.Unused, MediumSequentialFeedBytesLeft);
|
|
|
|
|
{Unlock the medium blocks}
|
|
|
|
|
MediumBlocksLocked := False;
|
|
|
|
|
{Unlock all the small block types}
|
|
|
|
|
for LInd := 0 to NumSmallBlockTypes - 1 do
|
|
|
|
|
SmallBlockTypes[LInd].BlockTypeLocked := False;
|
|
|
|
|
{Step through all the large blocks}
|
|
|
|
|
LockLargeBlocks;
|
|
|
|
|
LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
|
|
|
|
|
while (LPLargeBlock <> @LargeBlocksCircularList) do
|
|
|
|
|
begin
|
|
|
|
|
LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
|
|
|
|
|
Inc(Result.TotalAddrSpace, LLargeBlockSize);
|
|
|
|
|
Inc(Result.TotalCommitted, LLargeBlockSize);
|
|
|
|
|
Inc(Result.TotalAllocated, LPLargeBlock.UserAllocatedSize
|
|
|
|
|
{$ifdef FullDebugMode} - FullDebugBlockOverhead{$endif});
|
|
|
|
|
Inc(Result.Overhead, LLargeBlockSize - LPLargeBlock.UserAllocatedSize
|
|
|
|
|
{$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif});
|
|
|
|
|
{Get the next large block}
|
|
|
|
|
LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
|
|
|
|
|
end;
|
|
|
|
|
LargeBlocksLocked := False;
|
|
|
|
|
{Set the total number of free bytes}
|
|
|
|
|
Result.TotalFree := Result.FreeSmall + Result.FreeBig + Result.Unused;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Frees all allocated memory.}
|
|
|
|
|
procedure FreeAllMemory;
|
|
|
|
|
var
|
|
|
|
|
LPMediumBlockPoolHeader, LPNextMediumBlockPoolHeader: PMediumBlockPoolHeader;
|
|
|
|
|
LPMediumFreeBlock: PMediumFreeBlock;
|
|
|
|
|
LPLargeBlock, LPNextLargeBlock: PLargeBlockHeader;
|
|
|
|
|
LInd: integer;
|
|
|
|
|
begin
|
|
|
|
|
{Free all block pools}
|
|
|
|
|
LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
|
|
|
|
|
while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
|
|
|
|
|
begin
|
|
|
|
|
{Get the next medium block pool so long}
|
|
|
|
|
LPNextMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
|
|
|
|
|
{Free this pool}
|
|
|
|
|
VirtualFree(LPMediumBlockPoolHeader, 0, MEM_RELEASE);
|
|
|
|
|
{Next pool}
|
|
|
|
|
LPMediumBlockPoolHeader := LPNextMediumBlockPoolHeader;
|
|
|
|
|
end;
|
|
|
|
|
{Clear all small block types}
|
|
|
|
|
for LInd := 0 to high(SmallBlockTypes) do
|
|
|
|
|
begin
|
|
|
|
|
SmallBlockTypes[Lind].PreviousPartiallyFreePool := @SmallBlockTypes[Lind];
|
|
|
|
|
SmallBlockTypes[Lind].NextPartiallyFreePool := @SmallBlockTypes[Lind];
|
|
|
|
|
SmallBlockTypes[Lind].NextSequentialFeedBlockAddress := pointer(1);
|
|
|
|
|
SmallBlockTypes[Lind].MaxSequentialFeedBlockAddress := nil;
|
|
|
|
|
end;
|
|
|
|
|
{Clear all medium block pools}
|
|
|
|
|
MediumBlockPoolsCircularList.PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
|
|
|
|
|
MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
|
|
|
|
|
{All medium bins are empty}
|
|
|
|
|
for LInd := 0 to high(MediumBlockBins) do
|
|
|
|
|
begin
|
|
|
|
|
LPMediumFreeBlock := @MediumBlockBins[LInd];
|
|
|
|
|
LPMediumFreeBlock.PreviousFreeBlock := LPMediumFreeBlock;
|
|
|
|
|
LPMediumFreeBlock.NextFreeBlock := LPMediumFreeBlock;
|
|
|
|
|
end;
|
|
|
|
|
{Free all large blocks}
|
|
|
|
|
LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
|
|
|
|
|
while LPLargeBlock <> @LargeBlocksCircularList do
|
|
|
|
|
begin
|
|
|
|
|
{Get the next large block}
|
|
|
|
|
LPNextLargeBlock := LPLargeBlock.NextLargeBlockHeader;
|
|
|
|
|
{Free this large block}
|
|
|
|
|
VirtualFree(LPLargeBlock, 0, MEM_RELEASE);
|
|
|
|
|
{Next large block}
|
|
|
|
|
LPLargeBlock := LPNextLargeBlock;
|
|
|
|
|
end;
|
|
|
|
|
{There are no large blocks allocated}
|
|
|
|
|
LargeBlocksCircularList.PreviousLargeBlockHeader := @LargeBlocksCircularList;
|
|
|
|
|
LargeBlocksCircularList.NextLargeBlockHeader := @LargeBlocksCircularList;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{----------------------------Memory Manager Setup-----------------------------}
|
|
|
|
|
|
|
|
|
|
{Checks that no other memory manager has been installed after the RTL MM and
|
|
|
|
|
that there are currently no live pointers allocated through the RTL MM.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
function CheckCanInstallMemoryManager: Boolean;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LErrorMessageTitle: array[0..1023] of AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{Default to error}
|
|
|
|
|
Result := False;
|
|
|
|
|
{Is FastMM already installed?}
|
|
|
|
|
if FastMMIsInstalled then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(AlreadyInstalledMsg);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
AppendStringToModuleName(AlreadyInstalledTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(AlreadyInstalledMsg, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{Has another MM been set, or has the Borland MM been used? If so, this file
|
|
|
|
|
is not the first unit in the uses clause of the project's .dpr file.}
|
|
|
|
|
if IsMemoryManagerSet then
|
|
|
|
|
begin
|
|
|
|
|
{When using runtime packages, another library may already have installed
|
|
|
|
|
FastMM: Silently ignore the installation request.}
|
|
|
|
|
{$ifndef UseRuntimePackages}
|
|
|
|
|
{Another memory manager has been set.}
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(OtherMMInstalledMsg);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
AppendStringToModuleName(OtherMMInstalledTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(OtherMMInstalledMsg, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{$ifndef Linux}
|
|
|
|
|
if (GetHeapStatus.TotalAllocated <> 0) then
|
|
|
|
|
begin
|
|
|
|
|
{Memory has been already been allocated with the RTL MM}
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(MemoryAllocatedMsg);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NoMessageBoxes}
|
|
|
|
|
AppendStringToModuleName(MemoryAllocatedTitle, LErrorMessageTitle);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
ShowMessageBox(MemoryAllocatedMsg, LErrorMessageTitle);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
Exit;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{All OK}
|
|
|
|
|
Result := True;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Initializes the lookup tables for the memory manager}
|
|
|
|
|
procedure InitializeMemoryManager;
|
|
|
|
|
var
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LInd, LSizeInd, LMinimumPoolSize, LOptimalPoolSize, LGroupNumber,
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LBlocksPerPool, LPreviousBlockSize: Cardinal;
|
|
|
|
|
LPMediumFreeBlock: PMediumFreeBlock;
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{$ifdef LoadDebugDLLDynamically}
|
|
|
|
|
{Attempt to load the FullDebugMode DLL dynamically.}
|
|
|
|
|
FullDebugModeDLL := LoadLibrary(FullDebugModeLibraryName);
|
|
|
|
|
if FullDebugModeDLL <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
GetStackTrace := GetProcAddress(FullDebugModeDLL,
|
|
|
|
|
{$ifdef RawStackTraces}'GetRawStackTrace'{$else}'GetFrameBasedStackTrace'{$endif});
|
|
|
|
|
LogStackTrace := GetProcAddress(FullDebugModeDLL, 'LogStackTrace');
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifdef EnableMMX}
|
|
|
|
|
{$ifndef ForceMMX}
|
|
|
|
|
UseMMX := MMX_Supported;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{Initialize the memory manager}
|
|
|
|
|
{-------------Set up the small block types-------------}
|
|
|
|
|
LPreviousBlockSize := 0;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
for LInd := 0 to high(SmallBlockTypes) do
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{Set the move procedure}
|
|
|
|
|
{$ifdef UseCustomFixedSizeMoveRoutines}
|
|
|
|
|
{The upsize move procedure may move chunks in 16 bytes even with 8-byte
|
|
|
|
|
alignment, since the new size will always be at least 8 bytes bigger than
|
|
|
|
|
the old size.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if not Assigned(SmallBlockTypes[LInd].UpsizeMoveProcedure) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifdef UseCustomVariableSizeMoveRoutines}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
SmallBlockTypes[LInd].UpsizeMoveProcedure := MoveX16L4;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$else}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
SmallBlockTypes[LInd].UpsizeMoveProcedure := @System.Move;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{Set the first "available pool" to the block type itself, so that the
|
|
|
|
|
allocation routines know that there are currently no pools with free
|
|
|
|
|
blocks of this size.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
SmallBlockTypes[LInd].PreviousPartiallyFreePool := @SmallBlockTypes[LInd];
|
|
|
|
|
SmallBlockTypes[LInd].NextPartiallyFreePool := @SmallBlockTypes[LInd];
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Set the block size to block type index translation table}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
for LSizeInd := (LPreviousBlockSize div SmallBlockGranularity) to ((SmallBlockTypes[LInd].BlockSize - 1) div SmallBlockGranularity) do
|
|
|
|
|
AllocSize2SmallBlockTypeIndX4[LSizeInd] := LInd * 4;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Cannot sequential feed yet: Ensure that the next address is greater than
|
|
|
|
|
the maximum address}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
SmallBlockTypes[LInd].MaxSequentialFeedBlockAddress := pointer(0);
|
|
|
|
|
SmallBlockTypes[LInd].NextSequentialFeedBlockAddress := pointer(1);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Get the mask to use for finding a medium block suitable for a block pool}
|
|
|
|
|
LMinimumPoolSize :=
|
2008-07-25 09:23:00 -04:00
|
|
|
|
((SmallBlockTypes[LInd].BlockSize * MinimumSmallBlocksPerPool
|
2006-04-15 03:44:05 -04:00
|
|
|
|
+ SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)
|
|
|
|
|
and -MediumBlockGranularity) + MediumBlockSizeOffset;
|
|
|
|
|
if LMinimumPoolSize < MinimumMediumBlockSize then
|
|
|
|
|
LMinimumPoolSize := MinimumMediumBlockSize;
|
|
|
|
|
{Get the closest group number for the minimum pool size}
|
|
|
|
|
LGroupNumber := (LMinimumPoolSize - MinimumMediumBlockSize + MediumBlockBinsPerGroup * MediumBlockGranularity div 2)
|
|
|
|
|
div (MediumBlockBinsPerGroup * MediumBlockGranularity);
|
|
|
|
|
{Too large?}
|
|
|
|
|
if LGroupNumber > 7 then
|
|
|
|
|
LGroupNumber := 7;
|
|
|
|
|
{Set the bitmap}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
SmallBlockTypes[LInd].AllowedGroupsForBlockPoolBitmap := Byte(-(1 shl LGroupNumber));
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Set the minimum pool size}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
SmallBlockTypes[LInd].MinimumBlockPoolSize := MinimumMediumBlockSize + LGroupNumber * (MediumBlockBinsPerGroup * MediumBlockGranularity);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Get the optimal block pool size}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LOptimalPoolSize := ((SmallBlockTypes[LInd].BlockSize * TargetSmallBlocksPerPool
|
2006-04-15 03:44:05 -04:00
|
|
|
|
+ SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)
|
|
|
|
|
and -MediumBlockGranularity) + MediumBlockSizeOffset;
|
|
|
|
|
{Limit the optimal pool size to within range}
|
|
|
|
|
if LOptimalPoolSize < OptimalSmallBlockPoolSizeLowerLimit then
|
|
|
|
|
LOptimalPoolSize := OptimalSmallBlockPoolSizeLowerLimit;
|
|
|
|
|
if LOptimalPoolSize > OptimalSmallBlockPoolSizeUpperLimit then
|
|
|
|
|
LOptimalPoolSize := OptimalSmallBlockPoolSizeUpperLimit;
|
|
|
|
|
{How many blocks will fit in the adjusted optimal size?}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LBlocksPerPool := (LOptimalPoolSize - SmallBlockPoolHeaderSize) div SmallBlockTypes[LInd].BlockSize;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Recalculate the optimal pool size to minimize wastage due to a partial
|
|
|
|
|
last block.}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
SmallBlockTypes[LInd].OptimalBlockPoolSize :=
|
|
|
|
|
((LBlocksPerPool * SmallBlockTypes[LInd].BlockSize + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset) and -MediumBlockGranularity) + MediumBlockSizeOffset;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{Debug checks}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if (SmallBlockTypes[LInd].OptimalBlockPoolSize < MinimumMediumBlockSize)
|
|
|
|
|
or (SmallBlockTypes[LInd].BlockSize div SmallBlockGranularity * SmallBlockGranularity <> SmallBlockTypes[LInd].BlockSize) then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Set the previous small block size}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LPreviousBlockSize := SmallBlockTypes[LInd].BlockSize;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{-------------------Set up the medium blocks-------------------}
|
|
|
|
|
{$ifdef CheckHeapForCorruption}
|
|
|
|
|
{Check that there are no gaps between where the small blocks end and the
|
|
|
|
|
medium blocks start}
|
|
|
|
|
if (((MaximumSmallBlockSize - 3) + (MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset))
|
|
|
|
|
and -MediumBlockGranularity) + MediumBlockSizeOffset < MinimumMediumBlockSize then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef BCB6OrDelphi7AndUp}
|
|
|
|
|
System.Error(reInvalidPtr);
|
|
|
|
|
{$else}
|
|
|
|
|
System.RunError(reInvalidPtr);
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{There are currently no medium block pools}
|
|
|
|
|
MediumBlockPoolsCircularList.PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
|
|
|
|
|
MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
|
|
|
|
|
{All medium bins are empty}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
for LInd := 0 to high(MediumBlockBins) do
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LPMediumFreeBlock := @MediumBlockBins[LInd];
|
2006-04-15 03:44:05 -04:00
|
|
|
|
LPMediumFreeBlock.PreviousFreeBlock := LPMediumFreeBlock;
|
|
|
|
|
LPMediumFreeBlock.NextFreeBlock := LPMediumFreeBlock;
|
|
|
|
|
end;
|
|
|
|
|
{------------------Set up the large blocks---------------------}
|
|
|
|
|
LargeBlocksCircularList.PreviousLargeBlockHeader := @LargeBlocksCircularList;
|
|
|
|
|
LargeBlocksCircularList.NextLargeBlockHeader := @LargeBlocksCircularList;
|
|
|
|
|
{------------------Set up the debugging structures---------------------}
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{Set up the fake VMT}
|
|
|
|
|
{Copy the basic info from the TFreedObject class}
|
|
|
|
|
System.Move(Pointer(Integer(TFreedObject) + vmtSelfPtr + 4)^,
|
|
|
|
|
FreedObjectVMT.VMTData[vmtSelfPtr + 4], vmtParent - vmtSelfPtr);
|
|
|
|
|
PCardinal(@FreedObjectVMT.VMTData[vmtSelfPtr])^ := Cardinal(@FreedObjectVMT.VMTMethods[0]);
|
|
|
|
|
{Set up the virtual method table}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
for LInd := 0 to MaxFakeVMTEntries - 1 do
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
PCardinal(@FreedObjectVMT.VMTMethods[low(FreedObjectVMT.VMTMethods) + Integer(LInd * 4)])^ :=
|
|
|
|
|
Cardinal(@TFreedObject.GetVirtualMethodIndex) + LInd * 6;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$ifdef CatchUseOfFreedInterfaces}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
VMTBadInterface[LInd] := @TFreedObject.InterfaceError;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{Set up the default log file name}
|
|
|
|
|
SetDefaultMMLogFileName;
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
{Installs the memory manager (InitializeMemoryManager should be called first)}
|
|
|
|
|
procedure InstallMemoryManager;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef MMSharingEnabled}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
var
|
|
|
|
|
i, LCurrentProcessID: Cardinal;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LPMapAddress: PPointer;
|
|
|
|
|
LChar: AnsiChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
if not FastMMIsInstalled then
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef FullDebugMode}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Try to reserve the 64K block}
|
|
|
|
|
ReservedBlock := VirtualAlloc(Pointer(DebugReservedAddress), 65536, MEM_RESERVE, PAGE_NOACCESS);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef MMSharingEnabled}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Build a string identifying the current process}
|
|
|
|
|
LCurrentProcessID := GetCurrentProcessId;
|
|
|
|
|
for i := 0 to 7 do
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
LChar := HexTable[((LCurrentProcessID shr (i * 4)) and $F)];
|
|
|
|
|
MappingObjectName[(High(MappingObjectName) - 1) - i] := LChar;
|
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
|
|
|
|
UniqueProcessIDString[8 - i] := LChar;
|
|
|
|
|
UniqueProcessIDStringBE[8 - i] := LChar;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef AttemptToUseSharedMM}
|
|
|
|
|
{Is the replacement memory manager already installed for this process?}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
|
|
|
|
MMWindow := FindWindowA('STATIC', PAnsiChar(@UniqueProcessIDString[1]));
|
|
|
|
|
MMWindowBE := FindWindowA('STATIC', PAnsiChar(@UniqueProcessIDStringBE[1]));
|
|
|
|
|
{$endif}
|
|
|
|
|
MappingObjectHandle := OpenFileMappingA(FILE_MAP_READ, False, MappingObjectName);
|
|
|
|
|
{Is no MM being shared?}
|
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
|
|
|
|
if ((MMWindow or MMWindowBE or MappingObjectHandle) = 0) then
|
|
|
|
|
{$else}
|
|
|
|
|
if MappingObjectHandle = 0 then
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef ShareMM}
|
|
|
|
|
{Share the MM with other DLLs? - if this DLL is unloaded, then
|
|
|
|
|
dependent DLLs will cause a crash.}
|
|
|
|
|
{$ifndef ShareMMIfLibrary}
|
|
|
|
|
if not IsLibrary then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{No memory manager installed yet - create the invisible window}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
MMWindow := CreateWindowA('STATIC', PAnsiChar(@UniqueProcessIDString[1]),
|
2006-04-15 03:44:05 -04:00
|
|
|
|
WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
|
2008-07-25 09:23:00 -04:00
|
|
|
|
MMWindowBE := CreateWindowA('STATIC', PAnsiChar(@UniqueProcessIDStringBE[1]),
|
2006-04-15 03:44:05 -04:00
|
|
|
|
WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
|
|
|
|
|
{The window data is a pointer to this memory manager}
|
|
|
|
|
if MMWindow <> 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
SetWindowLongA(MMWindow, GWL_USERDATA, Integer(@NewMemoryManager));
|
2006-04-15 03:44:05 -04:00
|
|
|
|
if MMWindowBE <> 0 then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
SetWindowLongA(MMWindowBE, GWL_USERDATA, Integer(@NewMemoryManager));
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Create the memory mapped file}
|
|
|
|
|
MappingObjectHandle := CreateFileMappingA(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, 4,
|
|
|
|
|
MappingObjectName);
|
|
|
|
|
{Map a view of the memory}
|
|
|
|
|
LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_WRITE, 0, 0, 0);
|
|
|
|
|
{Set a pointer to the new memory manager}
|
|
|
|
|
LPMapAddress^ := @NewMemoryManager;
|
|
|
|
|
{Unmap the file}
|
|
|
|
|
UnmapViewOfFile(LPMapAddress);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{We will be using this memory manager}
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
NewMemoryManager.GetMem := FastGetMem;
|
|
|
|
|
NewMemoryManager.FreeMem := FastFreeMem;
|
|
|
|
|
NewMemoryManager.ReallocMem := FastReallocMem;
|
|
|
|
|
{$else}
|
|
|
|
|
NewMemoryManager.GetMem := DebugGetMem;
|
|
|
|
|
NewMemoryManager.FreeMem := DebugFreeMem;
|
|
|
|
|
NewMemoryManager.ReallocMem := DebugReallocMem;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef BDS2006AndUp}
|
|
|
|
|
{$ifndef FullDebugMode}
|
|
|
|
|
NewMemoryManager.AllocMem := FastAllocMem;
|
|
|
|
|
{$else}
|
|
|
|
|
NewMemoryManager.AllocMem := DebugAllocMem;
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
NewMemoryManager.RegisterExpectedMemoryLeak := RegisterExpectedMemoryLeak;
|
|
|
|
|
NewMemoryManager.UnRegisterExpectedMemoryLeak := UnRegisterExpectedMemoryLeak;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$else}
|
|
|
|
|
NewMemoryManager.RegisterExpectedMemoryLeak := NoOpRegisterExpectedMemoryLeak;
|
|
|
|
|
NewMemoryManager.UnRegisterExpectedMemoryLeak := NoOpUnRegisterExpectedMemoryLeak;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Owns the memory manager}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
IsMemoryManagerOwner := True;
|
|
|
|
|
{$ifdef AttemptToUseSharedMM}
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
{Get the address of the shared memory manager}
|
|
|
|
|
{$ifndef BDS2006AndUp}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
|
|
|
|
if MappingObjectHandle <> 0 then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Map a view of the memory}
|
|
|
|
|
LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_READ, 0, 0, 0);
|
|
|
|
|
{Set the new memory manager}
|
|
|
|
|
NewMemoryManager := PMemoryManager(LPMapAddress^)^;
|
|
|
|
|
{Unmap the file}
|
|
|
|
|
UnmapViewOfFile(LPMapAddress);
|
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if MMWindow <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
NewMemoryManager := PMemoryManager(GetWindowLong(MMWindow, GWL_USERDATA))^;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
NewMemoryManager := PMemoryManager(GetWindowLong(MMWindowBE, GWL_USERDATA))^;
|
|
|
|
|
end;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$else}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
|
|
|
|
if MappingObjectHandle <> 0 then
|
2006-04-15 03:44:05 -04:00
|
|
|
|
begin
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Map a view of the memory}
|
|
|
|
|
LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_READ, 0, 0, 0);
|
|
|
|
|
{Set the new memory manager}
|
|
|
|
|
NewMemoryManager := PMemoryManagerEx(LPMapAddress^)^;
|
|
|
|
|
{Unmap the file}
|
|
|
|
|
UnmapViewOfFile(LPMapAddress);
|
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
2008-07-25 09:23:00 -04:00
|
|
|
|
if MMWindow <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
NewMemoryManager := PMemoryManagerEx(GetWindowLong(MMWindow, GWL_USERDATA))^;
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
begin
|
|
|
|
|
NewMemoryManager := PMemoryManagerEx(GetWindowLong(MMWindowBE, GWL_USERDATA))^;
|
|
|
|
|
end;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{Close the file mapping handle}
|
|
|
|
|
CloseHandle(MappingObjectHandle);
|
|
|
|
|
MappingObjectHandle := 0;
|
|
|
|
|
{The memory manager is not owned by this module}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
IsMemoryManagerOwner := False;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Save the old memory manager}
|
|
|
|
|
GetMemoryManager(OldMemoryManager);
|
|
|
|
|
{Replace the memory manager with either this one or the shared one.}
|
|
|
|
|
SetMemoryManager(NewMemoryManager);
|
|
|
|
|
{FastMM is now installed}
|
|
|
|
|
FastMMIsInstalled := True;
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
|
|
|
|
if IsMemoryManagerOwner then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(FastMMInstallMsg)
|
2006-04-15 03:44:05 -04:00
|
|
|
|
else
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(FastMMInstallSharedMsg);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
procedure UninstallMemoryManager;
|
|
|
|
|
begin
|
|
|
|
|
{Is this the owner of the shared MM window?}
|
|
|
|
|
if IsMemoryManagerOwner then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef ShareMM}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$ifdef EnableBackwardCompatibleMMSharing}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Destroy the window}
|
|
|
|
|
if MMWindow <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
DestroyWindow(MMWindow);
|
|
|
|
|
MMWindow := 0;
|
|
|
|
|
end;
|
|
|
|
|
if MMWindowBE <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
DestroyWindow(MMWindowBE);
|
|
|
|
|
MMWindowBE := 0;
|
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{Destroy the memory mapped file handle}
|
|
|
|
|
if MappingObjectHandle <> 0 then
|
|
|
|
|
begin
|
|
|
|
|
CloseHandle(MappingObjectHandle);
|
|
|
|
|
MappingObjectHandle := 0;
|
|
|
|
|
end;
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef FullDebugMode}
|
|
|
|
|
{Release the reserved block}
|
|
|
|
|
if ReservedBlock <> nil then
|
|
|
|
|
begin
|
|
|
|
|
VirtualFree(ReservedBlock, 0, MEM_RELEASE);
|
|
|
|
|
ReservedBlock := nil;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
{$ifndef DetectMMOperationsAfterUninstall}
|
|
|
|
|
{Restore the old memory manager}
|
|
|
|
|
SetMemoryManager(OldMemoryManager);
|
|
|
|
|
{$else}
|
|
|
|
|
{Set the invalid memory manager: no more MM operations allowed}
|
|
|
|
|
SetMemoryManager(InvalidMemoryManager);
|
|
|
|
|
{$endif}
|
|
|
|
|
{Memory manager has been uninstalled}
|
|
|
|
|
FastMMIsInstalled := False;
|
|
|
|
|
{$ifdef UseOutputDebugString}
|
|
|
|
|
if IsMemoryManagerOwner then
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(FastMMuninstallMsg)
|
2006-04-15 03:44:05 -04:00
|
|
|
|
else
|
2008-07-25 09:23:00 -04:00
|
|
|
|
OutputDebugStringA(FastMMUninstallSharedMsg);
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
|
2008-07-25 09:23:00 -04:00
|
|
|
|
procedure FinalizeMemoryManager;
|
|
|
|
|
begin
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{Restore the old memory manager if FastMM has been installed}
|
|
|
|
|
if FastMMIsInstalled then
|
|
|
|
|
begin
|
|
|
|
|
{$ifndef NeverUninstall}
|
|
|
|
|
{Uninstall FastMM}
|
|
|
|
|
UninstallMemoryManager;
|
|
|
|
|
{$endif}
|
|
|
|
|
{Do we own the memory manager, or are we just sharing it?}
|
|
|
|
|
if IsMemoryManagerOwner then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef CheckUseOfFreedBlocksOnShutdown}
|
|
|
|
|
CheckBlocksOnShutdown(
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
True
|
|
|
|
|
{$ifdef RequireIDEPresenceForLeakReporting}
|
|
|
|
|
and DelphiIsRunning
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef RequireDebuggerPresenceForLeakReporting}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
and ((DebugHook <> 0)
|
|
|
|
|
{$ifdef PatchBCBTerminate}
|
|
|
|
|
or (Assigned(pCppDebugHook) and (pCppDebugHook^ <> 0))
|
|
|
|
|
{$endif PatchBCBTerminate}
|
|
|
|
|
)
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef ManualLeakReportingControl}
|
|
|
|
|
and ReportMemoryLeaksOnShutdown
|
|
|
|
|
{$endif}
|
|
|
|
|
{$else}
|
|
|
|
|
False
|
|
|
|
|
{$endif}
|
|
|
|
|
);
|
|
|
|
|
{$else}
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
if True
|
|
|
|
|
{$ifdef RequireIDEPresenceForLeakReporting}
|
|
|
|
|
and DelphiIsRunning
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef RequireDebuggerPresenceForLeakReporting}
|
2008-07-25 09:23:00 -04:00
|
|
|
|
and ((DebugHook <> 0)
|
|
|
|
|
{$ifdef PatchBCBTerminate}
|
|
|
|
|
or (Assigned(pCppDebugHook) and (pCppDebugHook^ <> 0))
|
|
|
|
|
{$endif PatchBCBTerminate}
|
|
|
|
|
)
|
2006-04-15 03:44:05 -04:00
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef ManualLeakReportingControl}
|
|
|
|
|
and ReportMemoryLeaksOnShutdown
|
|
|
|
|
{$endif}
|
|
|
|
|
then
|
|
|
|
|
CheckBlocksOnShutdown(True);
|
|
|
|
|
{$endif}
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifdef EnableMemoryLeakReporting}
|
|
|
|
|
{Free the expected memory leaks list}
|
|
|
|
|
if ExpectedMemoryLeaks <> nil then
|
|
|
|
|
begin
|
|
|
|
|
VirtualFree(ExpectedMemoryLeaks, 0, MEM_RELEASE);
|
|
|
|
|
ExpectedMemoryLeaks := nil;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
{$ifndef NeverUninstall}
|
|
|
|
|
{Clean up: Free all memory. If this is a .DLL that owns its own MM, then
|
|
|
|
|
it is necessary to prevent the main application from running out of
|
|
|
|
|
address space.}
|
|
|
|
|
FreeAllMemory;
|
|
|
|
|
{$endif}
|
|
|
|
|
end;
|
|
|
|
|
end;
|
2008-07-25 09:23:00 -04:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
initialization
|
|
|
|
|
{$ifndef BCB}
|
|
|
|
|
{$ifdef InstallOnlyIfRunningInIDE}
|
|
|
|
|
if (DebugHook <> 0) and DelphiIsRunning then
|
|
|
|
|
{$endif}
|
|
|
|
|
begin
|
|
|
|
|
{Initialize all the lookup tables, etc. for the memory manager}
|
|
|
|
|
InitializeMemoryManager;
|
|
|
|
|
{Has another MM been set, or has the Borland MM been used? If so, this file
|
|
|
|
|
is not the first unit in the uses clause of the project's .dpr file.}
|
|
|
|
|
if CheckCanInstallMemoryManager then
|
|
|
|
|
begin
|
|
|
|
|
{$ifdef ClearLogFileOnStartup}
|
|
|
|
|
DeleteEventLog;
|
|
|
|
|
{$endif}
|
|
|
|
|
InstallMemoryManager;
|
|
|
|
|
end;
|
|
|
|
|
end;
|
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
|
|
finalization
|
|
|
|
|
{$ifndef PatchBCBTerminate}
|
|
|
|
|
FinalizeMemoryManager;
|
|
|
|
|
{$endif}
|
2006-04-15 03:44:05 -04:00
|
|
|
|
|
|
|
|
|
end.
|