/Includes/FastMM/FastMM4.pas

https://bitbucket.org/fed/sas.planet.src · Pascal · 11699 lines · 6843 code · 277 blank · 4579 comment · 428 complexity · f19e16adc7d8751530cfda69ea0a681a MD5 · raw file

  1. (*
  2. Fast Memory Manager 4.991
  3. Description:
  4. A fast replacement memory manager for Embarcadero Delphi Win32 applications
  5. that scales well under multi-threaded usage, is not prone to memory
  6. fragmentation, and supports shared memory without the use of external .DLL
  7. files.
  8. Homepage:
  9. http://fastmm.sourceforge.net
  10. Advantages:
  11. - Fast
  12. - Low overhead. FastMM is designed for an average of 5% and maximum of 10%
  13. overhead per block.
  14. - Supports up to 3GB of user mode address space under Windows 32-bit and 4GB
  15. under Windows 64-bit. Add the "$SetPEFlags $20" option (in curly braces)
  16. to your .dpr to enable this.
  17. - Highly aligned memory blocks. Can be configured for either 8-byte or 16-byte
  18. alignment.
  19. - Good scaling under multi-threaded applications
  20. - Intelligent reallocations. Avoids slow memory move operations through
  21. not performing unneccesary downsizes and by having a minimum percentage
  22. block size growth factor when an in-place block upsize is not possible.
  23. - Resistant to address space fragmentation
  24. - No external DLL required when sharing memory between the application and
  25. external libraries (provided both use this memory manager)
  26. - Optionally reports memory leaks on program shutdown. (This check can be set
  27. to be performed only if Delphi is currently running on the machine, so end
  28. users won't be bothered by the error message.)
  29. - Supports Delphi 4 (or later), C++ Builder 4 (or later), Kylix 3.
  30. Usage:
  31. Delphi:
  32. Place this unit as the very first unit under the "uses" section in your
  33. project's .dpr file. When sharing memory between an application and a DLL
  34. (e.g. when passing a long string or dynamic array to a DLL function), both the
  35. main application and the DLL must be compiled using this memory manager (with
  36. the required conditional defines set). There are some conditional defines
  37. (inside FastMM4Options.inc) that may be used to tweak the memory manager. To
  38. enable support for a user mode address space greater than 2GB you will have to
  39. use the EditBin* tool to set the LARGE_ADDRESS_AWARE flag in the EXE header.
  40. This informs Windows x64 or Windows 32-bit (with the /3GB option set) that the
  41. application supports an address space larger than 2GB (up to 4GB). In Delphi 6
  42. and later you can also specify this flag through the compiler directive
  43. {$SetPEFlags $20}
  44. *The EditBin tool ships with the MS Visual C compiler.
  45. C++ Builder 6:
  46. Refer to the instructions inside FastMM4BCB.cpp.
  47. License:
  48. This work is copyright Professional Software Development / Pierre le Riche. It
  49. is released under a dual license, and you may choose to use it under either the
  50. Mozilla Public License 1.1 (MPL 1.1, available from
  51. http://www.mozilla.org/MPL/MPL-1.1.html) or the GNU Lesser General Public
  52. License 2.1 (LGPL 2.1, available from
  53. http://www.opensource.org/licenses/lgpl-license.php). If you find FastMM useful
  54. or you would like to support further development, a donation would be much
  55. appreciated. My banking details are:
  56. Country: South Africa
  57. Bank: ABSA Bank Ltd
  58. Branch: Somerset West
  59. Branch Code: 334-712
  60. Account Name: PSD (Distribution)
  61. Account No.: 4041827693
  62. Swift Code: ABSAZAJJ
  63. My PayPal account is:
  64. bof@psd.co.za
  65. Contact Details:
  66. My contact details are shown below if you would like to get in touch with me.
  67. If you use this memory manager I would like to hear from you: please e-mail me
  68. your comments - good and bad.
  69. Snailmail:
  70. PO Box 2514
  71. Somerset West
  72. 7129
  73. South Africa
  74. E-mail:
  75. plr@psd.co.za
  76. Support:
  77. If you have trouble using FastMM, you are welcome to drop me an e-mail at the
  78. address above, or you may post your questions in the BASM newsgroup on the
  79. Embarcadero news server (which is where I hang out quite frequently).
  80. Disclaimer:
  81. FastMM has been tested extensively with both single and multithreaded
  82. applications on various hardware platforms, but unfortunately I am not in a
  83. position to make any guarantees. Use it at your own risk.
  84. Acknowledgements (for version 4):
  85. - Eric Grange for his RecyclerMM on which the earlier versions of FastMM were
  86. based. RecyclerMM was what inspired me to try and write my own memory
  87. manager back in early 2004.
  88. - Primoz Gabrijelcic for helping to track down various bugs.
  89. - Dennis Christensen for his tireless efforts with the Fastcode project:
  90. helping to develop, optimize and debug the growing Fastcode library.
  91. - JiYuan Xie for implementing the leak reporting code for C++ Builder.
  92. - Sebastian Zierer for implementing the OS X support.
  93. - Pierre Y. for his suggestions regarding the extension of the memory leak
  94. checking options.
  95. - Hanspeter Widmer for his suggestion to have an option to display install and
  96. uninstall debug messages and moving options to a separate file, as well as
  97. the new usage tracker.
  98. - Anders Isaksson and Greg for finding and identifying the "DelphiIsRunning"
  99. bug under Delphi 5.
  100. - Francois Malan for various suggestions and bug reports.
  101. - Craig Peterson for helping me identify the cache associativity issues that
  102. could arise due to medium blocks always being an exact multiple of 256 bytes.
  103. Also for various other bug reports and enhancement suggestions.
  104. - Jarek Karciarz, Vladimir Ulchenko (Vavan) and Bob Gonder for their help in
  105. implementing the BCB support.
  106. - Ben Taylor for his suggestion to display the object class of all memory
  107. leaks.
  108. - Jean Marc Eber and Vincent Mahon (the Memcheck guys) for the call stack
  109. trace code and also the method used to catch virtual method calls on freed
  110. objects.
  111. - Nahan Hyn for the suggestion to be able to enable or disable memory leak
  112. reporting through a global variable (the "ManualLeakReportingControl"
  113. option.)
  114. - Leonel Togniolli for various suggestions with regard to enhancing the bug
  115. tracking features of FastMM and other helpful advice.
  116. - Joe Bain and Leonel Togniolli for the workaround to QC#10922 affecting
  117. compilation under Delphi 2005.
  118. - Robert Marquardt for the suggestion to make localisation of FastMM easier by
  119. having all string constants together.
  120. - Simon Kissel and Fikret Hasovic for their help in implementing Kylix support.
  121. - Matthias Thoma, Petr Vones, Robert Rossmair and the rest of the JCL team for
  122. their debug info library used in the debug info support DLL and also the
  123. code used to check for a valid call site in the "raw" stack trace code.
  124. - Andreas Hausladen for the suggestion to use an external DLL to enable the
  125. reporting of debug information.
  126. - Alexander Tabakov for various good suggestions regarding the debugging
  127. facilities of FastMM.
  128. - M. Skloff for some useful suggestions and bringing to my attention some
  129. compiler warnings.
  130. - Martin Aignesberger for the code to use madExcept instead of the JCL library
  131. inside the debug info support DLL.
  132. - Diederik and Dennis Passmore for the suggestion to be able to register
  133. expected leaks.
  134. - Dario Tiraboschi and Mark Gebauer for pointing out the problems that occur
  135. when range checking and complete boolean evaluation is turned on.
  136. - Arthur Hoornweg for notifying me of the image base being incorrect for
  137. borlndmm.dll.
  138. - Theo Carr-Brion and Hanspeter Widmer for finding the false alarm error
  139. message "Block Header Has Been Corrupted" bug in FullDebugMode.
  140. - Danny Heijl for reporting the compiler error in "release" mode.
  141. - Omar Zelaya for reporting the BCB support regression bug.
  142. - Dan Miser for various good suggestions, e.g. not logging expected leaks to
  143. file, enhancements the stack trace and messagebox functionality, etc.
  144. - Arjen de Ruijter for fixing the bug in GetMemoryLeakType that caused it
  145. to not properly detect expected leaks registered by class when in
  146. "FullDebugMode".
  147. - Aleksander Oven for reporting the installation problem when trying to use
  148. FastMM in an application together with libraries that all use runtime
  149. packages.
  150. - Kristofer Skaug for reporting the bug that sometimes causes the leak report
  151. to be shown, even when all the leaks have been registered as expected leaks.
  152. Also for some useful enhancement suggestions.
  153. - Günther Schoch for the "RequireDebuggerPresenceForLeakReporting" option.
  154. - Jan Schlüter for the "ForceMMX" option.
  155. - Hallvard Vassbotn for various good enhancement suggestions.
  156. - Mark Edington for some good suggestions and bug reports.
  157. - Paul Ishenin for reporting the compilation error when the NoMessageBoxes
  158. option is set and also the missing call stack entries issue when "raw" stack
  159. traces are enabled, as well as for the Russian translation.
  160. - Cristian Nicola for reporting the compilation bug when the
  161. CatchUseOfFreedInterfaces option was enabled (4.40).
  162. - Mathias Rauen (madshi) for improving the support for madExcept in the debug
  163. info support DLL.
  164. - Roddy Pratt for the BCB5 support code.
  165. - Rene Mihula for the Czech translation and the suggestion to have dynamic
  166. loading of the FullDebugMode DLL as an option.
  167. - Artur Redzko for the Polish translation.
  168. - Bart van der Werf for helping me solve the DLL unload order problem when
  169. using the debug mode borlndmm.dll library, as well as various other
  170. suggestions.
  171. - JRG ("The Delphi Guy") for the Spanish translation.
  172. - Justus Janssen for Delphi 4 support.
  173. - Vadim Lopushansky and Charles Vinal for reporting the Delphi 5 compiler
  174. error in version 4.50.
  175. - Johni Jeferson Capeletto for the Brazilian Portuguese translation.
  176. - Kurt Fitzner for reporting the BCB6 compiler error in 4.52.
  177. - Michal Niklas for reporting the Kylix compiler error in 4.54.
  178. - Thomas Speck and Uwe Queisser for German translations.
  179. - Zaenal Mutaqin for the Indonesian translation.
  180. - Carlos Macao for the Portuguese translation.
  181. - Michael Winter for catching the performance issue when reallocating certain
  182. block sizes.
  183. - dzmitry[li] for the Belarussian translation.
  184. - Marcelo Montenegro for the updated Spanish translation.
  185. - Jud Cole for finding and reporting the bug which may trigger a read access
  186. violation when upsizing certain small block sizes together with the
  187. "UseCustomVariableSizeMoveRoutines" option.
  188. - Zdenek Vasku for reporting and fixing the memory manager sharing bug
  189. affecting Windows 95/98/Me.
  190. - RB Winston for suggesting the improvement to GExperts "backup" support.
  191. - Thomas Schulz for reporting the bug affecting large address space support
  192. under FullDebugMode, as well as the recursive call bug when attempting to
  193. report memory leaks when EnableMemoryLeakReporting is disabled.
  194. - Luigi Sandon for the Italian translation.
  195. - Werner Bochtler for various suggestions and bug reports.
  196. - Markus Beth for suggesting the "NeverSleepOnThreadContention" option.
  197. - JiYuan Xie for the Simplified Chinese translation.
  198. - Andrey Shtukaturov for the updated Russian translation, as well as the
  199. Ukrainian translation.
  200. - Dimitry Timokhov for finding two elusive bugs in the memory leak class
  201. detection code.
  202. - Paulo Moreno for fixing the AllocMem bug in FullDebugMode that prevented
  203. large blocks from being cleared.
  204. - Vladimir Bochkarev for the suggestion to remove some unnecessary code if the
  205. MM sharing mechanism is disabled.
  206. - Loris Luise for the version constant suggestion.
  207. - J.W. de Bokx for the MessageBox bugfix.
  208. - Igor Lindunen for reporting the bug that caused the Align16Bytes option to
  209. not work in FullDebugMode.
  210. - Ionut Muntean for the Romanian translation.
  211. - Florent Ouchet for the French translation.
  212. - Marcus Mönnig for the ScanMemoryPoolForCorruptions suggestion and the
  213. suggestion to have the option to scan the memory pool before every
  214. operation when in FullDebugMode.
  215. - Francois Piette for bringing under my attention that
  216. ScanMemoryPoolForCorruption was not thread safe.
  217. - Michael Rabatscher for reporting some compiler warnings.
  218. - QianYuan Wang for the Simplified Chinese translation of FastMM4Options.inc.
  219. - Maurizio Lotauro and Christian-W. Budde for reporting some Delphi 5
  220. compiler errors.
  221. - Patrick van Logchem for the DisableLoggingOfMemoryDumps option.
  222. - Norbert Spiegel for the BCB4 support code.
  223. - Uwe Schuster for the improved string leak detection code.
  224. - Murray McGowan for improvements to the usage tracker.
  225. - Michael Hieke for the SuppressFreeMemErrorsInsideException option as well
  226. as a bugfix to GetMemoryMap.
  227. - Richard Bradbrook for fixing the Windows 95 FullDebugMode support that was
  228. broken in version 4.94.
  229. - Zach Saw for the suggestion to (optionally) use SwitchToThread when
  230. waiting for a lock on a shared resource to be released.
  231. - Everyone who have made donations. Thanks!
  232. - Any other Fastcoders or supporters that I have forgotten, and also everyone
  233. that helped with the older versions.
  234. Change log:
  235. Version 1.00 (28 June 2004):
  236. - First version (called PSDMemoryManager). Based on RecyclerMM (free block
  237. stack approach) by Eric Grange.
  238. Version 2.00 (3 November 2004):
  239. - Complete redesign and rewrite from scratch. Name changed to FastMM to
  240. reflect this fact. Uses a linked-list approach. Is faster, has less memory
  241. overhead, and will now catch most bad pointers on FreeMem calls.
  242. Version 3.00 (1 March 2005):
  243. - Another rewrite. Reduced the memory overhead by: (a) not having a separate
  244. memory area for the linked list of free blocks (uses space inside free
  245. blocks themselves) (b) batch managers are allocated as part of chunks (c)
  246. block size lookup table size reduced. This should make FastMM more CPU
  247. cache friendly.
  248. Version 4.00 (7 June 2005):
  249. - Yet another rewrite. FastMM4 is in fact three memory managers in one: Small
  250. blocks (up to a few KB) are managed through the binning model in the same
  251. way as previous versions, medium blocks (from a few KB up to approximately
  252. 256K) are allocated in a linked-list fashion, and large blocks are grabbed
  253. directly from the system through VirtualAlloc. This 3-layered design allows
  254. very fast operation with the most frequently used block sizes (small
  255. blocks), while also minimizing fragmentation and imparting significant
  256. overhead savings with blocks larger than a few KB.
  257. Version 4.01 (8 June 2005):
  258. - Added the options "RequireDebugInfoForLeakReporting" and
  259. "RequireIDEPresenceForLeakReporting" as suggested by Pierre Y.
  260. - Fixed the "DelphiIsRunning" function not working under Delphi 5, and
  261. consequently no leak checking. (Reported by Anders Isaksson and Greg.)
  262. Version 4.02 (8 June 2005):
  263. - Fixed the compilation error when both the "AssumeMultiThreaded" and
  264. "CheckHeapForCorruption options were set. (Reported by Francois Malan.)
  265. Version 4.03 (9 June 2005):
  266. - Added descriptive error messages when FastMM4 cannot be installed because
  267. another MM has already been installed or memory has already been allocated.
  268. Version 4.04 (13 June 2005):
  269. - Added a small fixed offset to the size of medium blocks (previously always
  270. exact multiples of 256 bytes). This makes performance problems due to CPU
  271. cache associativity limitations much less likely. (Reported by Craig
  272. Peterson.)
  273. Version 4.05 (17 June 2005):
  274. - Added the Align16Bytes option. Disable this option to drop the 16 byte
  275. alignment restriction and reduce alignment to 8 bytes for the smallest
  276. block sizes. Disabling Align16Bytes should lower memory consumption at the
  277. cost of complicating the use of aligned SSE move instructions. (Suggested
  278. by Craig Peterson.)
  279. - Added a support unit for C++ Builder 6 - Add FastMM4BCB.cpp and
  280. FastMM4.pas to your BCB project to use FastMM instead of the RTL MM. Memory
  281. leak checking is not supported because (unfortunately) once an MM is
  282. installed under BCB you cannot uninstall it... at least not without
  283. modifying the RTL code in exit.c or patching the RTL code runtime. (Thanks
  284. to Jarek Karciarz, Vladimir Ulchenko and Bob Gonder.)
  285. Version 4.06 (22 June 2005):
  286. - Displays the class of all leaked objects on the memory leak report and also
  287. tries to identify leaked long strings. Previously it only displayed the
  288. sizes of all leaked blocks. (Suggested by Ben Taylor.)
  289. - Added support for displaying the sizes of medium and large block memory
  290. leaks. Previously it only displayed details for small block leaks.
  291. Version 4.07 (22 June 2005):
  292. - Fixed the detection of the class of leaked objects not working under
  293. Windows 98/Me.
  294. Version 4.08 (27 June 2005):
  295. - Added a BorlndMM.dpr project to allow you to build a borlndmm.dll that uses
  296. FastMM4 instead of the default memory manager. You may replace the old
  297. DLL in the Delphi \Bin directory to make the IDE use this memory manager
  298. instead.
  299. Version 4.09 (30 June 2005):
  300. - Included a patch fix for the bug affecting replacement borlndmm.dll files
  301. with Delphi 2005 (QC#14007). Compile the patch, close Delphi, and run it
  302. once to patch your vclide90.bpl. You will now be able to use the
  303. replacement borlndmm.dll to speed up the Delphi 2005 IDE as well.
  304. Version 4.10 (7 July 2005):
  305. - Due to QC#14070 ("Delphi IDE attempts to free memory after the shutdown
  306. code of borlndmm.dll has been called"), FastMM cannot be uninstalled
  307. safely when used inside a replacement borlndmm.dll for the IDE. Added a
  308. conditional define "NeverUninstall" for this purpose.
  309. - Added the "FullDebugMode" option to pad all blocks with a header and footer
  310. to help you catch memory overwrite bugs in your applications. All blocks
  311. returned to freemem are also zeroed out to help catch bugs involving the
  312. use of previously freed blocks. Also catches attempts at calling virtual
  313. methods of freed objects provided the block in question has not been reused
  314. since the object was freed. Displays stack traces on error to aid debugging.
  315. - Added the "LogErrorsToFile" option to log all errors to a text file in the
  316. same folder as the application.
  317. - Added the "ManualLeakReportingControl" option (suggested by Nahan Hyn) to
  318. enable control over whether the memory leak report should be done or not
  319. via a global variable.
  320. Version 4.11 (7 July 2005):
  321. - Fixed a compilation error under Delphi 2005 due to QC#10922. (Thanks to Joe
  322. Bain and Leonel Togniolli.)
  323. - Fixed leaked object classes not displaying in the leak report in
  324. "FullDebugMode".
  325. Version 4.12 (8 July 2005):
  326. - Moved all the string constants to one place to make it easier to do
  327. translations into other languages. (Thanks to Robert Marquardt.)
  328. - Added support for Kylix. Some functionality is currently missing: No
  329. support for detecting the object class on leaks and also no MM sharing.
  330. (Thanks to Simon Kissel and Fikret Hasovic).
  331. Version 4.13 (11 July 2005):
  332. - Added the FastMM_DebugInfo.dll support library to display debug info for
  333. stack traces.
  334. - Stack traces for the memory leak report is now logged to the log file in
  335. "FullDebugMode".
  336. Version 4.14 (14 July 2005):
  337. - Fixed string leaks not being detected as such in "FullDebugMode". (Thanks
  338. to Leonel Togniolli.)
  339. - Fixed the compilation error in "FullDebugMode" when "LogErrorsToFile" is
  340. not set. (Thanks to Leonel Togniolli.)
  341. - Added a "Release" option to allow the grouping of various options and to
  342. make it easier to make debug and release builds. (Thanks to Alexander
  343. Tabakov.)
  344. - Added a "HideMemoryLeakHintMessage" option to not display the hint below
  345. the memory leak message. (Thanks to Alexander Tabakov.)
  346. - Changed the fill character for "FullDebugMode" from zero to $80 to be able
  347. to differentiate between invalid memory accesses using nil pointers to
  348. invalid memory accesses using fields of freed objects. FastMM tries to
  349. reserve the 64K block starting at $80800000 at startup to ensure that an
  350. A/V will occur when this block is accessed. (Thanks to Alexander Tabakov.)
  351. - Fixed some compiler warnings. (Thanks to M. Skloff)
  352. - Fixed some display bugs in the memory leak report. (Thanks to Leonel
  353. Togniolli.)
  354. - Added a "LogMemoryLeakDetailToFile" option. Some applications leak a lot of
  355. memory and can make the log file grow very large very quickly.
  356. - Added the option to use madExcept instead of the JCL Debug library in the
  357. debug info support DLL. (Thanks to Martin Aignesberger.)
  358. - Added procedures "GetMemoryManagerState" and "GetMemoryMap" to retrieve
  359. statistics about the current state of the memory manager and memory pool.
  360. (A usage tracker form together with a demo is also available.)
  361. Version 4.15 (14 July 2005):
  362. - Fixed a false 4GB(!) memory leak reported in some instances.
  363. Version 4.16 (15 July 2005):
  364. - Added the "CatchUseOfFreedInterfaces" option to catch the use of interfaces
  365. of freed objects. This option is not compatible with checking that a freed
  366. block has not been modified, so enable this option only when hunting an
  367. invalid interface reference. (Only relevant if "FullDebugMode" is set.)
  368. - During shutdown FastMM now checks that all free blocks have not been
  369. modified since being freed. (Only when "FullDebugMode" is set and
  370. "CatchUseOfFreedInterfaces" is disabled.)
  371. Version 4.17 (15 July 2005):
  372. - Added the AddExpectedMemoryLeaks and RemoveExpectedMemoryLeaks procedures to
  373. register/unregister expected leaks, thus preventing the leak report from
  374. displaying if only expected leaks occurred. (Thanks to Diederik and Dennis
  375. Passmore for the suggestion.) (Note: these functions were renamed in later
  376. versions.)
  377. - Fixed the "LogMemoryLeakDetailToFile" not logging memory leak detail to file
  378. as it is supposed to. (Thanks to Leonel Togniolli.)
  379. Version 4.18 (18 July 2005):
  380. - Fixed some issues when range checking or complete boolean evaluation is
  381. switched on. (Thanks to Dario Tiraboschi and Mark Gebauer.)
  382. - Added the "OutputInstallUninstallDebugString" option to display a message when
  383. FastMM is installed or uninstalled. (Thanks to Hanspeter Widmer.)
  384. - Moved the options to a separate include file. (Thanks to Hanspeter Widmer.)
  385. - Moved message strings to a separate file for easy translation.
  386. Version 4.19 (19 July 2005):
  387. - Fixed Kylix support that was broken in 4.14.
  388. Version 4.20 (20 July 2005):
  389. - Fixed a false memory overwrite report at shutdown in "FullDebugMode". If you
  390. consistently got a "Block Header Has Been Corrupted" error message during
  391. shutdown at address $xxxx0070 then it was probably a false alarm. (Thanks to
  392. Theo Carr-Brion and Hanspeter Widmer.}
  393. Version 4.21 (27 July 2005):
  394. - Minor change to the block header flags to make it possible to immediately
  395. tell whether a medium block is being used as a small block pool or not.
  396. (Simplifies the leak checking and status reporting code.)
  397. - Expanded the functionality around the management of expected memory leaks.
  398. - Added the "ClearLogFileOnStartup" option. Deletes the log file during
  399. initialization. (Thanks to M. Skloff.)
  400. - Changed "OutputInstallUninstallDebugString" to use OutputDebugString instead
  401. of MessageBox. (Thanks to Hanspeter Widmer.)
  402. Version 4.22 (1 August 2005):
  403. - Added a FastAllocMem function that avoids an unnecessary FillChar call with
  404. large blocks.
  405. - Changed large block resizing behavior to be a bit more conservative. Large
  406. blocks will be downsized if the new size is less than half of the old size
  407. (the threshold was a quarter previously).
  408. Version 4.23 (6 August 2005):
  409. - Fixed BCB6 support (Thanks to Omar Zelaya).
  410. - Renamed "OutputInstallUninstallDebugString" to "UseOutputDebugString", and
  411. added debug string output on memory leak or error detection.
  412. Version 4.24 (11 August 2005):
  413. - Added the "NoMessageBoxes" option to suppress the display of message boxes,
  414. which is useful for services that should not be interrupted. (Thanks to Dan
  415. Miser).
  416. - Changed the stack trace code to return the line number of the caller and not
  417. the line number of the return address. (Thanks to Dan Miser).
  418. Version 4.25 (15 August 2005):
  419. - Fixed GetMemoryLeakType not detecting expected leaks registered by class
  420. when in "FullDebugMode". (Thanks to Arjen de Ruijter).
  421. Version 4.26 (18 August 2005):
  422. - Added a "UseRuntimePackages" option that allows FastMM to be used in a main
  423. application together with DLLs that all use runtime packages. (Thanks to
  424. Aleksander Oven.)
  425. Version 4.27 (24 August 2005):
  426. - Fixed a bug that sometimes caused the leak report to be shown even though all
  427. leaks were registered as expected leaks. (Thanks to Kristofer Skaug.)
  428. Version 4.29 (30 September 2005):
  429. - Added the "RequireDebuggerPresenceForLeakReporting" option to only display
  430. the leak report if the application is run inside the IDE. (Thanks to Günther
  431. Schoch.)
  432. - Added the "ForceMMX" option, which when disabled will check the CPU for
  433. MMX compatibility before using MMX. (Thanks to Jan Schlüter.)
  434. - Added the module name to the title of error dialogs to more easily identify
  435. which application caused the error. (Thanks to Kristofer Skaug.)
  436. - Added an ASCII dump to the "FullDebugMode" memory dumps. (Thanks to Hallvard
  437. Vassbotn.)
  438. - Added the option "HideExpectedLeaksRegisteredByPointer" to suppress the
  439. display and logging of expected memory leaks that were registered by pointer.
  440. (Thanks to Dan Miser.) Leaks registered by size or class are often ambiguous,
  441. so these expected leaks are always logged to file (in FullDebugMode) and are
  442. never hidden from the leak display (only displayed if there is at least one
  443. unexpected leak).
  444. - Added a procedure "GetRegisteredMemoryLeaks" to return a list of all
  445. registered memory leaks. (Thanks to Dan Miser.)
  446. - Added the "RawStackTraces" option to perform "raw" stack traces, negating
  447. the need for stack frames. This will usually result in more complete stack
  448. traces in FullDebugMode error reports, but it is significantly slower.
  449. (Thanks to Hallvard Vassbotn, Dan Miser and the JCL team.)
  450. Version 4.31 (2 October 2005):
  451. - Fixed the crash bug when both "RawStackTraces" and "FullDebugMode" were
  452. enabled. (Thanks to Dan Miser and Mark Edington.)
  453. Version 4.33 (6 October 2005):
  454. - Added a header corruption check to all memory blocks that are identified as
  455. leaks in FullDebugMode. This allows better differentiation between memory
  456. pool corruption bugs and actual memory leaks.
  457. - Fixed the stack overflow bug when using "RawStackTraces".
  458. Version 4.35 (6 October 2005):
  459. - Fixed a compilation error when the "NoMessageBoxes" option is set. (Thanks
  460. to Paul Ishenin.)
  461. - Before performing a "raw" stack trace, FastMM now checks whether exception
  462. handling is in place. If exception handling is not in place FastMM falls
  463. back to stack frame tracing. (Exception handling is required to handle the
  464. possible A/Vs when reading invalid call addresses. Exception handling is
  465. usually always available except when SysUtils hasn't been initialized yet or
  466. after SysUtils has been finalized.)
  467. Version 4.37 (8 October 2005):
  468. - Fixed the missing call stack trace entry issue when dynamically loading DLLs.
  469. (Thanks to Paul Ishenin.)
  470. Version 4.39 (12 October 2005):
  471. - Restored the performance with "RawStackTraces" enabled back to the level it
  472. was in 4.35.
  473. - Fixed the stack overflow error when using "RawStackTraces" that I thought I
  474. had fixed in 4.31, but unfortunately didn't. (Thanks to Craig Peterson.)
  475. Version 4.40 (13 October 2005):
  476. - Improved "RawStackTraces" to have less incorrect extra entries. (Thanks to
  477. Craig Peterson.)
  478. - Added the Russian (by Paul Ishenin) and Afrikaans translations of
  479. FastMM4Messages.pas.
  480. Version 4.42 (13 October 2005):
  481. - Fixed the compilation error when "CatchUseOfFreedInterfaces" is enabled.
  482. (Thanks to Cristian Nicola.)
  483. Version 4.44 (25 October 2005):
  484. - Implemented a FastGetHeapStatus function in analogy with GetHeapStatus.
  485. (Suggested by Cristian Nicola.)
  486. - Shifted more of the stack trace code over to the support dll to allow third
  487. party vendors to make available their own stack tracing and stack trace
  488. logging facilities.
  489. - Mathias Rauen (madshi) improved the support for madExcept in the debug info
  490. support DLL. Thanks!
  491. - Added support for BCB5. (Thanks to Roddy Pratt.)
  492. - Added the Czech translation by Rene Mihula.
  493. - Added the "DetectMMOperationsAfterUninstall" option. This will catch
  494. attempts to use the MM after FastMM has been uninstalled, and is useful for
  495. debugging.
  496. Version 4.46 (26 October 2005):
  497. - Renamed FastMM_DebugInfo.dll to FastMM_FullDebugMode.dll and made the
  498. dependency on this library a static one. This solves a DLL unload order
  499. problem when using FullDebugMode together with the replacement
  500. borlndmm.dll. (Thanks to Bart van der Werf.)
  501. - Added the Polish translation by Artur Redzko.
  502. Version 4.48 (10 November 2005):
  503. - Fixed class detection for objects leaked in dynamically loaded DLLs that
  504. were relocated.
  505. - Fabio Dell'Aria implemented support for EurekaLog in the FullDebugMode
  506. support DLL. Thanks!
  507. - Added the Spanish translation by JRG ("The Delphi Guy").
  508. Version 4.49 (10 November 2005):
  509. - Implemented support for installing replacement AllocMem and leak
  510. registration mechanisms for Delphi/BCB versions that support it.
  511. - Added support for Delphi 4. (Thanks to Justus Janssen.)
  512. Version 4.50 (5 December 2005):
  513. - Renamed the ReportMemoryLeaks global variable to ReportMemoryLeaksOnShutdown
  514. to be more consistent with the Delphi 2006 memory manager.
  515. - Improved the handling of large blocks. Large blocks can now consist of
  516. several consecutive segments allocated through VirtualAlloc. This
  517. significantly improves speed when frequently resizing large blocks, since
  518. these blocks can now often be upsized in-place.
  519. Version 4.52 (7 December 2005):
  520. - Fixed the compilation error with Delphi 5. (Thanks to Vadim Lopushansky and
  521. Charles Vinal for reporting the error.)
  522. Version 4.54 (15 December 2005):
  523. - Added the Brazilian Portuguese translation by Johni Jeferson Capeletto.
  524. - Fixed the compilation error with BCB6. (Thanks to Kurt Fitzner.)
  525. Version 4.56 (20 December 2005):
  526. - Fixed the Kylix compilation problem. (Thanks to Michal Niklas.)
  527. Version 4.58 (1 February 2006):
  528. - Added the German translations by Thomas Speck and Uwe Queisser.
  529. - Added the Indonesian translation by Zaenal Mutaqin.
  530. - Added the Portuguese translation by Carlos Macao.
  531. Version 4.60 (21 February 2006):
  532. - Fixed a performance issue due to an unnecessary block move operation when
  533. allocating a block in the range 1261-1372 bytes and then reallocating it in
  534. the range 1373-1429 bytes twice. (Thanks to Michael Winter.)
  535. - Added the Belarussian translation by dzmitry[li].
  536. - Added the updated Spanish translation by Marcelo Montenegro.
  537. - Added a new option "EnableSharingWithDefaultMM". This option allows FastMM
  538. to be shared with the default MM of Delphi 2006. It is on by default, but
  539. MM sharing has to be enabled otherwise it has no effect (refer to the
  540. documentation for the "ShareMM" and "AttemptToUseSharedMM" options).
  541. Version 4.62 (22 February 2006):
  542. - Fixed a possible read access violation in the MoveX16LP routine when the
  543. UseCustomVariableSizeMoveRoutines option is enabled. (Thanks to Jud Cole for
  544. some great detective work in finding this bug.)
  545. - Improved the downsizing behaviour of medium blocks to better correlate with
  546. the reallocation behaviour of small blocks. This change reduces the number
  547. of transitions between small and medium block types when reallocating blocks
  548. in the 0.7K to 2.6K range. It cuts down on the number of memory move
  549. operations and improves performance.
  550. Version 4.64 (31 March 2006):
  551. - Added the following functions for use with FullDebugMode (and added the
  552. exports to the replacement BorlndMM.dll): SetMMLogFileName,
  553. GetCurrentAllocationGroup, PushAllocationGroup, PopAllocationGroup and
  554. LogAllocatedBlocksToFile. The purpose of these functions are to allow you to
  555. identify and log related memory leaks while your application is still
  556. running.
  557. - Fixed a bug in the memory manager sharing mechanism affecting Windows
  558. 95/98/ME. (Thanks to Zdenek Vasku.)
  559. Version 4.66 (9 May 2006):
  560. - Added a hint comment in this file so that FastMM4Messages.pas will also be
  561. backed up by GExperts. (Thanks to RB Winston.)
  562. - Fixed a bug affecting large address space (> 2GB) support under
  563. FullDebugMode. (Thanks to Thomas Schulz.)
  564. Version 4.68 (3 July 2006):
  565. - Added the Italian translation by Luigi Sandon.
  566. - If FastMM is used inside a DLL it will now use the name of the DLL as base
  567. for the log file name. (Previously it always used the name of the main
  568. application executable file.)
  569. - Fixed a rare A/V when both the FullDebugMode and RawStackTraces options were
  570. enabled. (Thanks to Primoz Gabrijelcic.)
  571. - Added the "NeverSleepOnThreadContention" option. This option may improve
  572. performance if the ratio of the the number of active threads to the number
  573. of CPU cores is low (typically < 2). This option is only useful for 4+ CPU
  574. systems, it almost always hurts performance on single and dual CPU systems.
  575. (Thanks to Werner Bochtler and Markus Beth.)
  576. Version 4.70 (4 August 2006):
  577. - Added the Simplified Chinese translation by JiYuan Xie.
  578. - Added the updated Russian as well as the Ukrainian translation by Andrey
  579. Shtukaturov.
  580. - Fixed two bugs in the leak class detection code that would sometimes fail
  581. to detect the class of leaked objects and strings, and report them as
  582. 'unknown'. (Thanks to Dimitry Timokhov)
  583. Version 4.72 (24 September 2006):
  584. - Fixed a bug that caused AllocMem to not clear blocks > 256K in
  585. FullDebugMode. (Thanks to Paulo Moreno.)
  586. Version 4.74 (9 November 2006):
  587. - Fixed a bug in the segmented large block functionality that could lead to
  588. an application freeze when upsizing blocks greater than 256K in a
  589. multithreaded application (one of those "what the heck was I thinking?"
  590. type bugs).
  591. Version 4.76 (12 January 2007):
  592. - Changed the RawStackTraces code in the FullDebugMode DLL
  593. to prevent it from modifying the Windows "GetLastError" error code.
  594. (Thanks to Primoz Gabrijelcic.)
  595. - Fixed a threading issue when the "CheckHeapForCorruption" option was
  596. enabled, but the "FullDebugMode" option was disabled. (Thanks to Primoz
  597. Gabrijelcic.)
  598. - Removed some unnecessary startup code when the MM sharing mechanism is
  599. disabled. (Thanks to Vladimir Bochkarev.)
  600. - In FullDebugMode leaked blocks would sometimes be reported as belonging to
  601. the class "TFreedObject" if they were allocated but never used. Such blocks
  602. will now be reported as "unknown". (Thanks to Francois Malan.)
  603. - In recent versions the replacement borlndmm.dll created a log file (when
  604. enabled) that used the "borlndmm" prefix instead of the application name.
  605. It is now fixed to use the application name, however if FastMM is used
  606. inside other DLLs the name of those DLLs will be used. (Thanks to Bart van
  607. der Werf.)
  608. - Added a "FastMMVersion" constant. (Suggested by Loris Luise.)
  609. - Fixed an issue with error message boxes not displaying under certain
  610. configurations. (Thanks to J.W. de Bokx.)
  611. - FastMM will now display only one error message at a time. If many errors
  612. occur in quick succession, only the first error will be shown (but all will
  613. be logged). This avoids a stack overflow with badly misbehaved programs.
  614. (Thanks to Bart van der Werf.)
  615. - Added a LoadDebugDLLDynamically option to be used in conjunction with
  616. FullDebugMode. In this mode FastMM_FullDebugMode.dll is loaded dynamically.
  617. If the DLL cannot be found, stack traces will not be available. (Thanks to
  618. Rene Mihula.)
  619. Version 4.78 (1 March 2007):
  620. - The MB_DEFAULT_DESKTOP_ONLY constant that is used when displaying messages
  621. boxes since 4.76 is not defined under Kylix, and the source would thus not
  622. compile. That constant is now defined. (Thanks to Werner Bochtler.)
  623. - Moved the medium block locking code that was duplicated in several places
  624. to a subroutine to reduce code size. (Thanks to Hallvard Vassbotn.)
  625. - Fixed a bug in the leak registration code that sometimes caused registered
  626. leaks to be reported erroneously. (Thanks to Primoz Gabrijelcic.)
  627. - Added the NoDebugInfo option (on by default) that suppresses the generation
  628. of debug info for the FastMM4.pas unit. This will prevent the integrated
  629. debugger from stepping into the memory manager. (Thanks to Primoz
  630. Gabrijelcic.)
  631. - Increased the default stack trace depth in FullDebugMode from 9 to 10 to
  632. ensure that the Align16Bytes setting works in FullDebugMode. (Thanks to
  633. Igor Lindunen.)
  634. - Updated the Czech translation. (Thanks to Rene Mihula.)
  635. Version 4.84 (7 July 2008):
  636. - Added the Romanian translation. (Thanks to Ionut Muntean.)
  637. - Optimized the GetMemoryMap procedure to improve speed.
  638. - Added the GetMemoryManagerUsageSummary function that returns a summary of
  639. the GetMemoryManagerState call. (Thanks to Hallvard Vassbotn.)
  640. - Added the French translation. (Thanks to Florent Ouchet.)
  641. - Added the "AlwaysAllocateTopDown" FullDebugMode option to help with
  642. catching bad pointer arithmetic code in an address space > 2GB. This option
  643. is enabled by default.
  644. - Added the "InstallOnlyIfRunningInIDE" option. Enable this option to
  645. only install FastMM as the memory manager when the application is run
  646. inside the Delphi IDE. This is useful when you want to deploy the same EXE
  647. that you use for testing, but only want the debugging features active on
  648. development machines. When this option is enabled and the application is
  649. not being run inside the IDE, then the default Delphi memory manager will
  650. be used (which, since Delphi 2006, is FastMM without FullDebugMode.) This
  651. option is off by default.
  652. - Added the "FullDebugModeInIDE" option. This is a convenient shorthand for
  653. enabling FullDebugMode, InstallOnlyIfRunningInIDE and
  654. LoadDebugDLLDynamically. This causes FastMM to be used in FullDebugMode
  655. when the application is being debugged on development machines, and the
  656. default memory manager when the same executable is deployed. This allows
  657. the debugging and deployment of an application without having to compile
  658. separate executables. This option is off by default.
  659. - Added a ScanMemoryPoolForCorruptions procedure that checks the entire
  660. memory pool for corruptions and raises an exception if one is found. It can
  661. be called at any time, but is only available in FullDebugMode. (Thanks to
  662. Marcus Mönnig.)
  663. - Added a global variable "FullDebugModeScanMemoryPoolBeforeEveryOperation".
  664. When this variable is set to true and FullDebugMode is enabled, then the
  665. entire memory pool is checked for consistency before every GetMem, FreeMem
  666. and ReallocMem operation. An "Out of Memory" error is raised if a
  667. corruption is found (and this variable is set to false to prevent recursive
  668. errors). This obviously incurs a massive performance hit, so enable it only
  669. when hunting for elusive memory corruption bugs. (Thanks to Marcus Mönnig.)
  670. - Fixed a bug in AllocMem that caused the FPU stack to be shifted by one
  671. position.
  672. - Changed the default for option "EnableMMX" to false, since using MMX may
  673. cause unexpected behaviour in code that passes parameters on the FPU stack
  674. (like some "compiler magic" routines, e.g. VarFromReal).
  675. - Removed the "EnableSharingWithDefaultMM" option. This is now the default
  676. behaviour and cannot be disabled. (FastMM will always try to share memory
  677. managers between itself and the default memory manager when memory manager
  678. sharing is enabled.)
  679. - Introduced a new memory manager sharing mechanism based on memory mapped
  680. files. This solves compatibility issues with console and service
  681. applications. This sharing mechanism currently runs in parallel with the
  682. old mechanism, but the old mechanism can be disabled by undefining
  683. "EnableBackwardCompatibleMMSharing" in FastMM4Options.inc.
  684. - Fixed the recursive call error when the EnableMemoryLeakReporting option
  685. is disabled and an attempt is made to register a memory leak under Delphi
  686. 2006 or later. (Thanks to Thomas Schulz.)
  687. - Added a global variable "SuppressMessageBoxes" to enable or disable
  688. messageboxes at runtime. (Thanks to Craig Peterson.)
  689. - Added the leak reporting code for C++ Builder, as well as various other
  690. C++ Builder bits written by JiYuan Xie. (Thank you!)
  691. - Added the new Usage Tracker written by Hanspeter Widmer. (Thank you!)
  692. Version 4.86 (31 July 2008):
  693. - Tweaked the string detection algorithm somewhat to be less strict, and
  694. allow non-class leaks to be more often categorized as strings.
  695. - Fixed a compilation error under Delphi 5.
  696. - Made LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions thread
  697. safe. (Thanks to Francois Piette.)
  698. Version 4.88 (13 August 2008):
  699. - Fixed compiler warnings in NoOpRegisterExpectedMemoryLeak and
  700. NoOpUnRegisterExpectedMemoryLeak. (Thanks to Michael Rabatscher.)
  701. - Added the Simplified Chinese translation of FastMM4Options.inc by
  702. QianYuan Wang. (Thank you!)
  703. - Included the updated C++ Builder files with support for BCB6 without
  704. update 4 applied. (Submitted by JiYuan Xie. Thanks!)
  705. - Fixed a compilation error under Delphi 5.
  706. - Made LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions thread
  707. safe - for real this time. (Thanks to Francois Piette.)
  708. Version 4.90 (9 September 2008):
  709. - Added logging of the thread ID when capturing and displaying stack
  710. traces. (Suggested by Allen Bauer and Mark Edington.)
  711. - Fixed a Delphi 5 compiler error under FullDebugMode. (Thanks to Maurizio
  712. Lotauro and Christian-W. Budde.)
  713. - Changed a default setting in FastMM4Options.inc: RawStackTraces is now
  714. off by default due to the high number of support requests I receive with
  715. regards to the false postives it may cause. I recommend compiling debug
  716. builds of applications with the "Stack Frames" option enabled.
  717. - Fixed a compilation error under Kylix. (Thanks to Werner Bochtler.)
  718. - Official support for Delphi 2009.
  719. Version 4.92 (25 November 2008):
  720. - Added the DisableLoggingOfMemoryDumps option under FullDebugMode. When
  721. this option is set, memory dumps will not be logged for memory leaks or
  722. errors. (Thanks to Patrick van Logchem.)
  723. - Exposed the class and string type detection code in the interface section
  724. for use in application code (if required). (Requested by Patrick van
  725. Logchem.)
  726. - Fixed a bug in SetMMLogFileName that could cause the log file name to be
  727. set incorrectly.
  728. - Added BCB4 support. (Thanks to Norbert Spiegel.)
  729. - Included the updated Czech translation by Rene Mihula.
  730. - When FastMM raises an error due to a freed block being modified, it now
  731. logs detail about which bytes in the block were modified.
  732. Version 4.94 (28 August 2009):
  733. - Added the DoNotInstallIfDLLMissing option that prevents FastMM from
  734. installing itself if the FastMM_FullDebugMode.dll library is not
  735. available. (Only applicable when FullDebugMode and LoadDebugDLLDynamically
  736. are both enabled.) This is useful when the same executable will be used for
  737. both debugging and deployment - when the debug support DLL is available
  738. FastMM will be installed in FullDebugMode, and otherwise the default memory
  739. manager will be used.
  740. - Added the FullDebugModeWhenDLLAvailable option that combines the
  741. FullDebugMode, LoadDebugDLLDynamically and DoNotInstallIfDLLMissing options.
  742. - Re-enabled RawStackTraces by default. The frame based stack traces (even
  743. when compiling with stack frames enabled) are generally too incomplete.
  744. - Improved the speed of large block operations under FullDebugMode: Since
  745. large blocks are never reused, there is no point in clearing them before
  746. and after use (so it does not do that anymore).
  747. - If an error occurs in FullDebugMode and FastMM is unable to append to the
  748. log file, it will attempt to write to a log file of the same name in the
  749. "My Documents" folder. This feature is helpful when the executable resides
  750. in a read-only location and the default log file, which is derived from the
  751. executable name, would thus not be writeable.
  752. - Added support for controlling the error log file location through an
  753. environment variable. If the 'FastMMLogFilePath' environment variable is
  754. set then any generated error logs will be written to the specified folder
  755. instead of the default location (which is the same folder as the
  756. application).
  757. - Improved the call instruction detection code in the FastMM_FullDebugMode
  758. library. (Thanks to the JCL team.)
  759. - Improved the string leak detection and reporting code. (Thanks to Uwe
  760. Schuster.)
  761. - New FullDebugMode feature: Whenever FreeMem or ReallocMem is called, FastMM
  762. will check that the block was actually allocated through the same FastMM
  763. instance. This is useful for tracking down memory manager sharing issues.
  764. - Compatible with Delphi 2010.
  765. Version 4.96 (31 August 2010):
  766. - Reduced the minimum block size to 4 bytes from the previous value of 12
  767. bytes (only applicable to 8 byte alignment). This reduces memory usage if
  768. the application allocates many blocks <= 4 bytes in size.
  769. - Added colour-coded change indication to the FastMM usage tracker, making
  770. it easier to spot changes in the memory usage grid. (Thanks to Murray
  771. McGowan.)
  772. - Added the SuppressFreeMemErrorsInsideException FullDebugMode option: If
  773. FastMM encounters a problem with a memory block inside the FullDebugMode
  774. FreeMem handler then an "invalid pointer operation" exception will usually
  775. be raised. If the FreeMem occurs while another exception is being handled
  776. (perhaps in the try.. finally code) then the original exception will be
  777. lost. With this option set FastMM will ignore errors inside FreeMem when an
  778. exception is being handled, thus allowing the original exception to
  779. propagate. This option is on by default. (Thanks to Michael Hieke.)
  780. - Fixed Windows 95 FullDebugMode support that was broken in 4.94. (Thanks to
  781. Richard Bradbrook.)
  782. - Fixed a bug affecting GetMemoryMap performance and accuracy of measurements
  783. above 2GB if a large address space is not enabled for the project. (Thanks
  784. to Michael Hieke.)
  785. - Added the FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak boolean flag.
  786. When set, all allocations are automatically registered as expected memory
  787. leaks. Only available in FullDebugMode. (Thanks to Brian Cook.)
  788. - Compatible with Delphi XE.
  789. Version 4.97 (30 September 2010):
  790. - Fixed a crash bug (that crept in in 4.96) that may manifest itself when
  791. resizing a block to 4 bytes or less.
  792. - Added the UseSwitchToThread option. Set this option to call SwitchToThread
  793. instead of sitting in a "busy waiting" loop when a thread contention
  794. occurs. This is used in conjunction with the NeverSleepOnThreadContention
  795. option, and has no effect unless NeverSleepOnThreadContention is also
  796. defined. This option may improve performance with many CPU cores and/or
  797. threads of different priorities. Note that the SwitchToThread API call is
  798. only available on Windows 2000 and later. (Thanks to Zach Saw.)
  799. Version 4.98 (23 September 2011):
  800. - Added the FullDebugModeCallBacks define which adds support for memory
  801. manager event callbacks. This allows the application to be notified of
  802. memory allocations, frees and reallocations as they occur. (Thanks to
  803. Jeroen Pluimers.)
  804. - Added security options ClearMemoryBeforeReturningToOS and
  805. AlwaysClearFreedMemory to force the clearing of memory blocks after being
  806. freed. This could possibly provide some protection against information
  807. theft, but at a significant performance penalty. (Thanks to Andrey
  808. Sozonov.)
  809. - Shifted the code in the initialization section to a procedure
  810. RunInitializationCode. This allows the startup code to be called before
  811. InitUnits, which is required by some software protection tools.
  812. - Added support for Delphi XE2 (Windows 32-bit and Windows 64-bit platforms
  813. only).
  814. Version 4.99 (6 November 2011):
  815. - Fixed crashes in the 64-bit BASM codepath when more than 4GB of memory is
  816. allocated.
  817. - Fixed bad record alignment under 64-bit that affected performance.
  818. - Fixed compilation errors with some older compilers.
  819. Version 4.991 (3 September 2012)
  820. - Added the LogMemoryManagerStateToFile call. This call logs a summary of
  821. the memory manager state to file: The total allocated memory, overhead,
  822. efficiency, and a breakdown of allocated memory by class and string type.
  823. This call may be useful to catch objects that do not necessarily leak, but
  824. do linger longer than they should.
  825. - OS X support added by Sebastian Zierer
  826. - Compatible with Delphi XE3
  827. *)
  828. unit FastMM4;
  829. interface
  830. {$Include FastMM4Options.inc}
  831. {$RANGECHECKS OFF}
  832. {$BOOLEVAL OFF}
  833. {$OVERFLOWCHECKS OFF}
  834. {$OPTIMIZATION ON}
  835. {$TYPEDADDRESS OFF}
  836. {$LONGSTRINGS ON}
  837. {Compiler version defines}
  838. {$ifndef BCB}
  839. {$ifdef ver120}
  840. {$define Delphi4or5}
  841. {$endif}
  842. {$ifdef ver130}
  843. {$define Delphi4or5}
  844. {$endif}
  845. {$ifdef ver140}
  846. {$define Delphi6}
  847. {$endif}
  848. {$ifdef ver150}
  849. {$define Delphi7}
  850. {$endif}
  851. {$ifdef ver170}
  852. {$define Delphi2005}
  853. {$endif}
  854. {$else}
  855. {for BCB4, use the Delphi 5 codepath}
  856. {$ifdef ver120}
  857. {$define Delphi4or5}
  858. {$define BCB4}
  859. {$endif}
  860. {for BCB5, use the Delphi 5 codepath}
  861. {$ifdef ver130}
  862. {$define Delphi4or5}
  863. {$endif}
  864. {$endif}
  865. {$ifdef ver180}
  866. {$define BDS2006}
  867. {$endif}
  868. {$define 32Bit}
  869. {$ifndef Delphi4or5}
  870. {$if SizeOf(Pointer) = 8}
  871. {$define 64Bit}
  872. {$undef 32Bit}
  873. {$ifend}
  874. {$if CompilerVersion >= 23}
  875. {$define XE2AndUp}
  876. {$ifend}
  877. {$define BCB6OrDelphi6AndUp}
  878. {$ifndef BCB}
  879. {$define Delphi6AndUp}
  880. {$endif}
  881. {$ifndef Delphi6}
  882. {$define BCB6OrDelphi7AndUp}
  883. {$ifndef BCB}
  884. {$define Delphi7AndUp}
  885. {$endif}
  886. {$ifndef BCB}
  887. {$ifndef Delphi7}
  888. {$ifndef Delphi2005}
  889. {$define BDS2006AndUp}
  890. {$endif}
  891. {$endif}
  892. {$endif}
  893. {$endif}
  894. {$endif}
  895. {$ifdef 64Bit}
  896. {Under 64 bit memory blocks must always be 16-byte aligned}
  897. {$define Align16Bytes}
  898. {No need for MMX under 64-bit, since SSE2 is available}
  899. {$undef EnableMMX}
  900. {There is little need for raw stack traces under 64-bit, since frame based
  901. stack traces are much more accurate than under 32-bit. (And frame based
  902. stack tracing is much faster.)}
  903. {$undef RawStackTraces}
  904. {$endif}
  905. {IDE debug mode always enables FullDebugMode and dynamic loading of the FullDebugMode DLL.}
  906. {$ifdef FullDebugModeInIDE}
  907. {$define InstallOnlyIfRunningInIDE}
  908. {$define FullDebugMode}
  909. {$define LoadDebugDLLDynamically}
  910. {$endif}
  911. {Install in FullDebugMode only when the DLL is available?}
  912. {$ifdef FullDebugModeWhenDLLAvailable}
  913. {$define FullDebugMode}
  914. {$define LoadDebugDLLDynamically}
  915. {$define DoNotInstallIfDLLMissing}
  916. {$endif}
  917. {$ifdef Linux}
  918. {$define POSIX}
  919. {$endif}
  920. {Some features not currently supported under Kylix / OS X}
  921. {$ifdef POSIX}
  922. {$undef FullDebugMode}
  923. {$undef LogErrorsToFile}
  924. {$undef LogMemoryLeakDetailToFile}
  925. {$undef ShareMM}
  926. {$undef AttemptToUseSharedMM}
  927. {$undef RequireIDEPresenceForLeakReporting}
  928. {$undef UseOutputDebugString}
  929. {$ifdef PIC}
  930. {BASM version does not support position independent code}
  931. {$undef ASMVersion}
  932. {$endif}
  933. {$endif}
  934. {Do we require debug info for leak checking?}
  935. {$ifdef RequireDebugInfoForLeakReporting}
  936. {$ifopt D-}
  937. {$undef EnableMemoryLeakReporting}
  938. {$endif}
  939. {$endif}
  940. {Enable heap checking and leak reporting in full debug mode}
  941. {$ifdef FullDebugMode}
  942. {$STACKFRAMES ON}
  943. {$define CheckHeapForCorruption}
  944. {$ifndef CatchUseOfFreedInterfaces}
  945. {$define CheckUseOfFreedBlocksOnShutdown}
  946. {$endif}
  947. {$else}
  948. {Error logging requires FullDebugMode}
  949. {$undef LogErrorsToFile}
  950. {$undef CatchUseOfFreedInterfaces}
  951. {$undef RawStackTraces}
  952. {$undef AlwaysAllocateTopDown}
  953. {$endif}
  954. {Set defines for security options}
  955. {$ifdef FullDebugMode}
  956. {In FullDebugMode small and medium blocks are always cleared when calling
  957. FreeMem. Large blocks are always returned to the OS immediately.}
  958. {$ifdef ClearMemoryBeforeReturningToOS}
  959. {$define ClearLargeBlocksBeforeReturningToOS}
  960. {$endif}
  961. {$ifdef AlwaysClearFreedMemory}
  962. {$define ClearLargeBlocksBeforeReturningToOS}
  963. {$endif}
  964. {$else}
  965. {If memory blocks are cleared in FreeMem then they do not need to be cleared
  966. before returning the memory to the OS.}
  967. {$ifdef AlwaysClearFreedMemory}
  968. {$define ClearSmallAndMediumBlocksInFreeMem}
  969. {$define ClearLargeBlocksBeforeReturningToOS}
  970. {$else}
  971. {$ifdef ClearMemoryBeforeReturningToOS}
  972. {$define ClearMediumBlockPoolsBeforeReturningToOS}
  973. {$define ClearLargeBlocksBeforeReturningToOS}
  974. {$endif}
  975. {$endif}
  976. {$endif}
  977. {Only the Pascal version supports extended heap corruption checking.}
  978. {$ifdef CheckHeapForCorruption}
  979. {$undef ASMVersion}
  980. {$endif}
  981. {For BASM bits that are not implemented in 64-bit.}
  982. {$ifdef 32Bit}
  983. {$ifdef ASMVersion}
  984. {$define Use32BitAsm}
  985. {$endif}
  986. {$endif}
  987. {$ifdef UseRuntimePackages}
  988. {$define AssumeMultiThreaded}
  989. {$endif}
  990. {$ifdef BCB6OrDelphi6AndUp}
  991. {$WARN SYMBOL_PLATFORM OFF}
  992. {$WARN SYMBOL_DEPRECATED OFF}
  993. {$endif}
  994. {Leak detail logging requires error logging}
  995. {$ifndef LogErrorsToFile}
  996. {$undef LogMemoryLeakDetailToFile}
  997. {$undef ClearLogFileOnStartup}
  998. {$endif}
  999. {$ifndef EnableMemoryLeakReporting}
  1000. {Manual leak reporting control requires leak reporting to be enabled}
  1001. {$undef ManualLeakReportingControl}
  1002. {$endif}
  1003. {$ifndef EnableMMX}
  1004. {$undef ForceMMX}
  1005. {$endif}
  1006. {Are any of the MM sharing options enabled?}
  1007. {$ifdef ShareMM}
  1008. {$define MMSharingEnabled}
  1009. {$endif}
  1010. {$ifdef AttemptToUseSharedMM}
  1011. {$define MMSharingEnabled}
  1012. {$endif}
  1013. {Instruct GExperts to back up the messages file as well.}
  1014. {#BACKUP FastMM4Messages.pas}
  1015. {Should debug info be disabled?}
  1016. {$ifdef NoDebugInfo}
  1017. {$DEBUGINFO OFF}
  1018. {$endif}
  1019. {$ifdef BCB}
  1020. {$ifdef borlndmmdll}
  1021. {$OBJEXPORTALL OFF}
  1022. {$endif}
  1023. {$ifndef PatchBCBTerminate}
  1024. {Cannot uninstall safely under BCB}
  1025. {$define NeverUninstall}
  1026. {Disable memory leak reporting}
  1027. {$undef EnableMemoryLeakReporting}
  1028. {$endif}
  1029. {$endif}
  1030. {-------------------------Public constants-----------------------------}
  1031. const
  1032. {The current version of FastMM}
  1033. FastMMVersion = '4.991';
  1034. {The number of small block types}
  1035. {$ifdef Align16Bytes}
  1036. NumSmallBlockTypes = 46;
  1037. {$else}
  1038. NumSmallBlockTypes = 56;
  1039. {$endif}
  1040. {----------------------------Public types------------------------------}
  1041. type
  1042. {Make sure all the required types are available}
  1043. {$ifdef BCB6OrDelphi6AndUp}
  1044. {$if CompilerVersion < 20}
  1045. PByte = PAnsiChar;
  1046. {NativeInt didn't exist or was broken before Delphi 2009.}
  1047. NativeInt = Integer;
  1048. {$ifend}
  1049. {$if CompilerVersion < 21}
  1050. {NativeUInt didn't exist or was broken before Delphi 2010.}
  1051. NativeUInt = Cardinal;
  1052. {$ifend}
  1053. {$if CompilerVersion < 22}
  1054. {PNativeUInt didn't exist before Delphi XE.}
  1055. PNativeUInt = ^Cardinal;
  1056. {$ifend}
  1057. {$if CompilerVersion < 23}
  1058. {IntPtr and UIntPtr didn't exist before Delphi XE2.}
  1059. IntPtr = Integer;
  1060. UIntPtr = Cardinal;
  1061. {$ifend}
  1062. {$else}
  1063. PByte = PAnsiChar;
  1064. NativeInt = Integer;
  1065. NativeUInt = Cardinal;
  1066. PNativeUInt = ^Cardinal;
  1067. IntPtr = Integer;
  1068. UIntPtr = Cardinal;
  1069. {$endif}
  1070. TSmallBlockTypeState = record
  1071. {The internal size of the block type}
  1072. InternalBlockSize: Cardinal;
  1073. {Useable block size: The number of non-reserved bytes inside the block.}
  1074. UseableBlockSize: Cardinal;
  1075. {The number of allocated blocks}
  1076. AllocatedBlockCount: NativeUInt;
  1077. {The total address space reserved for this block type (both allocated and
  1078. free blocks)}
  1079. ReservedAddressSpace: NativeUInt;
  1080. end;
  1081. TSmallBlockTypeStates = array[0..NumSmallBlockTypes - 1] of TSmallBlockTypeState;
  1082. TMemoryManagerState = record
  1083. {Small block type states}
  1084. SmallBlockTypeStates: TSmallBlockTypeStates;
  1085. {Medium block stats}
  1086. AllocatedMediumBlockCount: Cardinal;
  1087. TotalAllocatedMediumBlockSize: NativeUInt;
  1088. ReservedMediumBlockAddressSpace: NativeUInt;
  1089. {Large block stats}
  1090. AllocatedLargeBlockCount: Cardinal;
  1091. TotalAllocatedLargeBlockSize: NativeUInt;
  1092. ReservedLargeBlockAddressSpace: NativeUInt;
  1093. end;
  1094. TMemoryManagerUsageSummary = record
  1095. {The total number of bytes allocated by the application.}
  1096. AllocatedBytes: NativeUInt;
  1097. {The total number of address space bytes used by control structures, or
  1098. lost due to fragmentation and other overhead.}
  1099. OverheadBytes: NativeUInt;
  1100. {The efficiency of the memory manager expressed as a percentage. This is
  1101. 100 * AllocatedBytes / (AllocatedBytes + OverheadBytes).}
  1102. EfficiencyPercentage: Double;
  1103. end;
  1104. {Memory map}
  1105. TChunkStatus = (csUnallocated, csAllocated, csReserved, csSysAllocated,
  1106. csSysReserved);
  1107. TMemoryMap = array[0..65535] of TChunkStatus;
  1108. {$ifdef EnableMemoryLeakReporting}
  1109. {List of registered leaks}
  1110. TRegisteredMemoryLeak = record
  1111. LeakAddress: Pointer;
  1112. LeakedClass: TClass;
  1113. {$ifdef CheckCppObjectTypeEnabled}
  1114. LeakedCppTypeIdPtr: Pointer;
  1115. {$endif}
  1116. LeakSize: NativeInt;
  1117. LeakCount: Integer;
  1118. end;
  1119. TRegisteredMemoryLeaks = array of TRegisteredMemoryLeak;
  1120. {$endif}
  1121. {Used by the DetectStringData routine to detect whether a leaked block
  1122. contains string data.}
  1123. TStringDataType = (stUnknown, stAnsiString, stUnicodeString);
  1124. {The callback procedure for WalkAllocatedBlocks.}
  1125. TWalkAllocatedBlocksCallback = procedure(APBlock: Pointer; ABlockSize: NativeInt; AUserData: Pointer);
  1126. {--------------------------Public variables----------------------------}
  1127. var
  1128. {If this variable is set to true and FullDebugMode is enabled, then the
  1129. entire memory pool is checked for consistency before every memory
  1130. operation. Note that this incurs a massive performance hit on top of
  1131. the already significant FullDebugMode overhead, so enable this option
  1132. only when absolutely necessary.}
  1133. FullDebugModeScanMemoryPoolBeforeEveryOperation: Boolean = False;
  1134. FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak: Boolean = False;
  1135. {$ifdef ManualLeakReportingControl}
  1136. {Variable is declared in system.pas in newer Delphi versions.}
  1137. {$ifndef BDS2006AndUp}
  1138. ReportMemoryLeaksOnShutdown: Boolean;
  1139. {$endif}
  1140. {$endif}
  1141. {If set to True, disables the display of all messageboxes}
  1142. SuppressMessageBoxes: Boolean;
  1143. {-------------------------Public procedures----------------------------}
  1144. {Executes the code normally run in the initialization section. Running it
  1145. earlier may be required with e.g. some software protection tools.}
  1146. procedure RunInitializationCode;
  1147. {Installation procedures must be exposed for the BCB helper unit FastMM4BCB.cpp}
  1148. {$ifdef BCB}
  1149. procedure InitializeMemoryManager;
  1150. function CheckCanInstallMemoryManager: Boolean;
  1151. procedure InstallMemoryManager;
  1152. {$ifdef FullDebugMode}
  1153. (*$HPPEMIT '#define FullDebugMode' *)
  1154. {$ifdef ClearLogFileOnStartup}
  1155. (*$HPPEMIT ' #define ClearLogFileOnStartup' *)
  1156. procedure DeleteEventLog;
  1157. {$endif}
  1158. {$ifdef LoadDebugDLLDynamically}
  1159. (*$HPPEMIT ' #define LoadDebugDLLDynamically' *)
  1160. {$endif}
  1161. {$ifdef RawStackTraces}
  1162. (*$HPPEMIT ' #define RawStackTraces' *)
  1163. {$endif}
  1164. {$endif}
  1165. {$ifdef PatchBCBTerminate}
  1166. (*$HPPEMIT ''#13#10 *)
  1167. (*$HPPEMIT '#define PatchBCBTerminate' *)
  1168. {$ifdef EnableMemoryLeakReporting}
  1169. (*$HPPEMIT ''#13#10 *)
  1170. (*$HPPEMIT '#define EnableMemoryLeakReporting' *)
  1171. {$endif}
  1172. {$ifdef DetectMMOperationsAfterUninstall}
  1173. (*$HPPEMIT ''#13#10 *)
  1174. (*$HPPEMIT '#define DetectMMOperationsAfterUninstall' *)
  1175. {$endif}
  1176. {Called in FastMM4BCB.cpp, should contain codes of original "finalization" section}
  1177. procedure FinalizeMemoryManager;
  1178. {For completion of "RequireDebuggerPresenceForLeakReporting" checking in "FinalizeMemoryManager"}
  1179. var
  1180. pCppDebugHook: ^Integer = nil; //PInteger not defined in BCB5
  1181. {$ifdef CheckCppObjectTypeEnabled}
  1182. (*$HPPEMIT ''#13#10 *)
  1183. (*$HPPEMIT '#define CheckCppObjectTypeEnabled' *)
  1184. type
  1185. TGetCppVirtObjSizeByTypeIdPtrFunc = function(APointer: Pointer): Cardinal;
  1186. TGetCppVirtObjTypeIdPtrFunc = function(APointer: Pointer; ASize: Cardinal): Pointer;
  1187. TGetCppVirtObjTypeNameFunc = function(APointer: Pointer; ASize: Cardinal): PAnsiChar;
  1188. TGetCppVirtObjTypeNameByTypeIdPtrFunc = function (APointer: Pointer): PAnsiChar;
  1189. TGetCppVirtObjTypeNameByVTablePtrFunc = function(AVTablePtr: Pointer; AVTablePtrOffset: Cardinal): PAnsiChar;
  1190. var
  1191. {Return virtual object's size from typeId pointer}
  1192. GetCppVirtObjSizeByTypeIdPtrFunc: TGetCppVirtObjSizeByTypeIdPtrFunc = nil;
  1193. {Retrieve virtual object's typeId pointer}
  1194. GetCppVirtObjTypeIdPtrFunc: TGetCppVirtObjTypeIdPtrFunc = nil;
  1195. {Retrieve virtual object's type name}
  1196. GetCppVirtObjTypeNameFunc: TGetCppVirtObjTypeNameFunc = nil;
  1197. {Return virtual object's type name from typeId pointer}
  1198. GetCppVirtObjTypeNameByTypeIdPtrFunc: TGetCppVirtObjTypeNameByTypeIdPtrFunc = nil;
  1199. {Retrieve virtual object's typeId pointer from it's virtual table pointer}
  1200. GetCppVirtObjTypeNameByVTablePtrFunc: TGetCppVirtObjTypeNameByVTablePtrFunc = nil;
  1201. {$endif}
  1202. {$endif}
  1203. {$endif}
  1204. {$ifndef FullDebugMode}
  1205. {The standard memory manager functions}
  1206. function FastGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  1207. function FastFreeMem(APointer: Pointer): Integer;
  1208. function FastReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  1209. function FastAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  1210. {$else}
  1211. {The FullDebugMode memory manager functions}
  1212. function DebugGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  1213. function DebugFreeMem(APointer: Pointer): Integer;
  1214. function DebugReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  1215. function DebugAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  1216. {Scans the memory pool for any corruptions. If a corruption is encountered an "Out of Memory" exception is
  1217. raised.}
  1218. procedure ScanMemoryPoolForCorruptions;
  1219. {Specify the full path and name for the filename to be used for logging memory
  1220. errors, etc. If ALogFileName is nil or points to an empty string it will
  1221. revert to the default log file name.}
  1222. procedure SetMMLogFileName(ALogFileName: PAnsiChar = nil);
  1223. {Returns the current "allocation group". Whenever a GetMem request is serviced
  1224. in FullDebugMode, the current "allocation group" is stored in the block header.
  1225. This may help with debugging. Note that if a block is subsequently reallocated
  1226. that it keeps its original "allocation group" and "allocation number" (all
  1227. allocations are also numbered sequentially).}
  1228. function GetCurrentAllocationGroup: Cardinal;
  1229. {Allocation groups work in a stack like fashion. Group numbers are pushed onto
  1230. and popped off the stack. Note that the stack size is limited, so every push
  1231. should have a matching pop.}
  1232. procedure PushAllocationGroup(ANewCurrentAllocationGroup: Cardinal);
  1233. procedure PopAllocationGroup;
  1234. {Logs detail about currently allocated memory blocks for the specified range of
  1235. allocation groups. if ALastAllocationGroupToLog is less than
  1236. AFirstAllocationGroupToLog or it is zero, then all allocation groups are
  1237. logged. This routine also checks the memory pool for consistency at the same
  1238. time, raising an "Out of Memory" error if the check fails.}
  1239. procedure LogAllocatedBlocksToFile(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
  1240. {$endif}
  1241. {Releases all allocated memory (use with extreme care)}
  1242. procedure FreeAllMemory;
  1243. {Returns summarised information about the state of the memory manager. (For
  1244. backward compatibility.)}
  1245. function FastGetHeapStatus: THeapStatus;
  1246. {Returns statistics about the current state of the memory manager}
  1247. procedure GetMemoryManagerState(var AMemoryManagerState: TMemoryManagerState);
  1248. {Returns a summary of the information returned by GetMemoryManagerState}
  1249. procedure GetMemoryManagerUsageSummary(
  1250. var AMemoryManagerUsageSummary: TMemoryManagerUsageSummary);
  1251. {$ifndef POSIX}
  1252. {Gets the state of every 64K block in the 4GB address space}
  1253. procedure GetMemoryMap(var AMemoryMap: TMemoryMap);
  1254. {$endif}
  1255. {$ifdef EnableMemoryLeakReporting}
  1256. {Registers expected memory leaks. Returns true on success. The list of leaked
  1257. blocks is limited, so failure is possible if the list is full.}
  1258. function RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
  1259. function RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
  1260. function RegisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
  1261. {$ifdef CheckCppObjectTypeEnabled}
  1262. {Registers expected memory leaks by virtual object's typeId pointer.
  1263. Usage: RegisterExpectedMemoryLeak(typeid(ACppObject).tpp, Count);}
  1264. function RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): boolean; overload;
  1265. {$endif}
  1266. {Removes expected memory leaks. Returns true on success.}
  1267. function UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
  1268. function UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
  1269. function UnregisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
  1270. {$ifdef CheckCppObjectTypeEnabled}
  1271. {Usage: UnregisterExpectedMemoryLeak(typeid(ACppObject).tpp, Count);}
  1272. function UnregisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): boolean; overload;
  1273. {$endif}
  1274. {Returns a list of all expected memory leaks}
  1275. function GetRegisteredMemoryLeaks: TRegisteredMemoryLeaks;
  1276. {$endif}
  1277. {Returns the class for a memory block. Returns nil if it is not a valid class.
  1278. Used by the leak detection code.}
  1279. function DetectClassInstance(APointer: Pointer): TClass;
  1280. {Detects the probable string data type for a memory block. Used by the leak
  1281. classification code when a block cannot be identified as a known class
  1282. instance.}
  1283. function DetectStringData(APMemoryBlock: Pointer;
  1284. AAvailableSpaceInBlock: NativeInt): TStringDataType;
  1285. {Walks all allocated blocks, calling ACallBack for each. Passes the user block size and AUserData to the callback.
  1286. Important note: All block types will be locked during the callback, so the memory manager cannot be used inside it.}
  1287. procedure WalkAllocatedBlocks(ACallBack: TWalkAllocatedBlocksCallback; AUserData: Pointer);
  1288. {Writes a log file containing a summary of the memory mananger state and a summary of allocated blocks grouped by
  1289. class. The file will be saved in UTF-8 encoding (in supported Delphi versions). Returns True on success. }
  1290. function LogMemoryManagerStateToFile(const AFileName: string; const AAdditionalDetails: string = ''): Boolean;
  1291. {$ifdef FullDebugMode}
  1292. {-------------FullDebugMode constants---------------}
  1293. const
  1294. {The stack trace depth. (Must be an *uneven* number to ensure that the
  1295. Align16Bytes option works in FullDebugMode.)}
  1296. StackTraceDepth = 11;
  1297. {The number of entries in the allocation group stack}
  1298. AllocationGroupStackSize = 1000;
  1299. {The number of fake VMT entries - used to track virtual method calls on
  1300. freed objects. Do not change this value without also updating TFreedObject.GetVirtualMethodIndex}
  1301. MaxFakeVMTEntries = 200;
  1302. {The pattern used to fill unused memory}
  1303. DebugFillByte = $80;
  1304. {$ifdef 32Bit}
  1305. DebugFillPattern = $01010101 * Cardinal(DebugFillByte);
  1306. {The address that is reserved so that accesses to the address of the fill
  1307. pattern will result in an A/V. (Not used under 64-bit, since the upper half
  1308. of the address space is always reserved by the OS.)}
  1309. DebugReservedAddress = $01010000 * Cardinal(DebugFillByte);
  1310. {$else}
  1311. DebugFillPattern = $8080808080808080;
  1312. {$endif}
  1313. {-------------------------FullDebugMode structures--------------------}
  1314. type
  1315. PStackTrace = ^TStackTrace;
  1316. TStackTrace = array[0..StackTraceDepth - 1] of NativeUInt;
  1317. TBlockOperation = (boBlockCheck, boGetMem, boFreeMem, boReallocMem);
  1318. {The header placed in front of blocks in FullDebugMode (just after the
  1319. standard header). Must be a multiple of 16 bytes in size otherwise the
  1320. Align16Bytes option will not work. Current size = 128 bytes under 32-bit,
  1321. and 240 bytes under 64-bit.}
  1322. PFullDebugBlockHeader = ^TFullDebugBlockHeader;
  1323. TFullDebugBlockHeader = record
  1324. {Space used by the medium block manager for previous/next block management.
  1325. If a medium block is binned then these two fields will be modified.}
  1326. Reserved1: Pointer;
  1327. Reserved2: Pointer;
  1328. {Is the block currently allocated? If it is allocated this will be the
  1329. address of the getmem routine through which it was allocated, otherwise it
  1330. will be nil.}
  1331. AllocatedByRoutine: Pointer;
  1332. {The allocation group: Can be used in the debugging process to group
  1333. related memory leaks together}
  1334. AllocationGroup: Cardinal;
  1335. {The allocation number: All new allocations are numbered sequentially. This
  1336. number may be useful in memory leak analysis. If it reaches 4G it wraps
  1337. back to 0.}
  1338. AllocationNumber: Cardinal;
  1339. {The call stack when the block was allocated}
  1340. AllocationStackTrace: TStackTrace;
  1341. {The thread that allocated the block}
  1342. AllocatedByThread: Cardinal;
  1343. {The thread that freed the block}
  1344. FreedByThread: Cardinal;
  1345. {The call stack when the block was freed}
  1346. FreeStackTrace: TStackTrace;
  1347. {The user requested size for the block. 0 if this is the first time the
  1348. block is used.}
  1349. UserSize: NativeUInt;
  1350. {The object class this block was used for the previous time it was
  1351. allocated. When a block is freed, the pointer that would normally be in the
  1352. space of the class pointer is copied here, so if it is detected that
  1353. the block was used after being freed we have an idea what class it is.}
  1354. PreviouslyUsedByClass: NativeUInt;
  1355. {The sum of all the dwords(32-bit)/qwords(64-bit) in this structure
  1356. excluding the initial two reserved fields and this field.}
  1357. HeaderCheckSum: NativeUInt;
  1358. end;
  1359. {The NativeUInt following the user area of the block is the inverse of
  1360. HeaderCheckSum. This is used to catch buffer overrun errors.}
  1361. {The class used to catch attempts to execute a virtual method of a freed
  1362. object}
  1363. TFreedObject = class
  1364. public
  1365. procedure GetVirtualMethodIndex;
  1366. procedure VirtualMethodError;
  1367. {$ifdef CatchUseOfFreedInterfaces}
  1368. procedure InterfaceError;
  1369. {$endif}
  1370. end;
  1371. {$ifdef FullDebugModeCallBacks}
  1372. {FullDebugMode memory manager event callbacks. Note that APHeaderFreedBlock in the TOnDebugFreeMemFinish
  1373. will not be valid for large (>260K) blocks.}
  1374. TOnDebugGetMemFinish = procedure(APHeaderNewBlock: PFullDebugBlockHeader; ASize: NativeInt);
  1375. TOnDebugFreeMemStart = procedure(APHeaderBlockToFree: PFullDebugBlockHeader);
  1376. TOnDebugFreeMemFinish = procedure(APHeaderFreedBlock: PFullDebugBlockHeader; AResult: Integer);
  1377. TOnDebugReallocMemStart = procedure(APHeaderBlockToReallocate: PFullDebugBlockHeader; ANewSize: NativeInt);
  1378. TOnDebugReallocMemFinish = procedure(APHeaderReallocatedBlock: PFullDebugBlockHeader; ANewSize: NativeInt);
  1379. var
  1380. {Note: FastMM will not catch exceptions inside these hooks, so make sure your hook code runs without
  1381. exceptions.}
  1382. OnDebugGetMemFinish: TOnDebugGetMemFinish = nil;
  1383. OnDebugFreeMemStart: TOnDebugFreeMemStart = nil;
  1384. OnDebugFreeMemFinish: TOnDebugFreeMemFinish = nil;
  1385. OnDebugReallocMemStart: TOnDebugReallocMemStart = nil;
  1386. OnDebugReallocMemFinish: TOnDebugReallocMemFinish = nil;
  1387. {$endif}
  1388. {$endif}
  1389. implementation
  1390. uses
  1391. {$ifndef POSIX}
  1392. Windows,
  1393. {$ifdef FullDebugMode}
  1394. {$ifdef Delphi4or5}
  1395. ShlObj,
  1396. {$else}
  1397. SHFolder,
  1398. {$endif}
  1399. {$endif}
  1400. {$else}
  1401. {$ifdef MACOS}
  1402. Posix.Stdlib, Posix.Unistd, Posix.Fcntl,
  1403. {$ELSE}
  1404. Libc,
  1405. {$endif}
  1406. {$endif}
  1407. FastMM4Messages;
  1408. {Fixed size move procedures. The 64-bit versions assume 16-byte alignment.}
  1409. procedure Move4(const ASource; var ADest; ACount: NativeInt); forward;
  1410. procedure Move12(const ASource; var ADest; ACount: NativeInt); forward;
  1411. procedure Move20(const ASource; var ADest; ACount: NativeInt); forward;
  1412. procedure Move28(const ASource; var ADest; ACount: NativeInt); forward;
  1413. procedure Move36(const ASource; var ADest; ACount: NativeInt); forward;
  1414. procedure Move44(const ASource; var ADest; ACount: NativeInt); forward;
  1415. procedure Move52(const ASource; var ADest; ACount: NativeInt); forward;
  1416. procedure Move60(const ASource; var ADest; ACount: NativeInt); forward;
  1417. procedure Move68(const ASource; var ADest; ACount: NativeInt); forward;
  1418. {$ifdef 64Bit}
  1419. {These are not needed and thus unimplemented under 32-bit}
  1420. procedure Move8(const ASource; var ADest; ACount: NativeInt); forward;
  1421. procedure Move24(const ASource; var ADest; ACount: NativeInt); forward;
  1422. procedure Move40(const ASource; var ADest; ACount: NativeInt); forward;
  1423. procedure Move56(const ASource; var ADest; ACount: NativeInt); forward;
  1424. {$endif}
  1425. {$ifdef DetectMMOperationsAfterUninstall}
  1426. {Invalid handlers to catch MM operations after uninstall}
  1427. function InvalidFreeMem(APointer: Pointer): Integer; forward;
  1428. function InvalidGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer; forward;
  1429. function InvalidReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer; forward;
  1430. function InvalidAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer; forward;
  1431. function InvalidRegisterAndUnRegisterMemoryLeak(APointer: Pointer): Boolean; forward;
  1432. {$endif}
  1433. {-------------------------Private constants----------------------------}
  1434. const
  1435. {The size of a medium block pool. This is allocated through VirtualAlloc and
  1436. is used to serve medium blocks. The size must be a multiple of 16 and at
  1437. least 4 bytes less than a multiple of 4K (the page size) to prevent a
  1438. possible read access violation when reading past the end of a memory block
  1439. in the optimized move routine (MoveX16LP). In Full Debug mode we leave a
  1440. trailing 256 bytes to be able to safely do a memory dump.}
  1441. MediumBlockPoolSize = 20 * 64 * 1024{$ifndef FullDebugMode} - 16{$else} - 256{$endif};
  1442. {The granularity of small blocks}
  1443. {$ifdef Align16Bytes}
  1444. SmallBlockGranularity = 16;
  1445. {$else}
  1446. SmallBlockGranularity = 8;
  1447. {$endif}
  1448. {The granularity of medium blocks. Newly allocated medium blocks are
  1449. a multiple of this size plus MediumBlockSizeOffset, to avoid cache line
  1450. conflicts}
  1451. MediumBlockGranularity = 256;
  1452. MediumBlockSizeOffset = 48;
  1453. {The granularity of large blocks}
  1454. LargeBlockGranularity = 65536;
  1455. {The maximum size of a small block. Blocks Larger than this are either
  1456. medium or large blocks.}
  1457. MaximumSmallBlockSize = 2608;
  1458. {The smallest medium block size. (Medium blocks are rounded up to the nearest
  1459. multiple of MediumBlockGranularity plus MediumBlockSizeOffset)}
  1460. MinimumMediumBlockSize = 11 * 256 + MediumBlockSizeOffset;
  1461. {The number of bins reserved for medium blocks}
  1462. MediumBlockBinsPerGroup = 32;
  1463. MediumBlockBinGroupCount = 32;
  1464. MediumBlockBinCount = MediumBlockBinGroupCount * MediumBlockBinsPerGroup;
  1465. {The maximum size allocatable through medium blocks. Blocks larger than this
  1466. fall through to VirtualAlloc ( = large blocks).}
  1467. MaximumMediumBlockSize = MinimumMediumBlockSize + (MediumBlockBinCount - 1) * MediumBlockGranularity;
  1468. {The target number of small blocks per pool. The actual number of blocks per
  1469. pool may be much greater for very small sizes and less for larger sizes. The
  1470. cost of allocating the small block pool is amortized across all the small
  1471. blocks in the pool, however the blocks may not all end up being used so they
  1472. may be lying idle.}
  1473. TargetSmallBlocksPerPool = 48;
  1474. {The minimum number of small blocks per pool. Any available medium block must
  1475. have space for roughly this many small blocks (or more) to be useable as a
  1476. small block pool.}
  1477. MinimumSmallBlocksPerPool = 12;
  1478. {The lower and upper limits for the optimal small block pool size}
  1479. OptimalSmallBlockPoolSizeLowerLimit = 29 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
  1480. OptimalSmallBlockPoolSizeUpperLimit = 64 * 1024 - MediumBlockGranularity + MediumBlockSizeOffset;
  1481. {The maximum small block pool size. If a free block is this size or larger
  1482. then it will be split.}
  1483. MaximumSmallBlockPoolSize = OptimalSmallBlockPoolSizeUpperLimit + MinimumMediumBlockSize;
  1484. {-------------Block type flags--------------}
  1485. {The lower 3 bits in the dword header of small blocks (4 bits in medium and
  1486. large blocks) are used as flags to indicate the state of the block}
  1487. {Set if the block is not in use}
  1488. IsFreeBlockFlag = 1;
  1489. {Set if this is a medium block}
  1490. IsMediumBlockFlag = 2;
  1491. {Set if it is a medium block being used as a small block pool. Only valid if
  1492. IsMediumBlockFlag is set.}
  1493. IsSmallBlockPoolInUseFlag = 4;
  1494. {Set if it is a large block. Only valid if IsMediumBlockFlag is not set.}
  1495. IsLargeBlockFlag = 4;
  1496. {Is the medium block preceding this block available? (Only used by medium
  1497. blocks)}
  1498. PreviousMediumBlockIsFreeFlag = 8;
  1499. {Is this large block segmented? I.e. is it actually built up from more than
  1500. one chunk allocated through VirtualAlloc? (Only used by large blocks.)}
  1501. LargeBlockIsSegmented = 8;
  1502. {The flags masks for small blocks}
  1503. DropSmallFlagsMask = -8;
  1504. ExtractSmallFlagsMask = 7;
  1505. {The flags masks for medium and large blocks}
  1506. DropMediumAndLargeFlagsMask = -16;
  1507. ExtractMediumAndLargeFlagsMask = 15;
  1508. {-------------Block resizing constants---------------}
  1509. SmallBlockDownsizeCheckAdder = 64;
  1510. SmallBlockUpsizeAdder = 32;
  1511. {When a medium block is reallocated to a size smaller than this, then it must
  1512. be reallocated to a small block and the data moved. If not, then it is
  1513. shrunk in place down to MinimumMediumBlockSize. Currently the limit is set
  1514. at a quarter of the minimum medium block size.}
  1515. MediumInPlaceDownsizeLimit = MinimumMediumBlockSize div 4;
  1516. {-------------Memory leak reporting constants---------------}
  1517. ExpectedMemoryLeaksListSize = 64 * 1024;
  1518. {-------------Other constants---------------}
  1519. {$ifndef NeverSleepOnThreadContention}
  1520. {Sleep time when a resource (small/medium/large block manager) is in use}
  1521. InitialSleepTime = 0;
  1522. {Used when the resource is still in use after the first sleep}
  1523. AdditionalSleepTime = 1;
  1524. {$endif}
  1525. {Hexadecimal characters}
  1526. HexTable: array[0..15] of AnsiChar = ('0', '1', '2', '3', '4', '5', '6', '7',
  1527. '8', '9', 'A', 'B', 'C', 'D', 'E', 'F');
  1528. {Copyright message - not used anywhere in the code}
  1529. Copyright: AnsiString = 'FastMM4 (c) 2004 - 2011 Pierre le Riche / Professional Software Development';
  1530. {$ifdef FullDebugMode}
  1531. {Virtual Method Called On Freed Object Errors}
  1532. StandardVirtualMethodNames: array[1 + vmtParent div SizeOf(Pointer) .. vmtDestroy div SizeOf(Pointer)] of PAnsiChar = (
  1533. {$ifdef BCB6OrDelphi6AndUp}
  1534. {$if RTLVersion >= 20}
  1535. 'Equals',
  1536. 'GetHashCode',
  1537. 'ToString',
  1538. {$ifend}
  1539. {$endif}
  1540. 'SafeCallException',
  1541. 'AfterConstruction',
  1542. 'BeforeDestruction',
  1543. 'Dispatch',
  1544. 'DefaultHandler',
  1545. 'NewInstance',
  1546. 'FreeInstance',
  1547. 'Destroy');
  1548. {The name of the FullDebugMode support DLL. The support DLL implements stack
  1549. tracing and the conversion of addresses to unit and line number information.}
  1550. {$ifdef 32Bit}
  1551. FullDebugModeLibraryName = FullDebugModeLibraryName32Bit;
  1552. {$else}
  1553. FullDebugModeLibraryName = FullDebugModeLibraryName64Bit;
  1554. {$endif}
  1555. {$endif}
  1556. {-------------------------Private types----------------------------}
  1557. type
  1558. {$ifdef Delphi4or5}
  1559. {Delphi 5 Compatibility}
  1560. PCardinal = ^Cardinal;
  1561. PPointer = ^Pointer;
  1562. {$endif}
  1563. {$ifdef BCB4}
  1564. {Define some additional types for BCB4}
  1565. PInteger = ^Integer;
  1566. {$endif}
  1567. {Move procedure type}
  1568. TMoveProc = procedure(const ASource; var ADest; ACount: NativeInt);
  1569. {Registers structure (for GetCPUID)}
  1570. TRegisters = record
  1571. RegEAX, RegEBX, RegECX, RegEDX: Integer;
  1572. end;
  1573. {The layout of a string allocation. Used to detect string leaks.}
  1574. PStrRec = ^StrRec;
  1575. StrRec = packed record
  1576. {$ifdef 64Bit}
  1577. _Padding: Integer;
  1578. {$endif}
  1579. {$ifdef BCB6OrDelphi6AndUp}
  1580. {$if RTLVersion >= 20}
  1581. codePage: Word;
  1582. elemSize: Word;
  1583. {$ifend}
  1584. {$endif}
  1585. refCnt: Integer;
  1586. length: Integer;
  1587. end;
  1588. {$ifdef EnableMemoryLeakReporting}
  1589. {Different kinds of memory leaks}
  1590. TMemoryLeakType = (mltUnexpectedLeak, mltExpectedLeakRegisteredByPointer,
  1591. mltExpectedLeakRegisteredByClass, mltExpectedLeakRegisteredBySize);
  1592. {$endif}
  1593. {---------------Small block structures-------------}
  1594. {Pointer to the header of a small block pool}
  1595. PSmallBlockPoolHeader = ^TSmallBlockPoolHeader;
  1596. {Small block type (Size = 32 bytes for 32-bit, 64 bytes for 64-bit).}
  1597. PSmallBlockType = ^TSmallBlockType;
  1598. TSmallBlockType = record
  1599. {True = Block type is locked}
  1600. BlockTypeLocked: Boolean;
  1601. {Bitmap indicating which of the first 8 medium block groups contain blocks
  1602. of a suitable size for a block pool.}
  1603. AllowedGroupsForBlockPoolBitmap: Byte;
  1604. {The block size for this block type}
  1605. BlockSize: Word;
  1606. {The minimum and optimal size of a small block pool for this block type}
  1607. MinimumBlockPoolSize: Word;
  1608. OptimalBlockPoolSize: Word;
  1609. {The first partially free pool for the given small block. This field must
  1610. be at the same offset as TSmallBlockPoolHeader.NextPartiallyFreePool.}
  1611. NextPartiallyFreePool: PSmallBlockPoolHeader;
  1612. {The last partially free pool for the small block type. This field must
  1613. be at the same offset as TSmallBlockPoolHeader.PreviousPartiallyFreePool.}
  1614. PreviousPartiallyFreePool: PSmallBlockPoolHeader;
  1615. {The offset of the last block that was served sequentially. The field must
  1616. be at the same offset as TSmallBlockPoolHeader.FirstFreeBlock.}
  1617. NextSequentialFeedBlockAddress: Pointer;
  1618. {The last block that can be served sequentially.}
  1619. MaxSequentialFeedBlockAddress: Pointer;
  1620. {The pool that is current being used to serve blocks in sequential order}
  1621. CurrentSequentialFeedPool: PSmallBlockPoolHeader;
  1622. {$ifdef UseCustomFixedSizeMoveRoutines}
  1623. {The fixed size move procedure used to move data for this block size when
  1624. it is upsized. When a block is downsized (which usually does not occur
  1625. that often) the variable size move routine is used.}
  1626. UpsizeMoveProcedure: TMoveProc;
  1627. {$else}
  1628. Reserved1: Pointer;
  1629. {$endif}
  1630. {$ifdef 64Bit}
  1631. {Pad to 64 bytes for 64-bit}
  1632. Reserved2: Pointer;
  1633. {$endif}
  1634. end;
  1635. {Small block pool (Size = 32 bytes for 32-bit, 48 bytes for 64-bit).}
  1636. TSmallBlockPoolHeader = record
  1637. {BlockType}
  1638. BlockType: PSmallBlockType;
  1639. {$ifdef 32Bit}
  1640. {Align the next fields to the same fields in TSmallBlockType and pad this
  1641. structure to 32 bytes for 32-bit}
  1642. Reserved1: Cardinal;
  1643. {$endif}
  1644. {The next and previous pool that has free blocks of this size. Do not
  1645. change the position of these two fields: They must be at the same offsets
  1646. as the fields in TSmallBlockType of the same name.}
  1647. NextPartiallyFreePool: PSmallBlockPoolHeader;
  1648. PreviousPartiallyFreePool: PSmallBlockPoolHeader;
  1649. {Pointer to the first free block inside this pool. This field must be at
  1650. the same offset as TSmallBlockType.NextSequentialFeedBlockAddress.}
  1651. FirstFreeBlock: Pointer;
  1652. {The number of blocks allocated in this pool.}
  1653. BlocksInUse: Cardinal;
  1654. {Padding}
  1655. Reserved2: Cardinal;
  1656. {The pool pointer and flags of the first block}
  1657. FirstBlockPoolPointerAndFlags: NativeUInt;
  1658. end;
  1659. {Small block layout:
  1660. At offset -SizeOf(Pointer) = Flags + address of the small block pool.
  1661. At offset BlockSize - SizeOf(Pointer) = Flags + address of the small block
  1662. pool for the next small block.
  1663. }
  1664. {------------------------Medium block structures------------------------}
  1665. {The medium block pool from which medium blocks are drawn. Size = 16 bytes
  1666. for 32-bit and 32 bytes for 64-bit.}
  1667. PMediumBlockPoolHeader = ^TMediumBlockPoolHeader;
  1668. TMediumBlockPoolHeader = record
  1669. {Points to the previous and next medium block pools. This circular linked
  1670. list is used to track memory leaks on program shutdown.}
  1671. PreviousMediumBlockPoolHeader: PMediumBlockPoolHeader;
  1672. NextMediumBlockPoolHeader: PMediumBlockPoolHeader;
  1673. {Padding}
  1674. Reserved1: NativeUInt;
  1675. {The block size and flags of the first medium block in the block pool}
  1676. FirstMediumBlockSizeAndFlags: NativeUInt;
  1677. end;
  1678. {Medium block layout:
  1679. Offset: -2 * SizeOf(Pointer) = Previous Block Size (only if the previous block is free)
  1680. Offset: -SizeOf(Pointer) = This block size and flags
  1681. Offset: 0 = User data / Previous Free Block (if this block is free)
  1682. Offset: SizeOf(Pointer) = Next Free Block (if this block is free)
  1683. Offset: BlockSize - 2*SizeOf(Pointer) = Size of this block (if this block is free)
  1684. Offset: BlockSize - SizeOf(Pointer) = Size of the next block and flags
  1685. {A medium block that is unused}
  1686. PMediumFreeBlock = ^TMediumFreeBlock;
  1687. TMediumFreeBlock = record
  1688. PreviousFreeBlock: PMediumFreeBlock;
  1689. NextFreeBlock: PMediumFreeBlock;
  1690. end;
  1691. {-------------------------Large block structures------------------------}
  1692. {Large block header record (Size = 16 for 32-bit, 32 for 64-bit)}
  1693. PLargeBlockHeader = ^TLargeBlockHeader;
  1694. TLargeBlockHeader = record
  1695. {Points to the previous and next large blocks. This circular linked
  1696. list is used to track memory leaks on program shutdown.}
  1697. PreviousLargeBlockHeader: PLargeBlockHeader;
  1698. NextLargeBlockHeader: PLargeBlockHeader;
  1699. {The user allocated size of the Large block}
  1700. UserAllocatedSize: NativeUInt;
  1701. {The size of this block plus the flags}
  1702. BlockSizeAndFlags: NativeUInt;
  1703. end;
  1704. {-------------------------Expected Memory Leak Structures--------------------}
  1705. {$ifdef EnableMemoryLeakReporting}
  1706. {The layout of an expected leak. All fields may not be specified, in which
  1707. case it may be harder to determine which leaks are expected and which are
  1708. not.}
  1709. PExpectedMemoryLeak = ^TExpectedMemoryLeak;
  1710. PPExpectedMemoryLeak = ^PExpectedMemoryLeak;
  1711. TExpectedMemoryLeak = record
  1712. {Linked list pointers}
  1713. PreviousLeak, NextLeak: PExpectedMemoryLeak;
  1714. {Information about the expected leak}
  1715. LeakAddress: Pointer;
  1716. LeakedClass: TClass;
  1717. {$ifdef CheckCppObjectTypeEnabled}
  1718. LeakedCppTypeIdPtr: Pointer;
  1719. {$endif}
  1720. LeakSize: NativeInt;
  1721. LeakCount: Integer;
  1722. end;
  1723. TExpectedMemoryLeaks = record
  1724. {The number of entries used in the expected leaks buffer}
  1725. EntriesUsed: Integer;
  1726. {Freed entries}
  1727. FirstFreeSlot: PExpectedMemoryLeak;
  1728. {Entries with the address specified}
  1729. FirstEntryByAddress: PExpectedMemoryLeak;
  1730. {Entries with no address specified, but with the class specified}
  1731. FirstEntryByClass: PExpectedMemoryLeak;
  1732. {Entries with only size specified}
  1733. FirstEntryBySizeOnly: PExpectedMemoryLeak;
  1734. {The expected leaks buffer (Need to leave space for this header)}
  1735. ExpectedLeaks: array[0..(ExpectedMemoryLeaksListSize - 64) div SizeOf(TExpectedMemoryLeak) - 1] of TExpectedMemoryLeak;
  1736. end;
  1737. PExpectedMemoryLeaks = ^TExpectedMemoryLeaks;
  1738. {$endif}
  1739. {-------------------------Private constants----------------------------}
  1740. const
  1741. {$ifndef BCB6OrDelphi7AndUp}
  1742. reOutOfMemory = 1;
  1743. reInvalidPtr = 2;
  1744. {$endif}
  1745. {The size of the block header in front of small and medium blocks}
  1746. BlockHeaderSize = SizeOf(Pointer);
  1747. {The size of a small block pool header}
  1748. SmallBlockPoolHeaderSize = SizeOf(TSmallBlockPoolHeader);
  1749. {The size of a medium block pool header}
  1750. MediumBlockPoolHeaderSize = SizeOf(TMediumBlockPoolHeader);
  1751. {The size of the header in front of Large blocks}
  1752. LargeBlockHeaderSize = SizeOf(TLargeBlockHeader);
  1753. {$ifdef FullDebugMode}
  1754. {We need space for the header, the trailer checksum and the trailing block
  1755. size (only used by freed medium blocks).}
  1756. FullDebugBlockOverhead = SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt) + SizeOf(Pointer);
  1757. {$endif}
  1758. {-------------------------Private variables----------------------------}
  1759. var
  1760. {-----------------Small block management------------------}
  1761. {The small block types. Sizes include the leading header. Sizes are
  1762. picked to limit maximum wastage to about 10% or 256 bytes (whichever is
  1763. less) where possible.}
  1764. SmallBlockTypes: array[0..NumSmallBlockTypes - 1] of TSmallBlockType =(
  1765. {8/16 byte jumps}
  1766. {$ifndef Align16Bytes}
  1767. (BlockSize: 8 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move4{$endif}),
  1768. {$endif}
  1769. (BlockSize: 16 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: {$ifdef 32Bit}Move12{$else}Move8{$endif}{$endif}),
  1770. {$ifndef Align16Bytes}
  1771. (BlockSize: 24 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move20{$endif}),
  1772. {$endif}
  1773. (BlockSize: 32 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: {$ifdef 32Bit}Move28{$else}Move24{$endif}{$endif}),
  1774. {$ifndef Align16Bytes}
  1775. (BlockSize: 40 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move36{$endif}),
  1776. {$endif}
  1777. (BlockSize: 48 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: {$ifdef 32Bit}Move44{$else}Move40{$endif}{$endif}),
  1778. {$ifndef Align16Bytes}
  1779. (BlockSize: 56 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move52{$endif}),
  1780. {$endif}
  1781. (BlockSize: 64 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: {$ifdef 32Bit}Move60{$else}Move56{$endif}{$endif}),
  1782. {$ifndef Align16Bytes}
  1783. (BlockSize: 72 {$ifdef UseCustomFixedSizeMoveRoutines}; UpsizeMoveProcedure: Move68{$endif}),
  1784. {$endif}
  1785. (BlockSize: 80),
  1786. {$ifndef Align16Bytes}
  1787. (BlockSize: 88),
  1788. {$endif}
  1789. (BlockSize: 96),
  1790. {$ifndef Align16Bytes}
  1791. (BlockSize: 104),
  1792. {$endif}
  1793. (BlockSize: 112),
  1794. {$ifndef Align16Bytes}
  1795. (BlockSize: 120),
  1796. {$endif}
  1797. (BlockSize: 128),
  1798. {$ifndef Align16Bytes}
  1799. (BlockSize: 136),
  1800. {$endif}
  1801. (BlockSize: 144),
  1802. {$ifndef Align16Bytes}
  1803. (BlockSize: 152),
  1804. {$endif}
  1805. (BlockSize: 160),
  1806. {16 byte jumps}
  1807. (BlockSize: 176),
  1808. (BlockSize: 192),
  1809. (BlockSize: 208),
  1810. (BlockSize: 224),
  1811. (BlockSize: 240),
  1812. (BlockSize: 256),
  1813. (BlockSize: 272),
  1814. (BlockSize: 288),
  1815. (BlockSize: 304),
  1816. (BlockSize: 320),
  1817. {32 byte jumps}
  1818. (BlockSize: 352),
  1819. (BlockSize: 384),
  1820. (BlockSize: 416),
  1821. (BlockSize: 448),
  1822. (BlockSize: 480),
  1823. {48 byte jumps}
  1824. (BlockSize: 528),
  1825. (BlockSize: 576),
  1826. (BlockSize: 624),
  1827. (BlockSize: 672),
  1828. {64 byte jumps}
  1829. (BlockSize: 736),
  1830. (BlockSize: 800),
  1831. {80 byte jumps}
  1832. (BlockSize: 880),
  1833. (BlockSize: 960),
  1834. {96 byte jumps}
  1835. (BlockSize: 1056),
  1836. (BlockSize: 1152),
  1837. {112 byte jumps}
  1838. (BlockSize: 1264),
  1839. (BlockSize: 1376),
  1840. {128 byte jumps}
  1841. (BlockSize: 1504),
  1842. {144 byte jumps}
  1843. (BlockSize: 1648),
  1844. {160 byte jumps}
  1845. (BlockSize: 1808),
  1846. {176 byte jumps}
  1847. (BlockSize: 1984),
  1848. {192 byte jumps}
  1849. (BlockSize: 2176),
  1850. {208 byte jumps}
  1851. (BlockSize: 2384),
  1852. {224 byte jumps}
  1853. (BlockSize: MaximumSmallBlockSize),
  1854. {The last block size occurs three times. If, during a GetMem call, the
  1855. requested block size is already locked by another thread then up to two
  1856. larger block sizes may be used instead. Having the last block size occur
  1857. three times avoids the need to have a size overflow check.}
  1858. (BlockSize: MaximumSmallBlockSize),
  1859. (BlockSize: MaximumSmallBlockSize));
  1860. {Size to small block type translation table}
  1861. AllocSize2SmallBlockTypeIndX4: array[0..(MaximumSmallBlockSize - 1) div SmallBlockGranularity] of Byte;
  1862. {-----------------Medium block management------------------}
  1863. {A dummy medium block pool header: Maintains a circular list of all medium
  1864. block pools to enable memory leak detection on program shutdown.}
  1865. MediumBlockPoolsCircularList: TMediumBlockPoolHeader;
  1866. {Are medium blocks locked?}
  1867. MediumBlocksLocked: Boolean;
  1868. {The sequential feed medium block pool.}
  1869. LastSequentiallyFedMediumBlock: Pointer;
  1870. MediumSequentialFeedBytesLeft: Cardinal;
  1871. {The medium block bins are divided into groups of 32 bins. If a bit
  1872. is set in this group bitmap, then at least one bin in the group has free
  1873. blocks.}
  1874. MediumBlockBinGroupBitmap: Cardinal;
  1875. {The medium block bins: total of 32 * 32 = 1024 bins of a certain
  1876. minimum size.}
  1877. MediumBlockBinBitmaps: array[0..MediumBlockBinGroupCount - 1] of Cardinal;
  1878. {The medium block bins. There are 1024 LIFO circular linked lists each
  1879. holding blocks of a specified minimum size. The sizes vary in size from
  1880. MinimumMediumBlockSize to MaximumMediumBlockSize. The bins are treated as
  1881. type TMediumFreeBlock to avoid pointer checks.}
  1882. MediumBlockBins: array[0..MediumBlockBinCount - 1] of TMediumFreeBlock;
  1883. {-----------------Large block management------------------}
  1884. {Are large blocks locked?}
  1885. LargeBlocksLocked: Boolean;
  1886. {A dummy large block header: Maintains a list of all allocated large blocks
  1887. to enable memory leak detection on program shutdown.}
  1888. LargeBlocksCircularList: TLargeBlockHeader;
  1889. {-------------------------Expected Memory Leak Structures--------------------}
  1890. {$ifdef EnableMemoryLeakReporting}
  1891. {The expected memory leaks}
  1892. ExpectedMemoryLeaks: PExpectedMemoryLeaks;
  1893. ExpectedMemoryLeaksListLocked: Boolean;
  1894. {$endif}
  1895. {---------------------Full Debug Mode structures--------------------}
  1896. {$ifdef FullDebugMode}
  1897. {The allocation group stack}
  1898. AllocationGroupStack: array[0..AllocationGroupStackSize - 1] of Cardinal;
  1899. {The allocation group stack top (it is an index into AllocationGroupStack)}
  1900. AllocationGroupStackTop: Cardinal;
  1901. {The last allocation number used}
  1902. CurrentAllocationNumber: Cardinal;
  1903. {This is a count of the number of threads currently inside any of the
  1904. FullDebugMode GetMem, Freemem or ReallocMem handlers. If this value
  1905. is negative then a block scan is in progress and no thread may
  1906. allocate, free or reallocate any block or modify any FullDebugMode
  1907. block header or footer.}
  1908. ThreadsInFullDebugModeRoutine: Integer;
  1909. {The current log file name}
  1910. MMLogFileName: array[0..1023] of AnsiChar;
  1911. {The 64K block of reserved memory used to trap invalid memory accesses using
  1912. fields in a freed object.}
  1913. ReservedBlock: Pointer;
  1914. {The virtual method index count - used to get the virtual method index for a
  1915. virtual method call on a freed object.}
  1916. VMIndex: Integer;
  1917. {The fake VMT used to catch virtual method calls on freed objects.}
  1918. FreedObjectVMT: packed record
  1919. VMTData: array[vmtSelfPtr .. vmtParent + SizeOf(Pointer) - 1] of byte;
  1920. VMTMethods: array[SizeOf(Pointer) + vmtParent .. vmtParent + MaxFakeVMTEntries * SizeOf(Pointer) + SizeOf(Pointer) - 1] of Byte;
  1921. end;
  1922. {$ifdef CatchUseOfFreedInterfaces}
  1923. VMTBadInterface: array[0..MaxFakeVMTEntries - 1] of Pointer;
  1924. {$endif}
  1925. {$endif}
  1926. {--------------Other info--------------}
  1927. {The memory manager that was replaced}
  1928. OldMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif};
  1929. {The replacement memory manager}
  1930. NewMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif};
  1931. {$ifdef DetectMMOperationsAfterUninstall}
  1932. {Invalid handlers to catch MM operations after uninstall}
  1933. InvalidMemoryManager: {$ifndef BDS2006AndUp}TMemoryManager{$else}TMemoryManagerEx{$endif} = (
  1934. GetMem: InvalidGetMem;
  1935. FreeMem: InvalidFreeMem;
  1936. ReallocMem: InvalidReallocMem
  1937. {$ifdef BDS2006AndUp};
  1938. AllocMem: InvalidAllocMem;
  1939. RegisterExpectedMemoryLeak: InvalidRegisterAndUnRegisterMemoryLeak;
  1940. UnRegisterExpectedMemoryLeak: InvalidRegisterAndUnRegisterMemoryLeak;
  1941. {$endif}
  1942. );
  1943. {$endif}
  1944. {$ifdef MMSharingEnabled}
  1945. {A string uniquely identifying the current process (for sharing the memory
  1946. manager between DLLs and the main application)}
  1947. MappingObjectName: array[0..25] of AnsiChar = ('L', 'o', 'c', 'a', 'l', '\',
  1948. 'F', 'a', 's', 't', 'M', 'M', '_', 'P', 'I', 'D', '_', '?', '?', '?', '?',
  1949. '?', '?', '?', '?', #0);
  1950. {$ifdef EnableBackwardCompatibleMMSharing}
  1951. UniqueProcessIDString: array[1..20] of AnsiChar = ('?', '?', '?', '?', '?',
  1952. '?', '?', '?', '_', 'P', 'I', 'D', '_', 'F', 'a', 's', 't', 'M', 'M', #0);
  1953. UniqueProcessIDStringBE: array[1..23] of AnsiChar = ('?', '?', '?', '?', '?',
  1954. '?', '?', '?', '_', 'P', 'I', 'D', '_', 'F', 'a', 's', 't', 'M', 'M', '_',
  1955. 'B', 'E', #0);
  1956. {The handle of the MM window}
  1957. MMWindow: HWND;
  1958. {The handle of the MM window (for default MM of Delphi 2006 compatibility)}
  1959. MMWindowBE: HWND;
  1960. {$endif}
  1961. {The handle of the memory mapped file}
  1962. MappingObjectHandle: NativeUInt;
  1963. {$endif}
  1964. {Has FastMM been installed?}
  1965. FastMMIsInstalled: Boolean;
  1966. {Is the MM in place a shared memory manager?}
  1967. IsMemoryManagerOwner: Boolean;
  1968. {Must MMX be used for move operations?}
  1969. {$ifdef EnableMMX}
  1970. {$ifndef ForceMMX}
  1971. UseMMX: Boolean;
  1972. {$endif}
  1973. {$endif}
  1974. {Is a MessageBox currently showing? If so, do not show another one.}
  1975. ShowingMessageBox: Boolean;
  1976. {True if RunInitializationCode has been called already.}
  1977. InitializationCodeHasRun: Boolean = False;
  1978. {----------------Utility Functions------------------}
  1979. {A copy of StrLen in order to avoid the SysUtils unit, which would have
  1980. introduced overhead like exception handling code.}
  1981. function StrLen(const AStr: PAnsiChar): NativeUInt;
  1982. {$ifndef Use32BitAsm}
  1983. begin
  1984. Result := 0;
  1985. while AStr[Result] <> #0 do
  1986. Inc(Result);
  1987. end;
  1988. {$else}
  1989. asm
  1990. {Check the first byte}
  1991. cmp byte ptr [eax], 0
  1992. je @ZeroLength
  1993. {Get the negative of the string start in edx}
  1994. mov edx, eax
  1995. neg edx
  1996. {Word align}
  1997. add eax, 1
  1998. and eax, -2
  1999. @ScanLoop:
  2000. mov cx, [eax]
  2001. add eax, 2
  2002. test cl, ch
  2003. jnz @ScanLoop
  2004. test cl, cl
  2005. jz @ReturnLess2
  2006. test ch, ch
  2007. jnz @ScanLoop
  2008. lea eax, [eax + edx - 1]
  2009. ret
  2010. @ReturnLess2:
  2011. lea eax, [eax + edx - 2]
  2012. ret
  2013. @ZeroLength:
  2014. xor eax, eax
  2015. end;
  2016. {$endif}
  2017. {$ifdef EnableMMX}
  2018. {$ifndef ForceMMX}
  2019. {Returns true if the CPUID instruction is supported}
  2020. function CPUID_Supported: Boolean;
  2021. asm
  2022. pushfd
  2023. pop eax
  2024. mov edx, eax
  2025. xor eax, $200000
  2026. push eax
  2027. popfd
  2028. pushfd
  2029. pop eax
  2030. xor eax, edx
  2031. setnz al
  2032. end;
  2033. {Gets the CPUID}
  2034. function GetCPUID(AInfoRequired: Integer): TRegisters;
  2035. asm
  2036. push ebx
  2037. push esi
  2038. mov esi, edx
  2039. {cpuid instruction}
  2040. {$ifdef Delphi4or5}
  2041. db $0f, $a2
  2042. {$else}
  2043. cpuid
  2044. {$endif}
  2045. {Save registers}
  2046. mov TRegisters[esi].RegEAX, eax
  2047. mov TRegisters[esi].RegEBX, ebx
  2048. mov TRegisters[esi].RegECX, ecx
  2049. mov TRegisters[esi].RegEDX, edx
  2050. pop esi
  2051. pop ebx
  2052. end;
  2053. {Returns true if the CPU supports MMX}
  2054. function MMX_Supported: Boolean;
  2055. var
  2056. LReg: TRegisters;
  2057. begin
  2058. if CPUID_Supported then
  2059. begin
  2060. {Get the CPUID}
  2061. LReg := GetCPUID(1);
  2062. {Bit 23 must be set for MMX support}
  2063. Result := LReg.RegEDX and $800000 <> 0;
  2064. end
  2065. else
  2066. Result := False;
  2067. end;
  2068. {$endif}
  2069. {$endif}
  2070. {Compare [AAddress], CompareVal:
  2071. If Equal: [AAddress] := NewVal and result = CompareVal
  2072. If Unequal: Result := [AAddress]}
  2073. function LockCmpxchg(CompareVal, NewVal: Byte; AAddress: PByte): Byte;
  2074. asm
  2075. {$ifdef 32Bit}
  2076. {On entry:
  2077. al = CompareVal,
  2078. dl = NewVal,
  2079. ecx = AAddress}
  2080. {$ifndef LINUX}
  2081. lock cmpxchg [ecx], dl
  2082. {$else}
  2083. {Workaround for Kylix compiler bug}
  2084. db $F0, $0F, $B0, $11
  2085. {$endif}
  2086. {$else}
  2087. {On entry:
  2088. cl = CompareVal
  2089. dl = NewVal
  2090. r8 = AAddress}
  2091. .noframe
  2092. mov rax, rcx
  2093. lock cmpxchg [r8], dl
  2094. {$endif}
  2095. end;
  2096. {$ifndef ASMVersion}
  2097. {Gets the first set bit in the 32-bit number, returning the bit index}
  2098. function FindFirstSetBit(ACardinal: Cardinal): Cardinal;
  2099. asm
  2100. {$ifdef 64Bit}
  2101. .noframe
  2102. mov rax, rcx
  2103. {$endif}
  2104. bsf eax, eax
  2105. end;
  2106. {$endif}
  2107. {$ifdef MACOS}
  2108. function StrLCopy(Dest: PAnsiChar; const Source: PAnsiChar; MaxLen: Cardinal): PAnsiChar;
  2109. var
  2110. Len: Cardinal;
  2111. begin
  2112. Result := Dest;
  2113. Len := StrLen(Source);
  2114. if Len > MaxLen then
  2115. Len := MaxLen;
  2116. Move(Source^, Dest^, Len * SizeOf(AnsiChar));
  2117. Dest[Len] := #0;
  2118. end;
  2119. function GetModuleFileName(Module: HMODULE; Buffer: PAnsiChar; BufLen: Integer): Integer;
  2120. const
  2121. CUnknown: AnsiString = 'unknown';
  2122. var
  2123. tmp: array[0..512] of Char;
  2124. begin
  2125. if FastMMIsInstalled then
  2126. begin
  2127. Result := System.GetModuleFileName(Module, tmp, BufLen);
  2128. StrLCopy(Buffer, PAnsiChar(AnsiString(tmp)), BufLen);
  2129. end
  2130. else
  2131. begin
  2132. Result := Length(CUnknown);
  2133. StrLCopy(Buffer, Pointer(CUnknown), Result + 1);
  2134. end;
  2135. end;
  2136. const
  2137. INVALID_HANDLE_VALUE = THandle(-1);
  2138. function FileCreate(const FileName: string): THandle;
  2139. begin
  2140. Result := THandle(__open(PAnsiChar(UTF8String(FileName)), O_RDWR or O_CREAT or O_TRUNC or O_EXCL, FileAccessRights));
  2141. end;
  2142. {$endif}
  2143. {Writes the module filename to the specified buffer and returns the number of
  2144. characters written.}
  2145. function AppendModuleFileName(ABuffer: PAnsiChar): Integer;
  2146. var
  2147. LModuleHandle: HModule;
  2148. begin
  2149. {Get the module handle}
  2150. {$ifndef borlndmmdll}
  2151. if IsLibrary then
  2152. LModuleHandle := HInstance
  2153. else
  2154. {$endif}
  2155. LModuleHandle := 0;
  2156. {Get the module name}
  2157. {$ifndef POSIX}
  2158. Result := GetModuleFileNameA(LModuleHandle, ABuffer, 512);
  2159. {$else}
  2160. Result := GetModuleFileName(LModuleHandle, ABuffer, 512);
  2161. {$endif}
  2162. end;
  2163. {Copies the name of the module followed by the given string to the buffer,
  2164. returning the pointer following the buffer.}
  2165. function AppendStringToModuleName(AString, ABuffer: PAnsiChar): PAnsiChar;
  2166. var
  2167. LModuleNameLength: Cardinal;
  2168. LCopyStart: PAnsiChar;
  2169. begin
  2170. {Get the name of the application}
  2171. LModuleNameLength := AppendModuleFileName(ABuffer);
  2172. {Replace the last few characters}
  2173. if LModuleNameLength > 0 then
  2174. begin
  2175. {Find the last backslash}
  2176. LCopyStart := PAnsiChar(PByte(ABuffer) + LModuleNameLength - 1);
  2177. LModuleNameLength := 0;
  2178. while (UIntPtr(LCopyStart) >= UIntPtr(ABuffer))
  2179. and (LCopyStart^ <> '\') do
  2180. begin
  2181. Inc(LModuleNameLength);
  2182. Dec(LCopyStart);
  2183. end;
  2184. {Copy the name to the start of the buffer}
  2185. Inc(LCopyStart);
  2186. System.Move(LCopyStart^, ABuffer^, LModuleNameLength);
  2187. Inc(ABuffer, LModuleNameLength);
  2188. ABuffer^ := ':';
  2189. Inc(ABuffer);
  2190. ABuffer^ := ' ';
  2191. Inc(ABuffer);
  2192. end;
  2193. {Append the string}
  2194. while AString^ <> #0 do
  2195. begin
  2196. ABuffer^ := AString^;
  2197. Inc(ABuffer);
  2198. {Next char}
  2199. Inc(AString);
  2200. end;
  2201. ABuffer^ := #0;
  2202. Result := ABuffer;
  2203. end;
  2204. {----------------Faster Move Procedures-------------------}
  2205. {Fixed size move operations ignore the size parameter. All moves are assumed to
  2206. be non-overlapping.}
  2207. procedure Move4(const ASource; var ADest; ACount: NativeInt);
  2208. asm
  2209. {$ifdef 32Bit}
  2210. mov eax, [eax]
  2211. mov [edx], eax
  2212. {$else}
  2213. .noframe
  2214. mov eax, [rcx]
  2215. mov [rdx], eax
  2216. {$endif}
  2217. end;
  2218. {$ifdef 64Bit}
  2219. procedure Move8(const ASource; var ADest; ACount: NativeInt);
  2220. asm
  2221. mov rax, [rcx]
  2222. mov [rdx], rax
  2223. end;
  2224. {$endif}
  2225. procedure Move12(const ASource; var ADest; ACount: NativeInt);
  2226. asm
  2227. {$ifdef 32Bit}
  2228. mov ecx, [eax]
  2229. mov [edx], ecx
  2230. mov ecx, [eax + 4]
  2231. mov eax, [eax + 8]
  2232. mov [edx + 4], ecx
  2233. mov [edx + 8], eax
  2234. {$else}
  2235. .noframe
  2236. mov rax, [rcx]
  2237. mov ecx, [rcx + 8]
  2238. mov [rdx], rax
  2239. mov [rdx + 8], ecx
  2240. {$endif}
  2241. end;
  2242. procedure Move20(const ASource; var ADest; ACount: NativeInt);
  2243. asm
  2244. {$ifdef 32Bit}
  2245. mov ecx, [eax]
  2246. mov [edx], ecx
  2247. mov ecx, [eax + 4]
  2248. mov [edx + 4], ecx
  2249. mov ecx, [eax + 8]
  2250. mov [edx + 8], ecx
  2251. mov ecx, [eax + 12]
  2252. mov eax, [eax + 16]
  2253. mov [edx + 12], ecx
  2254. mov [edx + 16], eax
  2255. {$else}
  2256. .noframe
  2257. movdqa xmm0, [rcx]
  2258. mov ecx, [rcx + 16]
  2259. movdqa [rdx], xmm0
  2260. mov [rdx + 16], ecx
  2261. {$endif}
  2262. end;
  2263. {$ifdef 64Bit}
  2264. procedure Move24(const ASource; var ADest; ACount: NativeInt);
  2265. asm
  2266. movdqa xmm0, [rcx]
  2267. mov r8, [rcx + 16]
  2268. movdqa [rdx], xmm0
  2269. mov [rdx + 16], r8
  2270. end;
  2271. {$endif}
  2272. procedure Move28(const ASource; var ADest; ACount: NativeInt);
  2273. asm
  2274. {$ifdef 32Bit}
  2275. mov ecx, [eax]
  2276. mov [edx], ecx
  2277. mov ecx, [eax + 4]
  2278. mov [edx + 4], ecx
  2279. mov ecx, [eax + 8]
  2280. mov [edx + 8], ecx
  2281. mov ecx, [eax + 12]
  2282. mov [edx + 12], ecx
  2283. mov ecx, [eax + 16]
  2284. mov [edx + 16], ecx
  2285. mov ecx, [eax + 20]
  2286. mov eax, [eax + 24]
  2287. mov [edx + 20], ecx
  2288. mov [edx + 24], eax
  2289. {$else}
  2290. .noframe
  2291. movdqa xmm0, [rcx]
  2292. mov r8, [rcx + 16]
  2293. mov ecx, [rcx + 24]
  2294. movdqa [rdx], xmm0
  2295. mov [rdx + 16], r8
  2296. mov [rdx + 24], ecx
  2297. {$endif}
  2298. end;
  2299. procedure Move36(const ASource; var ADest; ACount: NativeInt);
  2300. asm
  2301. {$ifdef 32Bit}
  2302. fild qword ptr [eax]
  2303. fild qword ptr [eax + 8]
  2304. fild qword ptr [eax + 16]
  2305. fild qword ptr [eax + 24]
  2306. mov ecx, [eax + 32]
  2307. mov [edx + 32], ecx
  2308. fistp qword ptr [edx + 24]
  2309. fistp qword ptr [edx + 16]
  2310. fistp qword ptr [edx + 8]
  2311. fistp qword ptr [edx]
  2312. {$else}
  2313. .noframe
  2314. movdqa xmm0, [rcx]
  2315. movdqa xmm1, [rcx + 16]
  2316. mov ecx, [rcx + 32]
  2317. movdqa [rdx], xmm0
  2318. movdqa [rdx + 16], xmm1
  2319. mov [rdx + 32], ecx
  2320. {$endif}
  2321. end;
  2322. {$ifdef 64Bit}
  2323. procedure Move40(const ASource; var ADest; ACount: NativeInt);
  2324. asm
  2325. movdqa xmm0, [rcx]
  2326. movdqa xmm1, [rcx + 16]
  2327. mov r8, [rcx + 32]
  2328. movdqa [rdx], xmm0
  2329. movdqa [rdx + 16], xmm1
  2330. mov [rdx + 32], r8
  2331. end;
  2332. {$endif}
  2333. procedure Move44(const ASource; var ADest; ACount: NativeInt);
  2334. asm
  2335. {$ifdef 32Bit}
  2336. fild qword ptr [eax]
  2337. fild qword ptr [eax + 8]
  2338. fild qword ptr [eax + 16]
  2339. fild qword ptr [eax + 24]
  2340. fild qword ptr [eax + 32]
  2341. mov ecx, [eax + 40]
  2342. mov [edx + 40], ecx
  2343. fistp qword ptr [edx + 32]
  2344. fistp qword ptr [edx + 24]
  2345. fistp qword ptr [edx + 16]
  2346. fistp qword ptr [edx + 8]
  2347. fistp qword ptr [edx]
  2348. {$else}
  2349. .noframe
  2350. movdqa xmm0, [rcx]
  2351. movdqa xmm1, [rcx + 16]
  2352. mov r8, [rcx + 32]
  2353. mov ecx, [rcx + 40]
  2354. movdqa [rdx], xmm0
  2355. movdqa [rdx + 16], xmm1
  2356. mov [rdx + 32], r8
  2357. mov [rdx + 40], ecx
  2358. {$endif}
  2359. end;
  2360. procedure Move52(const ASource; var ADest; ACount: NativeInt);
  2361. asm
  2362. {$ifdef 32Bit}
  2363. fild qword ptr [eax]
  2364. fild qword ptr [eax + 8]
  2365. fild qword ptr [eax + 16]
  2366. fild qword ptr [eax + 24]
  2367. fild qword ptr [eax + 32]
  2368. fild qword ptr [eax + 40]
  2369. mov ecx, [eax + 48]
  2370. mov [edx + 48], ecx
  2371. fistp qword ptr [edx + 40]
  2372. fistp qword ptr [edx + 32]
  2373. fistp qword ptr [edx + 24]
  2374. fistp qword ptr [edx + 16]
  2375. fistp qword ptr [edx + 8]
  2376. fistp qword ptr [edx]
  2377. {$else}
  2378. .noframe
  2379. movdqa xmm0, [rcx]
  2380. movdqa xmm1, [rcx + 16]
  2381. movdqa xmm2, [rcx + 32]
  2382. mov ecx, [rcx + 48]
  2383. movdqa [rdx], xmm0
  2384. movdqa [rdx + 16], xmm1
  2385. movdqa [rdx + 32], xmm2
  2386. mov [rdx + 48], ecx
  2387. {$endif}
  2388. end;
  2389. {$ifdef 64Bit}
  2390. procedure Move56(const ASource; var ADest; ACount: NativeInt);
  2391. asm
  2392. movdqa xmm0, [rcx]
  2393. movdqa xmm1, [rcx + 16]
  2394. movdqa xmm2, [rcx + 32]
  2395. mov r8, [rcx + 48]
  2396. movdqa [rdx], xmm0
  2397. movdqa [rdx + 16], xmm1
  2398. movdqa [rdx + 32], xmm2
  2399. mov [rdx + 48], r8
  2400. end;
  2401. {$endif}
  2402. procedure Move60(const ASource; var ADest; ACount: NativeInt);
  2403. asm
  2404. {$ifdef 32Bit}
  2405. fild qword ptr [eax]
  2406. fild qword ptr [eax + 8]
  2407. fild qword ptr [eax + 16]
  2408. fild qword ptr [eax + 24]
  2409. fild qword ptr [eax + 32]
  2410. fild qword ptr [eax + 40]
  2411. fild qword ptr [eax + 48]
  2412. mov ecx, [eax + 56]
  2413. mov [edx + 56], ecx
  2414. fistp qword ptr [edx + 48]
  2415. fistp qword ptr [edx + 40]
  2416. fistp qword ptr [edx + 32]
  2417. fistp qword ptr [edx + 24]
  2418. fistp qword ptr [edx + 16]
  2419. fistp qword ptr [edx + 8]
  2420. fistp qword ptr [edx]
  2421. {$else}
  2422. .noframe
  2423. movdqa xmm0, [rcx]
  2424. movdqa xmm1, [rcx + 16]
  2425. movdqa xmm2, [rcx + 32]
  2426. mov r8, [rcx + 48]
  2427. mov ecx, [rcx + 56]
  2428. movdqa [rdx], xmm0
  2429. movdqa [rdx + 16], xmm1
  2430. movdqa [rdx + 32], xmm2
  2431. mov [rdx + 48], r8
  2432. mov [rdx + 56], ecx
  2433. {$endif}
  2434. end;
  2435. procedure Move68(const ASource; var ADest; ACount: NativeInt);
  2436. asm
  2437. {$ifdef 32Bit}
  2438. fild qword ptr [eax]
  2439. fild qword ptr [eax + 8]
  2440. fild qword ptr [eax + 16]
  2441. fild qword ptr [eax + 24]
  2442. fild qword ptr [eax + 32]
  2443. fild qword ptr [eax + 40]
  2444. fild qword ptr [eax + 48]
  2445. fild qword ptr [eax + 56]
  2446. mov ecx, [eax + 64]
  2447. mov [edx + 64], ecx
  2448. fistp qword ptr [edx + 56]
  2449. fistp qword ptr [edx + 48]
  2450. fistp qword ptr [edx + 40]
  2451. fistp qword ptr [edx + 32]
  2452. fistp qword ptr [edx + 24]
  2453. fistp qword ptr [edx + 16]
  2454. fistp qword ptr [edx + 8]
  2455. fistp qword ptr [edx]
  2456. {$else}
  2457. .noframe
  2458. movdqa xmm0, [rcx]
  2459. movdqa xmm1, [rcx + 16]
  2460. movdqa xmm2, [rcx + 32]
  2461. movdqa xmm3, [rcx + 48]
  2462. mov ecx, [rcx + 64]
  2463. movdqa [rdx], xmm0
  2464. movdqa [rdx + 16], xmm1
  2465. movdqa [rdx + 32], xmm2
  2466. movdqa [rdx + 48], xmm3
  2467. mov [rdx + 64], ecx
  2468. {$endif}
  2469. end;
  2470. {Variable size move procedure: Rounds ACount up to the next multiple of 16 less
  2471. SizeOf(Pointer). Important note: Always moves at least 16 - SizeOf(Pointer)
  2472. bytes (the minimum small block size with 16 byte alignment), irrespective of
  2473. ACount.}
  2474. procedure MoveX16LP(const ASource; var ADest; ACount: NativeInt);
  2475. asm
  2476. {$ifdef 32Bit}
  2477. {Make the counter negative based: The last 12 bytes are moved separately}
  2478. sub ecx, 12
  2479. add eax, ecx
  2480. add edx, ecx
  2481. {$ifdef EnableMMX}
  2482. {$ifndef ForceMMX}
  2483. cmp UseMMX, True
  2484. jne @FPUMove
  2485. {$endif}
  2486. {Make the counter negative based: The last 12 bytes are moved separately}
  2487. neg ecx
  2488. jns @MMXMoveLast12
  2489. @MMXMoveLoop:
  2490. {Move a 16 byte block}
  2491. {$ifdef Delphi4or5}
  2492. {Delphi 5 compatibility}
  2493. db $0f, $6f, $04, $01
  2494. db $0f, $6f, $4c, $01, $08
  2495. db $0f, $7f, $04, $11
  2496. db $0f, $7f, $4c, $11, $08
  2497. {$else}
  2498. movq mm0, [eax + ecx]
  2499. movq mm1, [eax + ecx + 8]
  2500. movq [edx + ecx], mm0
  2501. movq [edx + ecx + 8], mm1
  2502. {$endif}
  2503. {Are there another 16 bytes to move?}
  2504. add ecx, 16
  2505. js @MMXMoveLoop
  2506. @MMXMoveLast12:
  2507. {Do the last 12 bytes}
  2508. {$ifdef Delphi4or5}
  2509. {Delphi 5 compatibility}
  2510. db $0f, $6f, $04, $01
  2511. {$else}
  2512. movq mm0, [eax + ecx]
  2513. {$endif}
  2514. mov eax, [eax + ecx + 8]
  2515. {$ifdef Delphi4or5}
  2516. {Delphi 5 compatibility}
  2517. db $0f, $7f, $04, $11
  2518. {$else}
  2519. movq [edx + ecx], mm0
  2520. {$endif}
  2521. mov [edx + ecx + 8], eax
  2522. {Exit MMX state}
  2523. {$ifdef Delphi4or5}
  2524. {Delphi 5 compatibility}
  2525. db $0f, $77
  2526. {$else}
  2527. emms
  2528. {$endif}
  2529. {$ifndef ForceMMX}
  2530. ret
  2531. {$endif}
  2532. {$endif}
  2533. {FPU code is only used if MMX is not forced}
  2534. {$ifndef ForceMMX}
  2535. @FPUMove:
  2536. neg ecx
  2537. jns @FPUMoveLast12
  2538. @FPUMoveLoop:
  2539. {Move a 16 byte block}
  2540. fild qword ptr [eax + ecx]
  2541. fild qword ptr [eax + ecx + 8]
  2542. fistp qword ptr [edx + ecx + 8]
  2543. fistp qword ptr [edx + ecx]
  2544. {Are there another 16 bytes to move?}
  2545. add ecx, 16
  2546. js @FPUMoveLoop
  2547. @FPUMoveLast12:
  2548. {Do the last 12 bytes}
  2549. fild qword ptr [eax + ecx]
  2550. fistp qword ptr [edx + ecx]
  2551. mov eax, [eax + ecx + 8]
  2552. mov [edx + ecx + 8], eax
  2553. {$endif}
  2554. {$else}
  2555. .noframe
  2556. {Make the counter negative based: The last 8 bytes are moved separately}
  2557. sub r8, 8
  2558. add rcx, r8
  2559. add rdx, r8
  2560. neg r8
  2561. jns @MoveLast12
  2562. @MoveLoop:
  2563. {Move a 16 byte block}
  2564. movdqa xmm0, [rcx + r8]
  2565. movdqa [rdx + r8], xmm0
  2566. {Are there another 16 bytes to move?}
  2567. add r8, 16
  2568. js @MoveLoop
  2569. @MoveLast12:
  2570. {Do the last 8 bytes}
  2571. mov r9, [rcx + r8]
  2572. mov [rdx + r8], r9
  2573. {$endif}
  2574. end;
  2575. {Variable size move procedure: Rounds ACount up to the next multiple of 8 less
  2576. SizeOf(Pointer). Important note: Always moves at least 8 - SizeOf(Pointer)
  2577. bytes (the minimum small block size with 8 byte alignment), irrespective of
  2578. ACount.}
  2579. procedure MoveX8LP(const ASource; var ADest; ACount: NativeInt);
  2580. asm
  2581. {$ifdef 32Bit}
  2582. {Make the counter negative based: The last 4 bytes are moved separately}
  2583. sub ecx, 4
  2584. {4 bytes or less? -> Use the Move4 routine.}
  2585. jle @FourBytesOrLess
  2586. add eax, ecx
  2587. add edx, ecx
  2588. neg ecx
  2589. {$ifdef EnableMMX}
  2590. {$ifndef ForceMMX}
  2591. cmp UseMMX, True
  2592. jne @FPUMoveLoop
  2593. {$endif}
  2594. @MMXMoveLoop:
  2595. {Move an 8 byte block}
  2596. {$ifdef Delphi4or5}
  2597. {Delphi 5 compatibility}
  2598. db $0f, $6f, $04, $01
  2599. db $0f, $7f, $04, $11
  2600. {$else}
  2601. movq mm0, [eax + ecx]
  2602. movq [edx + ecx], mm0
  2603. {$endif}
  2604. {Are there another 8 bytes to move?}
  2605. add ecx, 8
  2606. js @MMXMoveLoop
  2607. {Exit MMX state}
  2608. {$ifdef Delphi4or5}
  2609. {Delphi 5 compatibility}
  2610. db $0f, $77
  2611. {$else}
  2612. emms
  2613. {$endif}
  2614. {Do the last 4 bytes}
  2615. mov eax, [eax + ecx]
  2616. mov [edx + ecx], eax
  2617. ret
  2618. {$endif}
  2619. {FPU code is only used if MMX is not forced}
  2620. {$ifndef ForceMMX}
  2621. @FPUMoveLoop:
  2622. {Move an 8 byte block}
  2623. fild qword ptr [eax + ecx]
  2624. fistp qword ptr [edx + ecx]
  2625. {Are there another 8 bytes to move?}
  2626. add ecx, 8
  2627. js @FPUMoveLoop
  2628. {Do the last 4 bytes}
  2629. mov eax, [eax + ecx]
  2630. mov [edx + ecx], eax
  2631. ret
  2632. {$endif}
  2633. @FourBytesOrLess:
  2634. {Four or less bytes to move}
  2635. mov eax, [eax]
  2636. mov [edx], eax
  2637. {$else}
  2638. .noframe
  2639. {Make the counter negative based}
  2640. add rcx, r8
  2641. add rdx, r8
  2642. neg r8
  2643. @MoveLoop:
  2644. {Move an 8 byte block}
  2645. mov r9, [rcx + r8]
  2646. mov [rdx + r8], r9
  2647. {Are there another 8 bytes to move?}
  2648. add r8, 8
  2649. js @MoveLoop
  2650. {$endif}
  2651. end;
  2652. {----------------Windows Emulation Functions for Kylix / OS X Support-----------------}
  2653. {$ifdef POSIX}
  2654. const
  2655. {Messagebox constants}
  2656. MB_OK = 0;
  2657. MB_ICONERROR = $10;
  2658. MB_TASKMODAL = $2000;
  2659. MB_DEFAULT_DESKTOP_ONLY = $20000;
  2660. {Virtual memory constants}
  2661. MEM_COMMIT = $1000;
  2662. MEM_RELEASE = $8000;
  2663. MEM_TOP_DOWN = $100000;
  2664. PAGE_READWRITE = 4;
  2665. procedure MessageBoxA(hWnd: Cardinal; AMessageText, AMessageTitle: PAnsiChar; uType: Cardinal); stdcall;
  2666. begin
  2667. if FastMMIsInstalled then
  2668. writeln(AMessageText)
  2669. else
  2670. __write(STDERR_FILENO, AMessageText, StrLen(AMessageText));
  2671. end;
  2672. function VirtualAlloc(lpvAddress: Pointer; dwSize, flAllocationType, flProtect: Cardinal): Pointer; stdcall;
  2673. begin
  2674. Result := valloc(dwSize);
  2675. end;
  2676. function VirtualFree(lpAddress: Pointer; dwSize, dwFreeType: Cardinal): LongBool; stdcall;
  2677. begin
  2678. free(lpAddress);
  2679. Result := True;
  2680. end;
  2681. function WriteFile(hFile: THandle; const Buffer; nNumberOfBytesToWrite: Cardinal;
  2682. var lpNumberOfBytesWritten: Cardinal; lpOverlapped: Pointer): Boolean; stdcall;
  2683. begin
  2684. lpNumberOfBytesWritten := __write(hFile, @Buffer, nNumberOfBytesToWrite);
  2685. if lpNumberOfBytesWritten = Cardinal(-1) then
  2686. begin
  2687. lpNumberOfBytesWritten := 0;
  2688. Result := False;
  2689. end
  2690. else
  2691. Result := True;
  2692. end;
  2693. {$ifndef NeverSleepOnThreadContention}
  2694. procedure Sleep(dwMilliseconds: Cardinal); stdcall;
  2695. begin
  2696. {Convert to microseconds (more or less)}
  2697. usleep(dwMilliseconds shl 10);
  2698. end;
  2699. {$endif}
  2700. {$endif}
  2701. {-----------------Debugging Support Functions and Procedures------------------}
  2702. {$ifdef FullDebugMode}
  2703. {Returns the current thread ID}
  2704. function GetThreadID: Cardinal;
  2705. {$ifdef 32Bit}
  2706. asm
  2707. mov eax, FS:[$24]
  2708. end;
  2709. {$else}
  2710. begin
  2711. Result := GetCurrentThreadId;
  2712. end;
  2713. {$endif}
  2714. {Fills a block of memory with the given dword (32-bit) or qword (64-bit).
  2715. Always fills a multiple of SizeOf(Pointer) bytes}
  2716. procedure DebugFillMem(var AAddress; AByteCount: NativeInt; AFillValue: NativeUInt);
  2717. asm
  2718. {$ifdef 32Bit}
  2719. {On Entry:
  2720. eax = AAddress
  2721. edx = AByteCount
  2722. ecx = AFillValue}
  2723. add eax, edx
  2724. neg edx
  2725. jns @Done
  2726. @FillLoop:
  2727. mov [eax + edx], ecx
  2728. add edx, 4
  2729. js @FillLoop
  2730. @Done:
  2731. {$else}
  2732. {On Entry:
  2733. rcx = AAddress
  2734. rdx = AByteCount
  2735. r8 = AFillValue}
  2736. add rcx, rdx
  2737. neg rdx
  2738. jns @Done
  2739. @FillLoop:
  2740. mov [rcx + rdx], r8
  2741. add rdx, 8
  2742. js @FillLoop
  2743. @Done:
  2744. {$endif}
  2745. end;
  2746. {$ifndef LoadDebugDLLDynamically}
  2747. {The stack trace procedure. The stack trace module is external since it may
  2748. raise handled access violations that result in the creation of exception
  2749. objects and the stack trace code is not re-entrant.}
  2750. procedure GetStackTrace(AReturnAddresses: PNativeUInt;
  2751. AMaxDepth, ASkipFrames: Cardinal); external FullDebugModeLibraryName
  2752. name {$ifdef RawStackTraces}'GetRawStackTrace'{$else}'GetFrameBasedStackTrace'{$endif};
  2753. {The exported procedure in the FastMM_FullDebugMode.dll library used to convert
  2754. the return addresses of a stack trace to a text string.}
  2755. function LogStackTrace(AReturnAddresses: PNativeUInt;
  2756. AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar; external FullDebugModeLibraryName
  2757. name 'LogStackTrace';
  2758. {$else}
  2759. {Default no-op stack trace and logging handlers}
  2760. procedure NoOpGetStackTrace(AReturnAddresses: PNativeUInt;
  2761. AMaxDepth, ASkipFrames: Cardinal);
  2762. begin
  2763. DebugFillMem(AReturnAddresses^, AMaxDepth * SizeOf(Pointer), 0);
  2764. end;
  2765. function NoOpLogStackTrace(AReturnAddresses: PNativeUInt;
  2766. AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
  2767. begin
  2768. Result := ABuffer;
  2769. end;
  2770. var
  2771. {Handle to the FullDebugMode DLL}
  2772. FullDebugModeDLL: HMODULE;
  2773. GetStackTrace: procedure (AReturnAddresses: PNativeUInt;
  2774. AMaxDepth, ASkipFrames: Cardinal) = NoOpGetStackTrace;
  2775. LogStackTrace: function (AReturnAddresses: PNativeUInt;
  2776. AMaxDepth: Cardinal; ABuffer: PAnsiChar): PAnsiChar = NoOpLogStackTrace;
  2777. {$endif}
  2778. {$endif}
  2779. {$ifndef POSIX}
  2780. function DelphiIsRunning: Boolean;
  2781. begin
  2782. Result := FindWindowA('TAppBuilder', nil) <> 0;
  2783. end;
  2784. {$endif}
  2785. {Converts an unsigned integer to string at the buffer location, returning the
  2786. new buffer position. Note: The 32-bit asm version only supports numbers up to
  2787. 2^31 - 1.}
  2788. function NativeUIntToStrBuf(ANum: NativeUInt; APBuffer: PAnsiChar): PAnsiChar;
  2789. {$ifndef Use32BitAsm}
  2790. const
  2791. MaxDigits = 20;
  2792. var
  2793. LDigitBuffer: array[0..MaxDigits - 1] of AnsiChar;
  2794. LCount: Cardinal;
  2795. LDigit: NativeUInt;
  2796. begin
  2797. {Generate the digits in the local buffer}
  2798. LCount := 0;
  2799. repeat
  2800. LDigit := ANum;
  2801. ANum := ANum div 10;
  2802. LDigit := LDigit - ANum * 10;
  2803. Inc(LCount);
  2804. LDigitBuffer[MaxDigits - LCount] := AnsiChar(Ord('0') + LDigit);
  2805. until ANum = 0;
  2806. {Copy the digits to the output buffer and advance it}
  2807. System.Move(LDigitBuffer[MaxDigits - LCount], APBuffer^, LCount);
  2808. Result := APBuffer + LCount;
  2809. end;
  2810. {$else}
  2811. asm
  2812. {On entry: eax = ANum, edx = ABuffer}
  2813. push edi
  2814. mov edi, edx //Pointer to the first character in edi
  2815. {Calculate leading digit: divide the number by 1e9}
  2816. add eax, 1 //Increment the number
  2817. mov edx, $89705F41 //1e9 reciprocal
  2818. mul edx //Multplying with reciprocal
  2819. shr eax, 30 //Save fraction bits
  2820. mov ecx, edx //First digit in bits <31:29>
  2821. and edx, $1FFFFFFF //Filter fraction part edx<28:0>
  2822. shr ecx, 29 //Get leading digit into accumulator
  2823. lea edx, [edx + 4 * edx] //Calculate ...
  2824. add edx, eax //... 5*fraction
  2825. mov eax, ecx //Copy leading digit
  2826. or eax, '0' //Convert digit to ASCII
  2827. mov [edi], al //Store digit out to memory
  2828. {Calculate digit #2}
  2829. mov eax, edx //Point format such that 1.0 = 2^28
  2830. cmp ecx, 1 //Any non-zero digit yet ?
  2831. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2832. shr eax, 28 //Next digit
  2833. and edx, $0fffffff //Fraction part edx<27:0>
  2834. or ecx, eax //Accumulate next digit
  2835. or eax, '0' //Convert digit to ASCII
  2836. mov [edi], al //Store digit out to memory
  2837. {Calculate digit #3}
  2838. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:27>
  2839. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<26:0>
  2840. cmp ecx, 1 //Any non-zero digit yet ?
  2841. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2842. shr eax, 27 //Next digit
  2843. and edx, $07ffffff //Fraction part
  2844. or ecx, eax //Accumulate next digit
  2845. or eax, '0' //Convert digit to ASCII
  2846. mov [edi], al //Store digit out to memory
  2847. {Calculate digit #4}
  2848. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:26>
  2849. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<25:0>
  2850. cmp ecx, 1 //Any non-zero digit yet ?
  2851. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2852. shr eax, 26 //Next digit
  2853. and edx, $03ffffff //Fraction part
  2854. or ecx, eax //Accumulate next digit
  2855. or eax, '0' //Convert digit to ASCII
  2856. mov [edi], al //Store digit out to memory
  2857. {Calculate digit #5}
  2858. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:25>
  2859. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<24:0>
  2860. cmp ecx, 1 //Any non-zero digit yet ?
  2861. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2862. shr eax, 25 //Next digit
  2863. and edx, $01ffffff //Fraction part
  2864. or ecx, eax //Accumulate next digit
  2865. or eax, '0' //Convert digit to ASCII
  2866. mov [edi], al //Store digit out to memory
  2867. {Calculate digit #6}
  2868. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:24>
  2869. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<23:0>
  2870. cmp ecx, 1 //Any non-zero digit yet ?
  2871. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2872. shr eax, 24 //Next digit
  2873. and edx, $00ffffff //Fraction part
  2874. or ecx, eax //Accumulate next digit
  2875. or eax, '0' //Convert digit to ASCII
  2876. mov [edi], al //Store digit out to memory
  2877. {Calculate digit #7}
  2878. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:23>
  2879. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<31:23>
  2880. cmp ecx, 1 //Any non-zero digit yet ?
  2881. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2882. shr eax, 23 //Next digit
  2883. and edx, $007fffff //Fraction part
  2884. or ecx, eax //Accumulate next digit
  2885. or eax, '0' //Convert digit to ASCII
  2886. mov [edi], al //Store digit out to memory
  2887. {Calculate digit #8}
  2888. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:22>
  2889. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<22:0>
  2890. cmp ecx, 1 //Any non-zero digit yet ?
  2891. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2892. shr eax, 22 //Next digit
  2893. and edx, $003fffff //Fraction part
  2894. or ecx, eax //Accumulate next digit
  2895. or eax, '0' //Convert digit to ASCII
  2896. mov [edi], al //Store digit out to memory
  2897. {Calculate digit #9}
  2898. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:21>
  2899. lea edx, [edx * 4 + edx] //5*fraction, new fraction edx<21:0>
  2900. cmp ecx, 1 //Any non-zero digit yet ?
  2901. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2902. shr eax, 21 //Next digit
  2903. and edx, $001fffff //Fraction part
  2904. or ecx, eax //Accumulate next digit
  2905. or eax, '0' //Convert digit to ASCII
  2906. mov [edi], al //Store digit out to memory
  2907. {Calculate digit #10}
  2908. lea eax, [edx * 4 + edx] //5*fraction, new digit eax<31:20>
  2909. cmp ecx, 1 //Any-non-zero digit yet ?
  2910. sbb edi, -1 //Yes->increment ptr, No->keep old ptr
  2911. shr eax, 20 //Next digit
  2912. or eax, '0' //Convert digit to ASCII
  2913. mov [edi], al //Store last digit and end marker out to memory
  2914. {Return a pointer to the next character}
  2915. lea eax, [edi + 1]
  2916. {Restore edi}
  2917. pop edi
  2918. end;
  2919. {$endif}
  2920. {Converts an unsigned integer to a hexadecimal string at the buffer location,
  2921. returning the new buffer position.}
  2922. function NativeUIntToHexBuf(ANum: NativeUInt; APBuffer: PAnsiChar): PAnsiChar;
  2923. {$ifndef Use32BitAsm}
  2924. const
  2925. MaxDigits = 16;
  2926. var
  2927. LDigitBuffer: array[0..MaxDigits - 1] of AnsiChar;
  2928. LCount: Cardinal;
  2929. LDigit: NativeUInt;
  2930. begin
  2931. {Generate the digits in the local buffer}
  2932. LCount := 0;
  2933. repeat
  2934. LDigit := ANum;
  2935. ANum := ANum div 16;
  2936. LDigit := LDigit - ANum * 16;
  2937. Inc(LCount);
  2938. LDigitBuffer[MaxDigits - LCount] := HexTable[LDigit];
  2939. until ANum = 0;
  2940. {Copy the digits to the output buffer and advance it}
  2941. System.Move(LDigitBuffer[MaxDigits - LCount], APBuffer^, LCount);
  2942. Result := APBuffer + LCount;
  2943. end;
  2944. {$else}
  2945. asm
  2946. {On entry:
  2947. eax = ANum
  2948. edx = ABuffer}
  2949. push ebx
  2950. push edi
  2951. {Save ANum in ebx}
  2952. mov ebx, eax
  2953. {Get a pointer to the first character in edi}
  2954. mov edi, edx
  2955. {Get the number in ecx as well}
  2956. mov ecx, eax
  2957. {Keep the low nibbles in ebx and the high nibbles in ecx}
  2958. and ebx, $0f0f0f0f
  2959. and ecx, $f0f0f0f0
  2960. {Swap the bytes into the right order}
  2961. ror ebx, 16
  2962. ror ecx, 20
  2963. {Get nibble 7}
  2964. movzx eax, ch
  2965. mov dl, ch
  2966. mov al, byte ptr HexTable[eax]
  2967. mov [edi], al
  2968. cmp dl, 1
  2969. sbb edi, -1
  2970. {Get nibble 6}
  2971. movzx eax, bh
  2972. or dl, bh
  2973. mov al, byte ptr HexTable[eax]
  2974. mov [edi], al
  2975. cmp dl, 1
  2976. sbb edi, -1
  2977. {Get nibble 5}
  2978. movzx eax, cl
  2979. or dl, cl
  2980. mov al, byte ptr HexTable[eax]
  2981. mov [edi], al
  2982. cmp dl, 1
  2983. sbb edi, -1
  2984. {Get nibble 4}
  2985. movzx eax, bl
  2986. or dl, bl
  2987. mov al, byte ptr HexTable[eax]
  2988. mov [edi], al
  2989. cmp dl, 1
  2990. sbb edi, -1
  2991. {Rotate ecx and ebx so we get access to the rest}
  2992. shr ebx, 16
  2993. shr ecx, 16
  2994. {Get nibble 3}
  2995. movzx eax, ch
  2996. or dl, ch
  2997. mov al, byte ptr HexTable[eax]
  2998. mov [edi], al
  2999. cmp dl, 1
  3000. sbb edi, -1
  3001. {Get nibble 2}
  3002. movzx eax, bh
  3003. or dl, bh
  3004. mov al, byte ptr HexTable[eax]
  3005. mov [edi], al
  3006. cmp dl, 1
  3007. sbb edi, -1
  3008. {Get nibble 1}
  3009. movzx eax, cl
  3010. or dl, cl
  3011. mov al, byte ptr HexTable[eax]
  3012. mov [edi], al
  3013. cmp dl, 1
  3014. sbb edi, -1
  3015. {Get nibble 0}
  3016. movzx eax, bl
  3017. mov al, byte ptr HexTable[eax]
  3018. mov [edi], al
  3019. {Return a pointer to the end of the string}
  3020. lea eax, [edi + 1]
  3021. {Restore registers}
  3022. pop edi
  3023. pop ebx
  3024. end;
  3025. {$endif}
  3026. {Appends the source text to the destination and returns the new destination
  3027. position}
  3028. function AppendStringToBuffer(const ASource, ADestination: PAnsiChar; ACount: Cardinal): PAnsiChar;
  3029. begin
  3030. System.Move(ASource^, ADestination^, ACount);
  3031. Result := Pointer(PByte(ADestination) + ACount);
  3032. end;
  3033. {Appends the name of the class to the destination buffer and returns the new
  3034. destination position}
  3035. function AppendClassNameToBuffer(AClass: TClass; ADestination: PAnsiChar): PAnsiChar;
  3036. var
  3037. LPClassName: PShortString;
  3038. begin
  3039. {Get a pointer to the class name}
  3040. if AClass <> nil then
  3041. begin
  3042. LPClassName := PShortString(PPointer(PByte(AClass) + vmtClassName)^);
  3043. {Append the class name}
  3044. Result := AppendStringToBuffer(@LPClassName^[1], ADestination, Length(LPClassName^));
  3045. end
  3046. else
  3047. begin
  3048. Result := AppendStringToBuffer(UnknownClassNameMsg, ADestination, Length(UnknownClassNameMsg));
  3049. end;
  3050. end;
  3051. {Shows a message box if the program is not showing one already.}
  3052. procedure ShowMessageBox(AText, ACaption: PAnsiChar);
  3053. begin
  3054. if (not ShowingMessageBox) and (not SuppressMessageBoxes) then
  3055. begin
  3056. ShowingMessageBox := True;
  3057. MessageBoxA(0, AText, ACaption,
  3058. MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_DEFAULT_DESKTOP_ONLY);
  3059. ShowingMessageBox := False;
  3060. end;
  3061. end;
  3062. {Returns the class for a memory block. Returns nil if it is not a valid class}
  3063. function DetectClassInstance(APointer: Pointer): TClass;
  3064. {$ifndef POSIX}
  3065. var
  3066. LMemInfo: TMemoryBasicInformation;
  3067. {Checks whether the given address is a valid address for a VMT entry.}
  3068. function IsValidVMTAddress(APAddress: Pointer): Boolean;
  3069. begin
  3070. {Do some basic pointer checks: Must be dword aligned and beyond 64K}
  3071. if (UIntPtr(APAddress) > 65535)
  3072. and (UIntPtr(APAddress) and 3 = 0) then
  3073. begin
  3074. {Do we need to recheck the virtual memory?}
  3075. if (UIntPtr(LMemInfo.BaseAddress) > UIntPtr(APAddress))
  3076. or ((UIntPtr(LMemInfo.BaseAddress) + LMemInfo.RegionSize) < (UIntPtr(APAddress) + 4)) then
  3077. begin
  3078. {Get the VM status for the pointer}
  3079. LMemInfo.RegionSize := 0;
  3080. VirtualQuery(APAddress, LMemInfo, SizeOf(LMemInfo));
  3081. end;
  3082. {Check the readability of the memory address}
  3083. Result := (LMemInfo.RegionSize >= 4)
  3084. and (LMemInfo.State = MEM_COMMIT)
  3085. 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)
  3086. and (LMemInfo.Protect and PAGE_GUARD = 0);
  3087. end
  3088. else
  3089. Result := False;
  3090. end;
  3091. {Returns true if AClassPointer points to a class VMT}
  3092. function InternalIsValidClass(AClassPointer: Pointer; ADepth: Integer = 0): Boolean;
  3093. var
  3094. LParentClassSelfPointer: PPointer;
  3095. begin
  3096. {Check that the self pointer as well as parent class self pointer addresses
  3097. are valid}
  3098. if (ADepth < 1000)
  3099. and IsValidVMTAddress(Pointer(PByte(AClassPointer) + vmtSelfPtr))
  3100. and IsValidVMTAddress(Pointer(PByte(AClassPointer) + vmtParent)) then
  3101. begin
  3102. {Get a pointer to the parent class' self pointer}
  3103. LParentClassSelfPointer := PPointer(PByte(AClassPointer) + vmtParent)^;
  3104. {Check that the self pointer as well as the parent class is valid}
  3105. Result := (PPointer(PByte(AClassPointer) + vmtSelfPtr)^ = AClassPointer)
  3106. and ((LParentClassSelfPointer = nil)
  3107. or (IsValidVMTAddress(LParentClassSelfPointer)
  3108. and InternalIsValidClass(LParentClassSelfPointer^, ADepth + 1)));
  3109. end
  3110. else
  3111. Result := False;
  3112. end;
  3113. begin
  3114. {Get the class pointer from the (suspected) object}
  3115. Result := TClass(PPointer(APointer)^);
  3116. {No VM info yet}
  3117. LMemInfo.RegionSize := 0;
  3118. {Check the block}
  3119. if (not InternalIsValidClass(Pointer(Result), 0))
  3120. {$ifdef FullDebugMode}
  3121. or (Result = @FreedObjectVMT.VMTMethods[0])
  3122. {$endif}
  3123. then
  3124. Result := nil;
  3125. end;
  3126. {$else}
  3127. begin
  3128. {Not currently supported under Linux / OS X}
  3129. Result := nil;
  3130. end;
  3131. {$endif}
  3132. {Gets the available size inside a block}
  3133. function GetAvailableSpaceInBlock(APointer: Pointer): NativeUInt;
  3134. var
  3135. LBlockHeader: NativeUInt;
  3136. LPSmallBlockPool: PSmallBlockPoolHeader;
  3137. begin
  3138. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  3139. if LBlockHeader and (IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
  3140. begin
  3141. LPSmallBlockPool := PSmallBlockPoolHeader(LBlockHeader and DropSmallFlagsMask);
  3142. Result := LPSmallBlockPool.BlockType.BlockSize - BlockHeaderSize;
  3143. end
  3144. else
  3145. begin
  3146. Result := (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
  3147. if (LBlockHeader and IsMediumBlockFlag) = 0 then
  3148. Dec(Result, LargeBlockHeaderSize);
  3149. end;
  3150. end;
  3151. {-----------------Small Block Management------------------}
  3152. {Locks all small block types}
  3153. procedure LockAllSmallBlockTypes;
  3154. var
  3155. LInd: Cardinal;
  3156. begin
  3157. {Lock the medium blocks}
  3158. {$ifndef AssumeMultiThreaded}
  3159. if IsMultiThread then
  3160. {$endif}
  3161. begin
  3162. for LInd := 0 to NumSmallBlockTypes - 1 do
  3163. begin
  3164. while LockCmpxchg(0, 1, @SmallBlockTypes[LInd].BlockTypeLocked) <> 0 do
  3165. begin
  3166. {$ifdef NeverSleepOnThreadContention}
  3167. {$ifdef UseSwitchToThread}
  3168. SwitchToThread;
  3169. {$endif}
  3170. {$else}
  3171. Sleep(InitialSleepTime);
  3172. if LockCmpxchg(0, 1, @SmallBlockTypes[LInd].BlockTypeLocked) = 0 then
  3173. Break;
  3174. Sleep(AdditionalSleepTime);
  3175. {$endif}
  3176. end;
  3177. end;
  3178. end;
  3179. end;
  3180. {Gets the first and last block pointer for a small block pool}
  3181. procedure GetFirstAndLastSmallBlockInPool(APSmallBlockPool: PSmallBlockPoolHeader;
  3182. var AFirstPtr, ALastPtr: Pointer);
  3183. var
  3184. LBlockSize: NativeUInt;
  3185. begin
  3186. {Get the pointer to the first block}
  3187. AFirstPtr := Pointer(PByte(APSmallBlockPool) + SmallBlockPoolHeaderSize);
  3188. {Get a pointer to the last block}
  3189. if (APSmallBlockPool.BlockType.CurrentSequentialFeedPool <> APSmallBlockPool)
  3190. or (UIntPtr(APSmallBlockPool.BlockType.NextSequentialFeedBlockAddress) > UIntPtr(APSmallBlockPool.BlockType.MaxSequentialFeedBlockAddress)) then
  3191. begin
  3192. {Not the sequential feed - point to the end of the block}
  3193. LBlockSize := PNativeUInt(PByte(APSmallBlockPool) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  3194. ALastPtr := Pointer(PByte(APSmallBlockPool) + LBlockSize - APSmallBlockPool.BlockType.BlockSize);
  3195. end
  3196. else
  3197. begin
  3198. {The sequential feed pool - point to before the next sequential feed block}
  3199. ALastPtr := Pointer(PByte(APSmallBlockPool.BlockType.NextSequentialFeedBlockAddress) - 1);
  3200. end;
  3201. end;
  3202. {-----------------Medium Block Management------------------}
  3203. {Advances to the next medium block. Returns nil if the end of the medium block
  3204. pool has been reached}
  3205. function NextMediumBlock(APMediumBlock: Pointer): Pointer;
  3206. var
  3207. LBlockSize: NativeUInt;
  3208. begin
  3209. {Get the size of this block}
  3210. LBlockSize := PNativeUInt(PByte(APMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  3211. {Advance the pointer}
  3212. Result := Pointer(PByte(APMediumBlock) + LBlockSize);
  3213. {Is the next block the end of medium pool marker?}
  3214. LBlockSize := PNativeUInt(PByte(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  3215. if LBlockSize = 0 then
  3216. Result := nil;
  3217. end;
  3218. {Gets the first medium block in the medium block pool}
  3219. function GetFirstMediumBlockInPool(APMediumBlockPoolHeader: PMediumBlockPoolHeader): Pointer;
  3220. begin
  3221. if (MediumSequentialFeedBytesLeft = 0)
  3222. or (UIntPtr(LastSequentiallyFedMediumBlock) < UIntPtr(APMediumBlockPoolHeader))
  3223. or (UIntPtr(LastSequentiallyFedMediumBlock) > UIntPtr(APMediumBlockPoolHeader) + MediumBlockPoolSize) then
  3224. begin
  3225. Result := Pointer(PByte(APMediumBlockPoolHeader) + MediumBlockPoolHeaderSize);
  3226. end
  3227. else
  3228. begin
  3229. {Is the sequential feed pool empty?}
  3230. if MediumSequentialFeedBytesLeft <> MediumBlockPoolSize - MediumBlockPoolHeaderSize then
  3231. Result := LastSequentiallyFedMediumBlock
  3232. else
  3233. Result := nil;
  3234. end;
  3235. end;
  3236. {Locks the medium blocks. Note that the 32-bit asm version is assumed to
  3237. preserve all registers except eax.}
  3238. {$ifndef Use32BitAsm}
  3239. procedure LockMediumBlocks;
  3240. begin
  3241. {Lock the medium blocks}
  3242. {$ifndef AssumeMultiThreaded}
  3243. if IsMultiThread then
  3244. {$endif}
  3245. begin
  3246. while LockCmpxchg(0, 1, @MediumBlocksLocked) <> 0 do
  3247. begin
  3248. {$ifdef NeverSleepOnThreadContention}
  3249. {$ifdef UseSwitchToThread}
  3250. SwitchToThread;
  3251. {$endif}
  3252. {$else}
  3253. Sleep(InitialSleepTime);
  3254. if LockCmpxchg(0, 1, @MediumBlocksLocked) = 0 then
  3255. Break;
  3256. Sleep(AdditionalSleepTime);
  3257. {$endif}
  3258. end;
  3259. end;
  3260. end;
  3261. {$else}
  3262. procedure LockMediumBlocks;
  3263. asm
  3264. {Note: This routine is assumed to preserve all registers except eax}
  3265. @MediumBlockLockLoop:
  3266. mov eax, $100
  3267. {Attempt to lock the medium blocks}
  3268. lock cmpxchg MediumBlocksLocked, ah
  3269. je @Done
  3270. {$ifdef NeverSleepOnThreadContention}
  3271. {Pause instruction (improves performance on P4)}
  3272. rep nop
  3273. {$ifdef UseSwitchToThread}
  3274. push ecx
  3275. push edx
  3276. call SwitchToThread
  3277. pop edx
  3278. pop ecx
  3279. {$endif}
  3280. {Try again}
  3281. jmp @MediumBlockLockLoop
  3282. {$else}
  3283. {Couldn't lock the medium blocks - sleep and try again}
  3284. push ecx
  3285. push edx
  3286. push InitialSleepTime
  3287. call Sleep
  3288. pop edx
  3289. pop ecx
  3290. {Try again}
  3291. mov eax, $100
  3292. {Attempt to grab the block type}
  3293. lock cmpxchg MediumBlocksLocked, ah
  3294. je @Done
  3295. {Couldn't lock the medium blocks - sleep and try again}
  3296. push ecx
  3297. push edx
  3298. push AdditionalSleepTime
  3299. call Sleep
  3300. pop edx
  3301. pop ecx
  3302. {Try again}
  3303. jmp @MediumBlockLockLoop
  3304. {$endif}
  3305. @Done:
  3306. end;
  3307. {$endif}
  3308. {Removes a medium block from the circular linked list of free blocks.
  3309. Does not change any header flags. Medium blocks should be locked
  3310. before calling this procedure.}
  3311. procedure RemoveMediumFreeBlock(APMediumFreeBlock: PMediumFreeBlock);
  3312. {$ifndef ASMVersion}
  3313. var
  3314. LPreviousFreeBlock, LNextFreeBlock: PMediumFreeBlock;
  3315. LBinNumber, LBinGroupNumber: Cardinal;
  3316. begin
  3317. {Get the current previous and next blocks}
  3318. LNextFreeBlock := APMediumFreeBlock.NextFreeBlock;
  3319. LPreviousFreeBlock := APMediumFreeBlock.PreviousFreeBlock;
  3320. {Remove this block from the linked list}
  3321. LPreviousFreeBlock.NextFreeBlock := LNextFreeBlock;
  3322. LNextFreeBlock.PreviousFreeBlock := LPreviousFreeBlock;
  3323. {Is this bin now empty? If the previous and next free block pointers are
  3324. equal, they must point to the bin.}
  3325. if LPreviousFreeBlock = LNextFreeBlock then
  3326. begin
  3327. {Get the bin number for this block size}
  3328. LBinNumber := (UIntPtr(LNextFreeBlock) - UIntPtr(@MediumBlockBins)) div SizeOf(TMediumFreeBlock);
  3329. LBinGroupNumber := LBinNumber div 32;
  3330. {Flag this bin as empty}
  3331. MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
  3332. and (not (1 shl (LBinNumber and 31)));
  3333. {Is the group now entirely empty?}
  3334. if MediumBlockBinBitmaps[LBinGroupNumber] = 0 then
  3335. begin
  3336. {Flag this group as empty}
  3337. MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
  3338. and (not (1 shl LBinGroupNumber));
  3339. end;
  3340. end;
  3341. end;
  3342. {$else}
  3343. {$ifdef 32Bit}
  3344. asm
  3345. {On entry: eax = APMediumFreeBlock}
  3346. {Get the current previous and next blocks}
  3347. mov ecx, TMediumFreeBlock[eax].NextFreeBlock
  3348. mov edx, TMediumFreeBlock[eax].PreviousFreeBlock
  3349. {Is this bin now empty? If the previous and next free block pointers are
  3350. equal, they must point to the bin.}
  3351. cmp ecx, edx
  3352. {Remove this block from the linked list}
  3353. mov TMediumFreeBlock[ecx].PreviousFreeBlock, edx
  3354. mov TMediumFreeBlock[edx].NextFreeBlock, ecx
  3355. {Is this bin now empty? If the previous and next free block pointers are
  3356. equal, they must point to the bin.}
  3357. je @BinIsNowEmpty
  3358. @Done:
  3359. ret
  3360. {Align branch target}
  3361. nop
  3362. @BinIsNowEmpty:
  3363. {Get the bin number for this block size in ecx}
  3364. sub ecx, offset MediumBlockBins
  3365. mov edx, ecx
  3366. shr ecx, 3
  3367. {Get the group number in edx}
  3368. movzx edx, dh
  3369. {Flag this bin as empty}
  3370. mov eax, -2
  3371. rol eax, cl
  3372. and dword ptr [MediumBlockBinBitmaps + edx * 4], eax
  3373. jnz @Done
  3374. {Flag this group as empty}
  3375. mov eax, -2
  3376. mov ecx, edx
  3377. rol eax, cl
  3378. and MediumBlockBinGroupBitmap, eax
  3379. end;
  3380. {$else}
  3381. asm
  3382. {On entry: rcx = APMediumFreeBlock}
  3383. mov rax, rcx
  3384. {Get the current previous and next blocks}
  3385. mov rcx, TMediumFreeBlock[rax].NextFreeBlock
  3386. mov rdx, TMediumFreeBlock[rax].PreviousFreeBlock
  3387. {Is this bin now empty? If the previous and next free block pointers are
  3388. equal, they must point to the bin.}
  3389. cmp rcx, rdx
  3390. {Remove this block from the linked list}
  3391. mov TMediumFreeBlock[rcx].PreviousFreeBlock, rdx
  3392. mov TMediumFreeBlock[rdx].NextFreeBlock, rcx
  3393. {Is this bin now empty? If the previous and next free block pointers are
  3394. equal, they must point to the bin.}
  3395. jne @Done
  3396. {Get the bin number for this block size in rcx}
  3397. lea r8, MediumBlockBins
  3398. sub rcx, r8
  3399. mov edx, ecx
  3400. shr ecx, 4
  3401. {Get the group number in edx}
  3402. shr edx, 9
  3403. {Flag this bin as empty}
  3404. mov eax, -2
  3405. rol eax, cl
  3406. lea r8, MediumBlockBinBitmaps
  3407. and dword ptr [r8 + rdx * 4], eax
  3408. jnz @Done
  3409. {Flag this group as empty}
  3410. mov eax, -2
  3411. mov ecx, edx
  3412. rol eax, cl
  3413. and MediumBlockBinGroupBitmap, eax
  3414. @Done:
  3415. end;
  3416. {$endif}
  3417. {$endif}
  3418. {Inserts a medium block into the appropriate medium block bin.}
  3419. procedure InsertMediumBlockIntoBin(APMediumFreeBlock: PMediumFreeBlock; AMediumBlockSize: Cardinal);
  3420. {$ifndef ASMVersion}
  3421. var
  3422. LBinNumber, LBinGroupNumber: Cardinal;
  3423. LPBin, LPFirstFreeBlock: PMediumFreeBlock;
  3424. begin
  3425. {Get the bin number for this block size. Get the bin that holds blocks of at
  3426. least this size.}
  3427. LBinNumber := (AMediumBlockSize - MinimumMediumBlockSize) div MediumBlockGranularity;
  3428. if LBinNumber >= MediumBlockBinCount then
  3429. LBinNumber := MediumBlockBinCount - 1;
  3430. {Get the bin}
  3431. LPBin := @MediumBlockBins[LBinNumber];
  3432. {Bins are LIFO, se we insert this block as the first free block in the bin}
  3433. LPFirstFreeBlock := LPBin.NextFreeBlock;
  3434. APMediumFreeBlock.PreviousFreeBlock := LPBin;
  3435. APMediumFreeBlock.NextFreeBlock := LPFirstFreeBlock;
  3436. LPFirstFreeBlock.PreviousFreeBlock := APMediumFreeBlock;
  3437. LPBin.NextFreeBlock := APMediumFreeBlock;
  3438. {Was this bin empty?}
  3439. if LPFirstFreeBlock = LPBin then
  3440. begin
  3441. {Get the group number}
  3442. LBinGroupNumber := LBinNumber div 32;
  3443. {Flag this bin as used}
  3444. MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
  3445. or (1 shl (LBinNumber and 31));
  3446. {Flag the group as used}
  3447. MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
  3448. or (1 shl LBinGroupNumber);
  3449. end;
  3450. end;
  3451. {$else}
  3452. {$ifdef 32Bit}
  3453. asm
  3454. {On entry: eax = APMediumFreeBlock, edx = AMediumBlockSize}
  3455. {Get the bin number for this block size. Get the bin that holds blocks of at
  3456. least this size.}
  3457. sub edx, MinimumMediumBlockSize
  3458. shr edx, 8
  3459. {Validate the bin number}
  3460. sub edx, MediumBlockBinCount - 1
  3461. sbb ecx, ecx
  3462. and edx, ecx
  3463. add edx, MediumBlockBinCount - 1
  3464. {Get the bin in ecx}
  3465. lea ecx, [MediumBlockBins + edx * 8]
  3466. {Bins are LIFO, se we insert this block as the first free block in the bin}
  3467. mov edx, TMediumFreeBlock[ecx].NextFreeBlock
  3468. {Was this bin empty?}
  3469. cmp edx, ecx
  3470. mov TMediumFreeBlock[eax].PreviousFreeBlock, ecx
  3471. mov TMediumFreeBlock[eax].NextFreeBlock, edx
  3472. mov TMediumFreeBlock[edx].PreviousFreeBlock, eax
  3473. mov TMediumFreeBlock[ecx].NextFreeBlock, eax
  3474. {Was this bin empty?}
  3475. je @BinWasEmpty
  3476. ret
  3477. {Align branch target}
  3478. nop
  3479. nop
  3480. @BinWasEmpty:
  3481. {Get the bin number in ecx}
  3482. sub ecx, offset MediumBlockBins
  3483. mov edx, ecx
  3484. shr ecx, 3
  3485. {Get the group number in edx}
  3486. movzx edx, dh
  3487. {Flag this bin as not empty}
  3488. mov eax, 1
  3489. shl eax, cl
  3490. or dword ptr [MediumBlockBinBitmaps + edx * 4], eax
  3491. {Flag the group as not empty}
  3492. mov eax, 1
  3493. mov ecx, edx
  3494. shl eax, cl
  3495. or MediumBlockBinGroupBitmap, eax
  3496. end;
  3497. {$else}
  3498. asm
  3499. {On entry: rax = APMediumFreeBlock, edx = AMediumBlockSize}
  3500. mov rax, rcx
  3501. {Get the bin number for this block size. Get the bin that holds blocks of at
  3502. least this size.}
  3503. sub edx, MinimumMediumBlockSize
  3504. shr edx, 8
  3505. {Validate the bin number}
  3506. sub edx, MediumBlockBinCount - 1
  3507. sbb ecx, ecx
  3508. and edx, ecx
  3509. add edx, MediumBlockBinCount - 1
  3510. mov r9, rdx
  3511. {Get the bin address in rcx}
  3512. lea rcx, MediumBlockBins
  3513. shl edx, 4
  3514. add rcx, rdx
  3515. {Bins are LIFO, se we insert this block as the first free block in the bin}
  3516. mov rdx, TMediumFreeBlock[rcx].NextFreeBlock
  3517. {Was this bin empty?}
  3518. cmp rdx, rcx
  3519. mov TMediumFreeBlock[rax].PreviousFreeBlock, rcx
  3520. mov TMediumFreeBlock[rax].NextFreeBlock, rdx
  3521. mov TMediumFreeBlock[rdx].PreviousFreeBlock, rax
  3522. mov TMediumFreeBlock[rcx].NextFreeBlock, rax
  3523. {Was this bin empty?}
  3524. jne @Done
  3525. {Get the bin number in ecx}
  3526. mov rcx, r9
  3527. {Get the group number in edx}
  3528. mov rdx, r9
  3529. shr edx, 5
  3530. {Flag this bin as not empty}
  3531. mov eax, 1
  3532. shl eax, cl
  3533. lea r8, MediumBlockBinBitmaps
  3534. or dword ptr [r8 + rdx * 4], eax
  3535. {Flag the group as not empty}
  3536. mov eax, 1
  3537. mov ecx, edx
  3538. shl eax, cl
  3539. or MediumBlockBinGroupBitmap, eax
  3540. @Done:
  3541. end;
  3542. {$endif}
  3543. {$endif}
  3544. {Bins what remains in the current sequential feed medium block pool. Medium
  3545. blocks must be locked.}
  3546. procedure BinMediumSequentialFeedRemainder;
  3547. {$ifndef ASMVersion}
  3548. var
  3549. LSequentialFeedFreeSize, LNextBlockSizeAndFlags: NativeUInt;
  3550. LPRemainderBlock, LNextMediumBlock: Pointer;
  3551. begin
  3552. LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
  3553. if LSequentialFeedFreeSize > 0 then
  3554. begin
  3555. {Get the block after the open space}
  3556. LNextMediumBlock := LastSequentiallyFedMediumBlock;
  3557. LNextBlockSizeAndFlags := PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^;
  3558. {Point to the remainder}
  3559. LPRemainderBlock := Pointer(PByte(LNextMediumBlock) - LSequentialFeedFreeSize);
  3560. {$ifndef FullDebugMode}
  3561. {Can the next block be combined with the remainder?}
  3562. if (LNextBlockSizeAndFlags and IsFreeBlockFlag) <> 0 then
  3563. begin
  3564. {Increase the size of this block}
  3565. Inc(LSequentialFeedFreeSize, LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask);
  3566. {Remove the next block as well}
  3567. if (LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask) >= MinimumMediumBlockSize then
  3568. RemoveMediumFreeBlock(LNextMediumBlock);
  3569. end
  3570. else
  3571. begin
  3572. {$endif}
  3573. {Set the "previous block is free" flag of the next block}
  3574. PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^ := LNextBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
  3575. {$ifndef FullDebugMode}
  3576. end;
  3577. {$endif}
  3578. {Store the size of the block as well as the flags}
  3579. PNativeUInt(PByte(LPRemainderBlock) - BlockHeaderSize)^ := LSequentialFeedFreeSize or IsMediumBlockFlag or IsFreeBlockFlag;
  3580. {Store the trailing size marker}
  3581. PNativeUInt(PByte(LPRemainderBlock) + LSequentialFeedFreeSize - BlockHeaderSize * 2)^ := LSequentialFeedFreeSize;
  3582. {$ifdef FullDebugMode}
  3583. {In full debug mode the sequential feed remainder will never be too small to
  3584. fit a full debug header.}
  3585. {Clear the user area of the block}
  3586. DebugFillMem(Pointer(PByte(LPRemainderBlock) + SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt))^,
  3587. LSequentialFeedFreeSize - FullDebugBlockOverhead - SizeOf(NativeUInt),
  3588. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  3589. {We need to set a valid debug header and footer in the remainder}
  3590. PFullDebugBlockHeader(LPRemainderBlock).HeaderCheckSum := NativeUInt(LPRemainderBlock);
  3591. PNativeUInt(PByte(LPRemainderBlock) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(LPRemainderBlock);
  3592. {$endif}
  3593. {Bin this medium block}
  3594. if LSequentialFeedFreeSize >= MinimumMediumBlockSize then
  3595. InsertMediumBlockIntoBin(LPRemainderBlock, LSequentialFeedFreeSize);
  3596. end;
  3597. end;
  3598. {$else}
  3599. {$ifdef 32Bit}
  3600. asm
  3601. cmp MediumSequentialFeedBytesLeft, 0
  3602. jne @MustBinMedium
  3603. {Nothing to bin}
  3604. ret
  3605. {Align branch target}
  3606. nop
  3607. nop
  3608. @MustBinMedium:
  3609. {Get a pointer to the last sequentially allocated medium block}
  3610. mov eax, LastSequentiallyFedMediumBlock
  3611. {Is the block that was last fed sequentially free?}
  3612. test byte ptr [eax - 4], IsFreeBlockFlag
  3613. jnz @LastBlockFedIsFree
  3614. {Set the "previous block is free" flag in the last block fed}
  3615. or dword ptr [eax - 4], PreviousMediumBlockIsFreeFlag
  3616. {Get the remainder in edx}
  3617. mov edx, MediumSequentialFeedBytesLeft
  3618. {Point eax to the start of the remainder}
  3619. sub eax, edx
  3620. @BinTheRemainder:
  3621. {Status: eax = start of remainder, edx = size of remainder}
  3622. {Store the size of the block as well as the flags}
  3623. lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  3624. mov [eax - 4], ecx
  3625. {Store the trailing size marker}
  3626. mov [eax + edx - 8], edx
  3627. {Bin this medium block}
  3628. cmp edx, MinimumMediumBlockSize
  3629. jnb InsertMediumBlockIntoBin
  3630. ret
  3631. {Align branch target}
  3632. nop
  3633. nop
  3634. @LastBlockFedIsFree:
  3635. {Drop the flags}
  3636. mov edx, DropMediumAndLargeFlagsMask
  3637. and edx, [eax - 4]
  3638. {Free the last block fed}
  3639. cmp edx, MinimumMediumBlockSize
  3640. jb @DontRemoveLastFed
  3641. {Last fed block is free - remove it from its size bin}
  3642. call RemoveMediumFreeBlock
  3643. {Re-read eax and edx}
  3644. mov eax, LastSequentiallyFedMediumBlock
  3645. mov edx, DropMediumAndLargeFlagsMask
  3646. and edx, [eax - 4]
  3647. @DontRemoveLastFed:
  3648. {Get the number of bytes left in ecx}
  3649. mov ecx, MediumSequentialFeedBytesLeft
  3650. {Point eax to the start of the remainder}
  3651. sub eax, ecx
  3652. {edx = total size of the remainder}
  3653. add edx, ecx
  3654. jmp @BinTheRemainder
  3655. @Done:
  3656. end;
  3657. {$else}
  3658. asm
  3659. .params 2
  3660. xor eax, eax
  3661. cmp MediumSequentialFeedBytesLeft, eax
  3662. je @Done
  3663. {Get a pointer to the last sequentially allocated medium block}
  3664. mov rax, LastSequentiallyFedMediumBlock
  3665. {Is the block that was last fed sequentially free?}
  3666. test byte ptr [rax - BlockHeaderSize], IsFreeBlockFlag
  3667. jnz @LastBlockFedIsFree
  3668. {Set the "previous block is free" flag in the last block fed}
  3669. or qword ptr [rax - BlockHeaderSize], PreviousMediumBlockIsFreeFlag
  3670. {Get the remainder in edx}
  3671. mov edx, MediumSequentialFeedBytesLeft
  3672. {Point eax to the start of the remainder}
  3673. sub rax, rdx
  3674. @BinTheRemainder:
  3675. {Status: rax = start of remainder, edx = size of remainder}
  3676. {Store the size of the block as well as the flags}
  3677. lea rcx, [rdx + IsMediumBlockFlag + IsFreeBlockFlag]
  3678. mov [rax - BlockHeaderSize], rcx
  3679. {Store the trailing size marker}
  3680. mov [rax + rdx - 2 * BlockHeaderSize], rdx
  3681. {Bin this medium block}
  3682. cmp edx, MinimumMediumBlockSize
  3683. jb @Done
  3684. mov rcx, rax
  3685. call InsertMediumBlockIntoBin
  3686. jmp @Done
  3687. @LastBlockFedIsFree:
  3688. {Drop the flags}
  3689. mov rdx, DropMediumAndLargeFlagsMask
  3690. and rdx, [rax - BlockHeaderSize]
  3691. {Free the last block fed}
  3692. cmp edx, MinimumMediumBlockSize
  3693. jb @DontRemoveLastFed
  3694. {Last fed block is free - remove it from its size bin}
  3695. mov rcx, rax
  3696. call RemoveMediumFreeBlock
  3697. {Re-read rax and rdx}
  3698. mov rax, LastSequentiallyFedMediumBlock
  3699. mov rdx, DropMediumAndLargeFlagsMask
  3700. and rdx, [rax - BlockHeaderSize]
  3701. @DontRemoveLastFed:
  3702. {Get the number of bytes left in ecx}
  3703. mov ecx, MediumSequentialFeedBytesLeft
  3704. {Point rax to the start of the remainder}
  3705. sub rax, rcx
  3706. {edx = total size of the remainder}
  3707. add edx, ecx
  3708. jmp @BinTheRemainder
  3709. @Done:
  3710. end;
  3711. {$endif}
  3712. {$endif}
  3713. {Allocates a new sequential feed medium block pool and immediately splits off a
  3714. block of the requested size. The block size must be a multiple of 16 and
  3715. medium blocks must be locked.}
  3716. function AllocNewSequentialFeedMediumPool(AFirstBlockSize: Cardinal): Pointer;
  3717. var
  3718. LOldFirstMediumBlockPool: PMediumBlockPoolHeader;
  3719. LNewPool: Pointer;
  3720. begin
  3721. {Bin the current sequential feed remainder}
  3722. BinMediumSequentialFeedRemainder;
  3723. {Allocate a new sequential feed block pool}
  3724. LNewPool := VirtualAlloc(nil, MediumBlockPoolSize,
  3725. MEM_COMMIT{$ifdef AlwaysAllocateTopDown} or MEM_TOP_DOWN{$endif}, PAGE_READWRITE);
  3726. if LNewPool <> nil then
  3727. begin
  3728. {Insert this block pool into the list of block pools}
  3729. LOldFirstMediumBlockPool := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  3730. PMediumBlockPoolHeader(LNewPool).PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  3731. MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := LNewPool;
  3732. PMediumBlockPoolHeader(LNewPool).NextMediumBlockPoolHeader := LOldFirstMediumBlockPool;
  3733. LOldFirstMediumBlockPool.PreviousMediumBlockPoolHeader := LNewPool;
  3734. {Store the sequential feed pool trailer}
  3735. PNativeUInt(PByte(LNewPool) + MediumBlockPoolSize - BlockHeaderSize)^ := IsMediumBlockFlag;
  3736. {Get the number of bytes still available}
  3737. MediumSequentialFeedBytesLeft := (MediumBlockPoolSize - MediumBlockPoolHeaderSize) - AFirstBlockSize;
  3738. {Get the result}
  3739. Result := Pointer(PByte(LNewPool) + MediumBlockPoolSize - AFirstBlockSize);
  3740. LastSequentiallyFedMediumBlock := Result;
  3741. {Store the block header}
  3742. PNativeUInt(PByte(Result) - BlockHeaderSize)^ := AFirstBlockSize or IsMediumBlockFlag;
  3743. end
  3744. else
  3745. begin
  3746. {Out of memory}
  3747. MediumSequentialFeedBytesLeft := 0;
  3748. Result := nil;
  3749. end;
  3750. end;
  3751. {-----------------Large Block Management------------------}
  3752. {Locks the large blocks}
  3753. procedure LockLargeBlocks;
  3754. begin
  3755. {Lock the large blocks}
  3756. {$ifndef AssumeMultiThreaded}
  3757. if IsMultiThread then
  3758. {$endif}
  3759. begin
  3760. while LockCmpxchg(0, 1, @LargeBlocksLocked) <> 0 do
  3761. begin
  3762. {$ifdef NeverSleepOnThreadContention}
  3763. {$ifdef UseSwitchToThread}
  3764. SwitchToThread;
  3765. {$endif}
  3766. {$else}
  3767. Sleep(InitialSleepTime);
  3768. if LockCmpxchg(0, 1, @LargeBlocksLocked) = 0 then
  3769. Break;
  3770. Sleep(AdditionalSleepTime);
  3771. {$endif}
  3772. end;
  3773. end;
  3774. end;
  3775. {Allocates a Large block of at least ASize (actual size may be larger to
  3776. allow for alignment etc.). ASize must be the actual user requested size. This
  3777. procedure will pad it to the appropriate page boundary and also add the space
  3778. required by the header.}
  3779. function AllocateLargeBlock(ASize: NativeUInt): Pointer;
  3780. var
  3781. LLargeUsedBlockSize: NativeUInt;
  3782. LOldFirstLargeBlock: PLargeBlockHeader;
  3783. begin
  3784. {Pad the block size to include the header and granularity. We also add a
  3785. SizeOf(Pointer) overhead so a huge block size is a multiple of 16 bytes less
  3786. SizeOf(Pointer) (so we can use a single move function for reallocating all
  3787. block types)}
  3788. LLargeUsedBlockSize := (ASize + LargeBlockHeaderSize + LargeBlockGranularity - 1 + BlockHeaderSize)
  3789. and -LargeBlockGranularity;
  3790. {Get the Large block}
  3791. Result := VirtualAlloc(nil, LLargeUsedBlockSize, MEM_COMMIT or MEM_TOP_DOWN,
  3792. PAGE_READWRITE);
  3793. {Set the Large block fields}
  3794. if Result <> nil then
  3795. begin
  3796. {Set the large block size and flags}
  3797. PLargeBlockHeader(Result).UserAllocatedSize := ASize;
  3798. PLargeBlockHeader(Result).BlockSizeAndFlags := LLargeUsedBlockSize or IsLargeBlockFlag;
  3799. {Insert the large block into the linked list of large blocks}
  3800. LockLargeBlocks;
  3801. LOldFirstLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  3802. PLargeBlockHeader(Result).PreviousLargeBlockHeader := @LargeBlocksCircularList;
  3803. LargeBlocksCircularList.NextLargeBlockHeader := Result;
  3804. PLargeBlockHeader(Result).NextLargeBlockHeader := LOldFirstLargeBlock;
  3805. LOldFirstLargeBlock.PreviousLargeBlockHeader := Result;
  3806. LargeBlocksLocked := False;
  3807. {Add the size of the header}
  3808. Inc(PByte(Result), LargeBlockHeaderSize);
  3809. {$ifdef FullDebugMode}
  3810. {Since large blocks are never reused, the user area is not initialized to
  3811. the debug fill pattern, but the debug header and footer must be set.}
  3812. PFullDebugBlockHeader(Result).HeaderCheckSum := NativeUInt(Result);
  3813. PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(Result);
  3814. {$endif}
  3815. end;
  3816. end;
  3817. {Frees a large block, returning 0 on success, -1 otherwise}
  3818. function FreeLargeBlock(APointer: Pointer): Integer;
  3819. var
  3820. LPreviousLargeBlockHeader, LNextLargeBlockHeader: PLargeBlockHeader;
  3821. {$ifndef POSIX}
  3822. LRemainingSize: NativeUInt;
  3823. LCurrentSegment: Pointer;
  3824. LMemInfo: TMemoryBasicInformation;
  3825. {$endif}
  3826. begin
  3827. {$ifdef ClearLargeBlocksBeforeReturningToOS}
  3828. FillChar(APointer^,
  3829. (PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags
  3830. and DropMediumAndLargeFlagsMask) - LargeBlockHeaderSize, 0);
  3831. {$endif}
  3832. {Point to the start of the large block}
  3833. APointer := Pointer(PByte(APointer) - LargeBlockHeaderSize);
  3834. {Get the previous and next large blocks}
  3835. LockLargeBlocks;
  3836. LPreviousLargeBlockHeader := PLargeBlockHeader(APointer).PreviousLargeBlockHeader;
  3837. LNextLargeBlockHeader := PLargeBlockHeader(APointer).NextLargeBlockHeader;
  3838. {$ifndef POSIX}
  3839. {Is the large block segmented?}
  3840. if PLargeBlockHeader(APointer).BlockSizeAndFlags and LargeBlockIsSegmented = 0 then
  3841. begin
  3842. {$endif}
  3843. {Single segment large block: Try to free it}
  3844. if VirtualFree(APointer, 0, MEM_RELEASE) then
  3845. Result := 0
  3846. else
  3847. Result := -1;
  3848. {$ifndef POSIX}
  3849. end
  3850. else
  3851. begin
  3852. {The large block is segmented - free all segments}
  3853. LCurrentSegment := APointer;
  3854. LRemainingSize := PLargeBlockHeader(APointer).BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  3855. Result := 0;
  3856. while True do
  3857. begin
  3858. {Get the size of the current segment}
  3859. VirtualQuery(LCurrentSegment, LMemInfo, SizeOf(LMemInfo));
  3860. {Free the segment}
  3861. if not VirtualFree(LCurrentSegment, 0, MEM_RELEASE) then
  3862. begin
  3863. Result := -1;
  3864. Break;
  3865. end;
  3866. {Done?}
  3867. if NativeUInt(LMemInfo.RegionSize) >= LRemainingSize then
  3868. Break;
  3869. {Decrement the remaining size}
  3870. Dec(LRemainingSize, NativeUInt(LMemInfo.RegionSize));
  3871. Inc(PByte(LCurrentSegment), NativeUInt(LMemInfo.RegionSize));
  3872. end;
  3873. end;
  3874. {$endif}
  3875. {Success?}
  3876. if Result = 0 then
  3877. begin
  3878. {Remove the large block from the linked list}
  3879. LNextLargeBlockHeader.PreviousLargeBlockHeader := LPreviousLargeBlockHeader;
  3880. LPreviousLargeBlockHeader.NextLargeBlockHeader := LNextLargeBlockHeader;
  3881. end;
  3882. {Unlock the large blocks}
  3883. LargeBlocksLocked := False;
  3884. end;
  3885. {$ifndef FullDebugMode}
  3886. {Reallocates a large block to at least the requested size. Returns the new
  3887. pointer, or nil on error}
  3888. function ReallocateLargeBlock(APointer: Pointer; ANewSize: NativeUInt): Pointer;
  3889. var
  3890. LOldAvailableSize, LBlockHeader, LOldUserSize, LMinimumUpsize,
  3891. LNewAllocSize: NativeUInt;
  3892. {$ifndef POSIX}
  3893. LNewSegmentSize: NativeUInt;
  3894. LNextSegmentPointer: Pointer;
  3895. LMemInfo: TMemoryBasicInformation;
  3896. {$endif}
  3897. begin
  3898. {Get the block header}
  3899. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  3900. {Large block - size is (16 + 4) less than the allocated size}
  3901. LOldAvailableSize := (LBlockHeader and DropMediumAndLargeFlagsMask) - (LargeBlockHeaderSize + BlockHeaderSize);
  3902. {Is it an upsize or a downsize?}
  3903. if ANewSize > LOldAvailableSize then
  3904. begin
  3905. {This pointer is being reallocated to a larger block and therefore it is
  3906. logical to assume that it may be enlarged again. Since reallocations are
  3907. expensive, there is a minimum upsize percentage to avoid unnecessary
  3908. future move operations.}
  3909. {Add 25% for large block upsizes}
  3910. LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
  3911. if ANewSize < LMinimumUpsize then
  3912. LNewAllocSize := LMinimumUpsize
  3913. else
  3914. LNewAllocSize := ANewSize;
  3915. {$ifndef POSIX}
  3916. {Can another large block segment be allocated directly after this segment,
  3917. thus negating the need to move the data?}
  3918. LNextSegmentPointer := Pointer(PByte(APointer) - LargeBlockHeaderSize + (LBlockHeader and DropMediumAndLargeFlagsMask));
  3919. VirtualQuery(LNextSegmentPointer, LMemInfo, SizeOf(LMemInfo));
  3920. if LMemInfo.State = MEM_FREE then
  3921. begin
  3922. {Round the region size to the previous 64K}
  3923. LMemInfo.RegionSize := LMemInfo.RegionSize and -LargeBlockGranularity;
  3924. {Enough space to grow in place?}
  3925. if NativeUInt(LMemInfo.RegionSize) > (ANewSize - LOldAvailableSize) then
  3926. begin
  3927. {There is enough space after the block to extend it - determine by how
  3928. much}
  3929. LNewSegmentSize := (LNewAllocSize - LOldAvailableSize + LargeBlockGranularity - 1) and -LargeBlockGranularity;
  3930. if LNewSegmentSize > LMemInfo.RegionSize then
  3931. LNewSegmentSize := LMemInfo.RegionSize;
  3932. {Attempy to reserve the address range (which will fail if another
  3933. thread has just reserved it) and commit it immediately afterwards.}
  3934. if (VirtualAlloc(LNextSegmentPointer, LNewSegmentSize, MEM_RESERVE, PAGE_READWRITE) <> nil)
  3935. and (VirtualAlloc(LNextSegmentPointer, LNewSegmentSize, MEM_COMMIT, PAGE_READWRITE) <> nil) then
  3936. begin
  3937. {Update the requested size}
  3938. PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  3939. PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags :=
  3940. (PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).BlockSizeAndFlags + LNewSegmentSize)
  3941. or LargeBlockIsSegmented;
  3942. {Success}
  3943. Result := APointer;
  3944. Exit;
  3945. end;
  3946. end;
  3947. end;
  3948. {$endif}
  3949. {Could not resize in place: Allocate the new block}
  3950. Result := FastGetMem(LNewAllocSize);
  3951. if Result <> nil then
  3952. begin
  3953. {If it's a large block - store the actual user requested size (it may
  3954. not be if the block that is being reallocated from was previously
  3955. downsized)}
  3956. if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
  3957. PLargeBlockHeader(PByte(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  3958. {The user allocated size is stored for large blocks}
  3959. LOldUserSize := PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize;
  3960. {The number of bytes to move is the old user size.}
  3961. {$ifdef UseCustomVariableSizeMoveRoutines}
  3962. MoveX16LP(APointer^, Result^, LOldUserSize);
  3963. {$else}
  3964. System.Move(APointer^, Result^, LOldUserSize);
  3965. {$endif}
  3966. {Free the old block}
  3967. FastFreeMem(APointer);
  3968. end;
  3969. end
  3970. else
  3971. begin
  3972. {It's a downsize: do we need to reallocate? Only if the new size is less
  3973. than half the old size}
  3974. if ANewSize >= (LOldAvailableSize shr 1) then
  3975. begin
  3976. {No need to reallocate}
  3977. Result := APointer;
  3978. {Update the requested size}
  3979. PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  3980. end
  3981. else
  3982. begin
  3983. {The block is less than half the old size, and the current size is
  3984. greater than the minimum block size allowing a downsize: reallocate}
  3985. Result := FastGetMem(ANewSize);
  3986. if Result <> nil then
  3987. begin
  3988. {Still a large block? -> Set the user size}
  3989. if ANewSize > (MaximumMediumBlockSize - BlockHeaderSize) then
  3990. PLargeBlockHeader(PByte(APointer) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  3991. {Move the data across}
  3992. {$ifdef UseCustomVariableSizeMoveRoutines}
  3993. {$ifdef Align16Bytes}
  3994. MoveX16LP(APointer^, Result^, ANewSize);
  3995. {$else}
  3996. MoveX8LP(APointer^, Result^, ANewSize);
  3997. {$endif}
  3998. {$else}
  3999. System.Move(APointer^, Result^, ANewSize);
  4000. {$endif}
  4001. {Free the old block}
  4002. FastFreeMem(APointer);
  4003. end;
  4004. end;
  4005. end;
  4006. end;
  4007. {$endif}
  4008. {---------------------Replacement Memory Manager Interface---------------------}
  4009. {Replacement for SysGetMem}
  4010. function FastGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  4011. {$ifndef ASMVersion}
  4012. var
  4013. LMediumBlock{$ifndef FullDebugMode}, LNextFreeBlock, LSecondSplit{$endif}: PMediumFreeBlock;
  4014. LNextMediumBlockHeader: PNativeUInt;
  4015. LBlockSize, LAvailableBlockSize{$ifndef FullDebugMode}, LSecondSplitSize{$endif},
  4016. LSequentialFeedFreeSize: NativeUInt;
  4017. LPSmallBlockType: PSmallBlockType;
  4018. LPSmallBlockPool, LPNewFirstPool: PSmallBlockPoolHeader;
  4019. LNewFirstFreeBlock: Pointer;
  4020. LPMediumBin: PMediumFreeBlock;
  4021. LBinNumber, {$ifndef FullDebugMode}LBinGroupsMasked, {$endif}LBinGroupMasked,
  4022. LBinGroupNumber: Cardinal;
  4023. begin
  4024. {Is it a small block? -> Take the header size into account when
  4025. determining the required block size}
  4026. if NativeUInt(ASize) <= (MaximumSmallBlockSize - BlockHeaderSize) then
  4027. begin
  4028. {-------------------------Allocate a small block---------------------------}
  4029. {Get the block type from the size}
  4030. LPSmallBlockType := PSmallBlockType(AllocSize2SmallBlockTypeIndX4[
  4031. (NativeUInt(ASize) + (BlockHeaderSize - 1)) div SmallBlockGranularity]
  4032. * (SizeOf(TSmallBlockType) div 4)
  4033. + UIntPtr(@SmallBlockTypes));
  4034. {Lock the block type}
  4035. {$ifndef AssumeMultiThreaded}
  4036. if IsMultiThread then
  4037. {$endif}
  4038. begin
  4039. while True do
  4040. begin
  4041. {Try to lock the small block type}
  4042. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  4043. Break;
  4044. {Try the next block type}
  4045. Inc(PByte(LPSmallBlockType), SizeOf(TSmallBlockType));
  4046. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  4047. Break;
  4048. {Try up to two sizes past the requested size}
  4049. Inc(PByte(LPSmallBlockType), SizeOf(TSmallBlockType));
  4050. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  4051. Break;
  4052. {All three sizes locked - given up and sleep}
  4053. Dec(PByte(LPSmallBlockType), 2 * SizeOf(TSmallBlockType));
  4054. {$ifdef NeverSleepOnThreadContention}
  4055. {$ifdef UseSwitchToThread}
  4056. SwitchToThread;
  4057. {$endif}
  4058. {$else}
  4059. {Both this block type and the next is in use: sleep}
  4060. Sleep(InitialSleepTime);
  4061. {Try the lock again}
  4062. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  4063. Break;
  4064. {Sleep longer}
  4065. Sleep(AdditionalSleepTime);
  4066. {$endif}
  4067. end;
  4068. end;
  4069. {Get the first pool with free blocks}
  4070. LPSmallBlockPool := LPSmallBlockType.NextPartiallyFreePool;
  4071. {Is the pool valid?}
  4072. if UIntPtr(LPSmallBlockPool) <> UIntPtr(LPSmallBlockType) then
  4073. begin
  4074. {Get the first free offset}
  4075. Result := LPSmallBlockPool.FirstFreeBlock;
  4076. {Get the new first free block}
  4077. LNewFirstFreeBlock := PPointer(PByte(Result) - BlockHeaderSize)^;
  4078. {$ifdef CheckHeapForCorruption}
  4079. {The block should be free}
  4080. if (NativeUInt(LNewFirstFreeBlock) and ExtractSmallFlagsMask) <> IsFreeBlockFlag then
  4081. {$ifdef BCB6OrDelphi7AndUp}
  4082. System.Error(reInvalidPtr);
  4083. {$else}
  4084. System.RunError(reInvalidPtr);
  4085. {$endif}
  4086. {$endif}
  4087. LNewFirstFreeBlock := Pointer(UIntPtr(LNewFirstFreeBlock) and DropSmallFlagsMask);
  4088. {Increment the number of used blocks}
  4089. Inc(LPSmallBlockPool.BlocksInUse);
  4090. {Set the new first free block}
  4091. LPSmallBlockPool.FirstFreeBlock := LNewFirstFreeBlock;
  4092. {Is the pool now full?}
  4093. if LNewFirstFreeBlock = nil then
  4094. begin
  4095. {Pool is full - remove it from the partially free list}
  4096. LPNewFirstPool := LPSmallBlockPool.NextPartiallyFreePool;
  4097. LPSmallBlockType.NextPartiallyFreePool := LPNewFirstPool;
  4098. LPNewFirstPool.PreviousPartiallyFreePool := PSmallBlockPoolHeader(LPSmallBlockType);
  4099. end;
  4100. end
  4101. else
  4102. begin
  4103. {Try to feed a small block sequentially}
  4104. Result := LPSmallBlockType.NextSequentialFeedBlockAddress;
  4105. {Can another block fit?}
  4106. if UIntPtr(Result) <= UIntPtr(LPSmallBlockType.MaxSequentialFeedBlockAddress) then
  4107. begin
  4108. {Get the sequential feed block pool}
  4109. LPSmallBlockPool := LPSmallBlockType.CurrentSequentialFeedPool;
  4110. {Increment the number of used blocks in the sequential feed pool}
  4111. Inc(LPSmallBlockPool.BlocksInUse);
  4112. {Store the next sequential feed block address}
  4113. LPSmallBlockType.NextSequentialFeedBlockAddress := Pointer(PByte(Result) + LPSmallBlockType.BlockSize);
  4114. end
  4115. else
  4116. begin
  4117. {Need to allocate a pool: Lock the medium blocks}
  4118. LockMediumBlocks;
  4119. {$ifndef FullDebugMode}
  4120. {Are there any available blocks of a suitable size?}
  4121. LBinGroupsMasked := MediumBlockBinGroupBitmap and ($ffffff00 or LPSmallBlockType.AllowedGroupsForBlockPoolBitmap);
  4122. if LBinGroupsMasked <> 0 then
  4123. begin
  4124. {Get the bin group with free blocks}
  4125. LBinGroupNumber := FindFirstSetBit(LBinGroupsMasked);
  4126. {Get the bin in the group with free blocks}
  4127. LBinNumber := FindFirstSetBit(MediumBlockBinBitmaps[LBinGroupNumber])
  4128. + LBinGroupNumber * 32;
  4129. LPMediumBin := @MediumBlockBins[LBinNumber];
  4130. {Get the first block in the bin}
  4131. LMediumBlock := LPMediumBin.NextFreeBlock;
  4132. {Remove the first block from the linked list (LIFO)}
  4133. LNextFreeBlock := LMediumBlock.NextFreeBlock;
  4134. LPMediumBin.NextFreeBlock := LNextFreeBlock;
  4135. LNextFreeBlock.PreviousFreeBlock := LPMediumBin;
  4136. {Is this bin now empty?}
  4137. if LNextFreeBlock = LPMediumBin then
  4138. begin
  4139. {Flag this bin as empty}
  4140. MediumBlockBinBitmaps[LBinGroupNumber] := MediumBlockBinBitmaps[LBinGroupNumber]
  4141. and (not (1 shl (LBinNumber and 31)));
  4142. {Is the group now entirely empty?}
  4143. if MediumBlockBinBitmaps[LBinGroupNumber] = 0 then
  4144. begin
  4145. {Flag this group as empty}
  4146. MediumBlockBinGroupBitmap := MediumBlockBinGroupBitmap
  4147. and (not (1 shl LBinGroupNumber));
  4148. end;
  4149. end;
  4150. {Get the size of the available medium block}
  4151. LBlockSize := PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  4152. {$ifdef CheckHeapForCorruption}
  4153. {Check that this block is actually free and the next and previous blocks
  4154. are both in use.}
  4155. if ((PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag))
  4156. or ((PNativeUInt(PByte(LMediumBlock) + (PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and IsFreeBlockFlag) <> 0)
  4157. then
  4158. begin
  4159. {$ifdef BCB6OrDelphi7AndUp}
  4160. System.Error(reInvalidPtr);
  4161. {$else}
  4162. System.RunError(reInvalidPtr);
  4163. {$endif}
  4164. end;
  4165. {$endif}
  4166. {Should the block be split?}
  4167. if LBlockSize >= MaximumSmallBlockPoolSize then
  4168. begin
  4169. {Get the size of the second split}
  4170. LSecondSplitSize := LBlockSize - LPSmallBlockType.OptimalBlockPoolSize;
  4171. {Adjust the block size}
  4172. LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
  4173. {Split the block in two}
  4174. LSecondSplit := PMediumFreeBlock(PByte(LMediumBlock) + LBlockSize);
  4175. PNativeUInt(PByte(LSecondSplit) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  4176. {Store the size of the second split as the second last dword/qword}
  4177. PNativeUInt(PByte(LSecondSplit) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
  4178. {Put the remainder in a bin (it will be big enough)}
  4179. InsertMediumBlockIntoBin(LSecondSplit, LSecondSplitSize);
  4180. end
  4181. else
  4182. begin
  4183. {Mark this block as used in the block following it}
  4184. LNextMediumBlockHeader := PNativeUInt(PByte(LMediumBlock) + LBlockSize - BlockHeaderSize);
  4185. LNextMediumBlockHeader^ := LNextMediumBlockHeader^ and (not PreviousMediumBlockIsFreeFlag);
  4186. end;
  4187. end
  4188. else
  4189. begin
  4190. {$endif}
  4191. {Check the sequential feed medium block pool for space}
  4192. LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
  4193. if LSequentialFeedFreeSize >= LPSmallBlockType.MinimumBlockPoolSize then
  4194. begin
  4195. {Enough sequential feed space: Will the remainder be usable?}
  4196. if LSequentialFeedFreeSize >= (LPSmallBlockType.OptimalBlockPoolSize + MinimumMediumBlockSize) then
  4197. begin
  4198. LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
  4199. end
  4200. else
  4201. LBlockSize := LSequentialFeedFreeSize;
  4202. {Get the block}
  4203. LMediumBlock := Pointer(PByte(LastSequentiallyFedMediumBlock) - LBlockSize);
  4204. {Update the sequential feed parameters}
  4205. LastSequentiallyFedMediumBlock := LMediumBlock;
  4206. MediumSequentialFeedBytesLeft := LSequentialFeedFreeSize - LBlockSize;
  4207. end
  4208. else
  4209. begin
  4210. {Need to allocate a new sequential feed medium block pool: use the
  4211. optimal size for this small block pool}
  4212. LBlockSize := LPSmallBlockType.OptimalBlockPoolSize;
  4213. {Allocate the medium block pool}
  4214. LMediumBlock := AllocNewSequentialFeedMediumPool(LBlockSize);
  4215. if LMediumBlock = nil then
  4216. begin
  4217. {Out of memory}
  4218. {Unlock the medium blocks}
  4219. MediumBlocksLocked := False;
  4220. {Unlock the block type}
  4221. LPSmallBlockType.BlockTypeLocked := False;
  4222. {Failed}
  4223. Result := nil;
  4224. {done}
  4225. Exit;
  4226. end;
  4227. end;
  4228. {$ifndef FullDebugMode}
  4229. end;
  4230. {$endif}
  4231. {Mark this block as in use}
  4232. {Set the size and flags for this block}
  4233. PNativeUInt(PByte(LMediumBlock) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag or IsSmallBlockPoolInUseFlag;
  4234. {Unlock medium blocks}
  4235. MediumBlocksLocked := False;
  4236. {Set up the block pool}
  4237. LPSmallBlockPool := PSmallBlockPoolHeader(LMediumBlock);
  4238. LPSmallBlockPool.BlockType := LPSmallBlockType;
  4239. LPSmallBlockPool.FirstFreeBlock := nil;
  4240. LPSmallBlockPool.BlocksInUse := 1;
  4241. {Set it up for sequential block serving}
  4242. LPSmallBlockType.CurrentSequentialFeedPool := LPSmallBlockPool;
  4243. Result := Pointer(PByte(LPSmallBlockPool) + SmallBlockPoolHeaderSize);
  4244. LPSmallBlockType.NextSequentialFeedBlockAddress := Pointer(PByte(Result) + LPSmallBlockType.BlockSize);
  4245. LPSmallBlockType.MaxSequentialFeedBlockAddress := Pointer(PByte(LPSmallBlockPool) + LBlockSize - LPSmallBlockType.BlockSize);
  4246. end;
  4247. {$ifdef FullDebugMode}
  4248. {Clear the user area of the block}
  4249. DebugFillMem(Pointer(PByte(Result) + (SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt)))^,
  4250. LPSmallBlockType.BlockSize - FullDebugBlockOverhead - SizeOf(NativeUInt),
  4251. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  4252. {Block was fed sequentially - we need to set a valid debug header. Use
  4253. the block address.}
  4254. PFullDebugBlockHeader(Result).HeaderCheckSum := NativeUInt(Result);
  4255. PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(Result);
  4256. {$endif}
  4257. end;
  4258. {Unlock the block type}
  4259. LPSmallBlockType.BlockTypeLocked := False;
  4260. {Set the block header}
  4261. PNativeUInt(PByte(Result) - BlockHeaderSize)^ := UIntPtr(LPSmallBlockPool);
  4262. end
  4263. else
  4264. begin
  4265. {Medium block or Large block?}
  4266. if NativeUInt(ASize) <= (MaximumMediumBlockSize - BlockHeaderSize) then
  4267. begin
  4268. {------------------------Allocate a medium block--------------------------}
  4269. {Get the block size and bin number for this block size. Block sizes are
  4270. rounded up to the next bin size.}
  4271. LBlockSize := ((NativeUInt(ASize) + (MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset))
  4272. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  4273. {Get the bin number}
  4274. LBinNumber := (LBlockSize - MinimumMediumBlockSize) div MediumBlockGranularity;
  4275. {Lock the medium blocks}
  4276. LockMediumBlocks;
  4277. {Calculate the bin group}
  4278. LBinGroupNumber := LBinNumber div 32;
  4279. {Is there a suitable block inside this group?}
  4280. LBinGroupMasked := MediumBlockBinBitmaps[LBinGroupNumber] and -(1 shl (LBinNumber and 31));
  4281. if LBinGroupMasked <> 0 then
  4282. begin
  4283. {Get the actual bin number}
  4284. LBinNumber := FindFirstSetBit(LBinGroupMasked) + LBinGroupNumber * 32;
  4285. end
  4286. else
  4287. begin
  4288. {$ifndef FullDebugMode}
  4289. {Try all groups greater than this group}
  4290. LBinGroupsMasked := MediumBlockBinGroupBitmap and -(2 shl LBinGroupNumber);
  4291. if LBinGroupsMasked <> 0 then
  4292. begin
  4293. {There is a suitable group with space: get the bin number}
  4294. LBinGroupNumber := FindFirstSetBit(LBinGroupsMasked);
  4295. {Get the bin in the group with free blocks}
  4296. LBinNumber := FindFirstSetBit(MediumBlockBinBitmaps[LBinGroupNumber])
  4297. + LBinGroupNumber * 32;
  4298. end
  4299. else
  4300. begin
  4301. {$endif}
  4302. {There are no bins with a suitable block: Sequentially feed the required block}
  4303. LSequentialFeedFreeSize := MediumSequentialFeedBytesLeft;
  4304. if LSequentialFeedFreeSize >= LBlockSize then
  4305. begin
  4306. {$ifdef FullDebugMode}
  4307. {In full debug mode a medium block must have enough bytes to fit
  4308. all the debug info, so we must make sure there are no tiny medium
  4309. blocks at the start of the pool.}
  4310. if LSequentialFeedFreeSize - LBlockSize < (FullDebugBlockOverhead + BlockHeaderSize) then
  4311. LBlockSize := LSequentialFeedFreeSize;
  4312. {$endif}
  4313. {Block can be fed sequentially}
  4314. Result := Pointer(PByte(LastSequentiallyFedMediumBlock) - LBlockSize);
  4315. {Store the last sequentially fed block}
  4316. LastSequentiallyFedMediumBlock := Result;
  4317. {Store the remaining bytes}
  4318. MediumSequentialFeedBytesLeft := LSequentialFeedFreeSize - LBlockSize;
  4319. {Set the flags for the block}
  4320. PNativeUInt(PByte(Result) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag;
  4321. end
  4322. else
  4323. begin
  4324. {Need to allocate a new sequential feed block}
  4325. Result := AllocNewSequentialFeedMediumPool(LBlockSize);
  4326. end;
  4327. {$ifdef FullDebugMode}
  4328. {Block was fed sequentially - we need to set a valid debug header}
  4329. if Result <> nil then
  4330. begin
  4331. PFullDebugBlockHeader(Result).HeaderCheckSum := NativeUInt(Result);
  4332. PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader))^ := not NativeUInt(Result);
  4333. {Clear the user area of the block}
  4334. DebugFillMem(Pointer(PByte(Result) + SizeOf(TFullDebugBlockHeader) + SizeOf(NativeUInt))^,
  4335. LBlockSize - FullDebugBlockOverhead - SizeOf(NativeUInt),
  4336. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  4337. end;
  4338. {$endif}
  4339. {Done}
  4340. MediumBlocksLocked := False;
  4341. Exit;
  4342. {$ifndef FullDebugMode}
  4343. end;
  4344. {$endif}
  4345. end;
  4346. {If we get here we have a valid LBinGroupNumber and LBinNumber:
  4347. Use the first block in the bin, splitting it if necessary}
  4348. {Get a pointer to the bin}
  4349. LPMediumBin := @MediumBlockBins[LBinNumber];
  4350. {Get the result}
  4351. Result := LPMediumBin.NextFreeBlock;
  4352. {$ifdef CheckHeapForCorruption}
  4353. {Check that this block is actually free and the next and previous blocks
  4354. are both in use (except in full debug mode).}
  4355. if ((PNativeUInt(PByte(Result) - BlockHeaderSize)^ and {$ifndef FullDebugMode}ExtractMediumAndLargeFlagsMask{$else}(IsMediumBlockFlag or IsFreeBlockFlag){$endif}) <> (IsFreeBlockFlag or IsMediumBlockFlag))
  4356. {$ifndef FullDebugMode}
  4357. or ((PNativeUInt(PByte(Result) + (PNativeUInt(PByte(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and (ExtractMediumAndLargeFlagsMask - IsSmallBlockPoolInUseFlag)) <> (IsMediumBlockFlag or PreviousMediumBlockIsFreeFlag))
  4358. {$endif}
  4359. then
  4360. begin
  4361. {$ifdef BCB6OrDelphi7AndUp}
  4362. System.Error(reInvalidPtr);
  4363. {$else}
  4364. System.RunError(reInvalidPtr);
  4365. {$endif}
  4366. end;
  4367. {$endif}
  4368. {Remove the block from the bin containing it}
  4369. RemoveMediumFreeBlock(Result);
  4370. {Get the block size}
  4371. LAvailableBlockSize := PNativeUInt(PByte(Result) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask;
  4372. {$ifndef FullDebugMode}
  4373. {Is it an exact fit or not?}
  4374. LSecondSplitSize := LAvailableBlockSize - LBlockSize;
  4375. if LSecondSplitSize <> 0 then
  4376. begin
  4377. {Split the block in two}
  4378. LSecondSplit := PMediumFreeBlock(PByte(Result) + LBlockSize);
  4379. {Set the size of the second split}
  4380. PNativeUInt(PByte(LSecondSplit) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  4381. {Store the size of the second split}
  4382. PNativeUInt(PByte(LSecondSplit) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
  4383. {Put the remainder in a bin if it is big enough}
  4384. if LSecondSplitSize >= MinimumMediumBlockSize then
  4385. InsertMediumBlockIntoBin(LSecondSplit, LSecondSplitSize);
  4386. end
  4387. else
  4388. begin
  4389. {$else}
  4390. {In full debug mode blocks are never split or coalesced}
  4391. LBlockSize := LAvailableBlockSize;
  4392. {$endif}
  4393. {Mark this block as used in the block following it}
  4394. LNextMediumBlockHeader := Pointer(PByte(Result) + LBlockSize - BlockHeaderSize);
  4395. {$ifndef FullDebugMode}
  4396. {$ifdef CheckHeapForCorruption}
  4397. {The next block must be in use}
  4398. if (LNextMediumBlockHeader^ and (ExtractMediumAndLargeFlagsMask - IsSmallBlockPoolInUseFlag)) <> (IsMediumBlockFlag or PreviousMediumBlockIsFreeFlag) then
  4399. {$ifdef BCB6OrDelphi7AndUp}
  4400. System.Error(reInvalidPtr);
  4401. {$else}
  4402. System.RunError(reInvalidPtr);
  4403. {$endif}
  4404. {$endif}
  4405. {$endif}
  4406. LNextMediumBlockHeader^ :=
  4407. LNextMediumBlockHeader^ and (not PreviousMediumBlockIsFreeFlag);
  4408. {$ifndef FullDebugMode}
  4409. end;
  4410. {Set the size and flags for this block}
  4411. PNativeUInt(PByte(Result) - BlockHeaderSize)^ := LBlockSize or IsMediumBlockFlag;
  4412. {$else}
  4413. {In full debug mode blocks are never split or coalesced}
  4414. Dec(PNativeUInt(PByte(Result) - BlockHeaderSize)^, IsFreeBlockFlag);
  4415. {$endif}
  4416. {Unlock the medium blocks}
  4417. MediumBlocksLocked := False;
  4418. end
  4419. else
  4420. begin
  4421. {Allocate a Large block}
  4422. if ASize > 0 then
  4423. Result := AllocateLargeBlock(ASize)
  4424. else
  4425. Result := nil;
  4426. end;
  4427. end;
  4428. end;
  4429. {$else}
  4430. {$ifdef 32Bit}
  4431. asm
  4432. {On entry:
  4433. eax = ASize}
  4434. {Since most allocations are for small blocks, determine the small block type
  4435. index so long}
  4436. lea edx, [eax + BlockHeaderSize - 1]
  4437. {$ifdef Align16Bytes}
  4438. shr edx, 4
  4439. {$else}
  4440. shr edx, 3
  4441. {$endif}
  4442. {Is it a small block?}
  4443. cmp eax, (MaximumSmallBlockSize - BlockHeaderSize)
  4444. {Save ebx}
  4445. push ebx
  4446. {Get the IsMultiThread variable so long}
  4447. {$ifndef AssumeMultiThreaded}
  4448. mov cl, IsMultiThread
  4449. {$endif}
  4450. {Is it a small block?}
  4451. ja @NotASmallBlock
  4452. {Do we need to lock the block type?}
  4453. {$ifndef AssumeMultiThreaded}
  4454. test cl, cl
  4455. {$endif}
  4456. {Get the small block type in ebx}
  4457. movzx eax, byte ptr [AllocSize2SmallBlockTypeIndX4 + edx]
  4458. lea ebx, [SmallBlockTypes + eax * 8]
  4459. {Do we need to lock the block type?}
  4460. {$ifndef AssumeMultiThreaded}
  4461. jnz @LockBlockTypeLoop
  4462. {$else}
  4463. jmp @LockBlockTypeLoop
  4464. {Align branch target}
  4465. nop
  4466. nop
  4467. {$endif}
  4468. @GotLockOnSmallBlockType:
  4469. {Find the next free block: Get the first pool with free blocks in edx}
  4470. mov edx, TSmallBlockType[ebx].NextPartiallyFreePool
  4471. {Get the first free block (or the next sequential feed address if edx = ebx)}
  4472. mov eax, TSmallBlockPoolHeader[edx].FirstFreeBlock
  4473. {Get the drop flags mask in ecx so long}
  4474. mov ecx, DropSmallFlagsMask
  4475. {Is there a pool with free blocks?}
  4476. cmp edx, ebx
  4477. je @TrySmallSequentialFeed
  4478. {Increment the number of used blocks}
  4479. add TSmallBlockPoolHeader[edx].BlocksInUse, 1
  4480. {Get the new first free block}
  4481. and ecx, [eax - 4]
  4482. {Set the new first free block}
  4483. mov TSmallBlockPoolHeader[edx].FirstFreeBlock, ecx
  4484. {Set the block header}
  4485. mov [eax - 4], edx
  4486. {Is the chunk now full?}
  4487. jz @RemoveSmallPool
  4488. {Unlock the block type}
  4489. mov TSmallBlockType[ebx].BlockTypeLocked, False
  4490. {Restore ebx}
  4491. pop ebx
  4492. {All done}
  4493. ret
  4494. {Align branch target}
  4495. {$ifndef AssumeMultiThreaded}
  4496. nop
  4497. nop
  4498. {$endif}
  4499. nop
  4500. @TrySmallSequentialFeed:
  4501. {Try to feed a small block sequentially: Get the sequential feed block pool}
  4502. mov edx, TSmallBlockType[ebx].CurrentSequentialFeedPool
  4503. {Get the next sequential feed address so long}
  4504. movzx ecx, TSmallBlockType[ebx].BlockSize
  4505. add ecx, eax
  4506. {Can another block fit?}
  4507. cmp eax, TSmallBlockType[ebx].MaxSequentialFeedBlockAddress
  4508. ja @AllocateSmallBlockPool
  4509. {Increment the number of used blocks in the sequential feed pool}
  4510. add TSmallBlockPoolHeader[edx].BlocksInUse, 1
  4511. {Store the next sequential feed block address}
  4512. mov TSmallBlockType[ebx].NextSequentialFeedBlockAddress, ecx
  4513. {Unlock the block type}
  4514. mov TSmallBlockType[ebx].BlockTypeLocked, False
  4515. {Set the block header}
  4516. mov [eax - 4], edx
  4517. {Restore ebx}
  4518. pop ebx
  4519. {All done}
  4520. ret
  4521. {Align branch target}
  4522. nop
  4523. nop
  4524. nop
  4525. @RemoveSmallPool:
  4526. {Pool is full - remove it from the partially free list}
  4527. mov ecx, TSmallBlockPoolHeader[edx].NextPartiallyFreePool
  4528. mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, ebx
  4529. mov TSmallBlockType[ebx].NextPartiallyFreePool, ecx
  4530. {Unlock the block type}
  4531. mov TSmallBlockType[ebx].BlockTypeLocked, False
  4532. {Restore ebx}
  4533. pop ebx
  4534. {All done}
  4535. ret
  4536. {Align branch target}
  4537. nop
  4538. nop
  4539. @LockBlockTypeLoop:
  4540. mov eax, $100
  4541. {Attempt to grab the block type}
  4542. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  4543. je @GotLockOnSmallBlockType
  4544. {Try the next size}
  4545. add ebx, Type(TSmallBlockType)
  4546. mov eax, $100
  4547. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  4548. je @GotLockOnSmallBlockType
  4549. {Try the next size (up to two sizes larger)}
  4550. add ebx, Type(TSmallBlockType)
  4551. mov eax, $100
  4552. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  4553. je @GotLockOnSmallBlockType
  4554. {Block type and two sizes larger are all locked - give up and sleep}
  4555. sub ebx, 2 * Type(TSmallBlockType)
  4556. {$ifdef NeverSleepOnThreadContention}
  4557. {Pause instruction (improves performance on P4)}
  4558. rep nop
  4559. {$ifdef UseSwitchToThread}
  4560. call SwitchToThread
  4561. {$endif}
  4562. {Try again}
  4563. jmp @LockBlockTypeLoop
  4564. {Align branch target}
  4565. nop
  4566. {$ifndef UseSwitchToThread}
  4567. nop
  4568. {$endif}
  4569. {$else}
  4570. {Couldn't grab the block type - sleep and try again}
  4571. push InitialSleepTime
  4572. call Sleep
  4573. {Try again}
  4574. mov eax, $100
  4575. {Attempt to grab the block type}
  4576. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  4577. je @GotLockOnSmallBlockType
  4578. {Couldn't grab the block type - sleep and try again}
  4579. push AdditionalSleepTime
  4580. call Sleep
  4581. {Try again}
  4582. jmp @LockBlockTypeLoop
  4583. {Align branch target}
  4584. nop
  4585. nop
  4586. nop
  4587. {$endif}
  4588. @AllocateSmallBlockPool:
  4589. {save additional registers}
  4590. push esi
  4591. push edi
  4592. {Do we need to lock the medium blocks?}
  4593. {$ifndef AssumeMultiThreaded}
  4594. cmp IsMultiThread, False
  4595. je @MediumBlocksLockedForPool
  4596. {$endif}
  4597. call LockMediumBlocks
  4598. @MediumBlocksLockedForPool:
  4599. {Are there any available blocks of a suitable size?}
  4600. movsx esi, TSmallBlockType[ebx].AllowedGroupsForBlockPoolBitmap
  4601. and esi, MediumBlockBinGroupBitmap
  4602. jz @NoSuitableMediumBlocks
  4603. {Get the bin group number with free blocks in eax}
  4604. bsf eax, esi
  4605. {Get the bin number in ecx}
  4606. lea esi, [eax * 8]
  4607. mov ecx, dword ptr [MediumBlockBinBitmaps + eax * 4]
  4608. bsf ecx, ecx
  4609. lea ecx, [ecx + esi * 4]
  4610. {Get a pointer to the bin in edi}
  4611. lea edi, [MediumBlockBins + ecx * 8]
  4612. {Get the free block in esi}
  4613. mov esi, TMediumFreeBlock[edi].NextFreeBlock
  4614. {Remove the first block from the linked list (LIFO)}
  4615. mov edx, TMediumFreeBlock[esi].NextFreeBlock
  4616. mov TMediumFreeBlock[edi].NextFreeBlock, edx
  4617. mov TMediumFreeBlock[edx].PreviousFreeBlock, edi
  4618. {Is this bin now empty?}
  4619. cmp edi, edx
  4620. jne @MediumBinNotEmpty
  4621. {eax = bin group number, ecx = bin number, edi = @bin, esi = free block, ebx = block type}
  4622. {Flag this bin as empty}
  4623. mov edx, -2
  4624. rol edx, cl
  4625. and dword ptr [MediumBlockBinBitmaps + eax * 4], edx
  4626. jnz @MediumBinNotEmpty
  4627. {Flag the group as empty}
  4628. btr MediumBlockBinGroupBitmap, eax
  4629. @MediumBinNotEmpty:
  4630. {esi = free block, ebx = block type}
  4631. {Get the size of the available medium block in edi}
  4632. mov edi, DropMediumAndLargeFlagsMask
  4633. and edi, [esi - 4]
  4634. cmp edi, MaximumSmallBlockPoolSize
  4635. jb @UseWholeBlock
  4636. {Split the block: get the size of the second part, new block size is the
  4637. optimal size}
  4638. mov edx, edi
  4639. movzx edi, TSmallBlockType[ebx].OptimalBlockPoolSize
  4640. sub edx, edi
  4641. {Split the block in two}
  4642. lea eax, [esi + edi]
  4643. lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  4644. mov [eax - 4], ecx
  4645. {Store the size of the second split as the second last dword}
  4646. mov [eax + edx - 8], edx
  4647. {Put the remainder in a bin (it will be big enough)}
  4648. call InsertMediumBlockIntoBin
  4649. jmp @GotMediumBlock
  4650. {Align branch target}
  4651. {$ifdef AssumeMultiThreaded}
  4652. nop
  4653. {$endif}
  4654. @NoSuitableMediumBlocks:
  4655. {Check the sequential feed medium block pool for space}
  4656. movzx ecx, TSmallBlockType[ebx].MinimumBlockPoolSize
  4657. mov edi, MediumSequentialFeedBytesLeft
  4658. cmp edi, ecx
  4659. jb @AllocateNewSequentialFeed
  4660. {Get the address of the last block that was fed}
  4661. mov esi, LastSequentiallyFedMediumBlock
  4662. {Enough sequential feed space: Will the remainder be usable?}
  4663. movzx ecx, TSmallBlockType[ebx].OptimalBlockPoolSize
  4664. lea edx, [ecx + MinimumMediumBlockSize]
  4665. cmp edi, edx
  4666. jb @NotMuchSpace
  4667. mov edi, ecx
  4668. @NotMuchSpace:
  4669. sub esi, edi
  4670. {Update the sequential feed parameters}
  4671. sub MediumSequentialFeedBytesLeft, edi
  4672. mov LastSequentiallyFedMediumBlock, esi
  4673. {Get the block pointer}
  4674. jmp @GotMediumBlock
  4675. {Align branch target}
  4676. @AllocateNewSequentialFeed:
  4677. {Need to allocate a new sequential feed medium block pool: use the
  4678. optimal size for this small block pool}
  4679. movzx eax, TSmallBlockType[ebx].OptimalBlockPoolSize
  4680. mov edi, eax
  4681. {Allocate the medium block pool}
  4682. call AllocNewSequentialFeedMediumPool
  4683. mov esi, eax
  4684. test eax, eax
  4685. jnz @GotMediumBlock
  4686. mov MediumBlocksLocked, al
  4687. mov TSmallBlockType[ebx].BlockTypeLocked, al
  4688. pop edi
  4689. pop esi
  4690. pop ebx
  4691. ret
  4692. {Align branch target}
  4693. @UseWholeBlock:
  4694. {esi = free block, ebx = block type, edi = block size}
  4695. {Mark this block as used in the block following it}
  4696. and byte ptr [esi + edi - 4], not PreviousMediumBlockIsFreeFlag
  4697. @GotMediumBlock:
  4698. {esi = free block, ebx = block type, edi = block size}
  4699. {Set the size and flags for this block}
  4700. lea ecx, [edi + IsMediumBlockFlag + IsSmallBlockPoolInUseFlag]
  4701. mov [esi - 4], ecx
  4702. {Unlock medium blocks}
  4703. xor eax, eax
  4704. mov MediumBlocksLocked, al
  4705. {Set up the block pool}
  4706. mov TSmallBlockPoolHeader[esi].BlockType, ebx
  4707. mov TSmallBlockPoolHeader[esi].FirstFreeBlock, eax
  4708. mov TSmallBlockPoolHeader[esi].BlocksInUse, 1
  4709. {Set it up for sequential block serving}
  4710. mov TSmallBlockType[ebx].CurrentSequentialFeedPool, esi
  4711. {Return the pointer to the first block}
  4712. lea eax, [esi + SmallBlockPoolHeaderSize]
  4713. movzx ecx, TSmallBlockType[ebx].BlockSize
  4714. lea edx, [eax + ecx]
  4715. mov TSmallBlockType[ebx].NextSequentialFeedBlockAddress, edx
  4716. add edi, esi
  4717. sub edi, ecx
  4718. mov TSmallBlockType[ebx].MaxSequentialFeedBlockAddress, edi
  4719. {Unlock the small block type}
  4720. mov TSmallBlockType[ebx].BlockTypeLocked, False
  4721. {Set the small block header}
  4722. mov [eax - 4], esi
  4723. {Restore registers}
  4724. pop edi
  4725. pop esi
  4726. pop ebx
  4727. {Done}
  4728. ret
  4729. {-------------------Medium block allocation-------------------}
  4730. {Align branch target}
  4731. nop
  4732. @NotASmallBlock:
  4733. cmp eax, (MaximumMediumBlockSize - BlockHeaderSize)
  4734. ja @IsALargeBlockRequest
  4735. {Get the bin size for this block size. Block sizes are
  4736. rounded up to the next bin size.}
  4737. lea ebx, [eax + MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset]
  4738. and ebx, -MediumBlockGranularity
  4739. add ebx, MediumBlockSizeOffset
  4740. {Do we need to lock the medium blocks?}
  4741. {$ifndef AssumeMultiThreaded}
  4742. test cl, cl
  4743. jz @MediumBlocksLocked
  4744. {$endif}
  4745. call LockMediumBlocks
  4746. @MediumBlocksLocked:
  4747. {Get the bin number in ecx and the group number in edx}
  4748. lea edx, [ebx - MinimumMediumBlockSize]
  4749. mov ecx, edx
  4750. shr edx, 8 + 5
  4751. shr ecx, 8
  4752. {Is there a suitable block inside this group?}
  4753. mov eax, -1
  4754. shl eax, cl
  4755. and eax, dword ptr [MediumBlockBinBitmaps + edx * 4]
  4756. jz @GroupIsEmpty
  4757. {Get the actual bin number}
  4758. and ecx, -32
  4759. bsf eax, eax
  4760. or ecx, eax
  4761. jmp @GotBinAndGroup
  4762. {Align branch target}
  4763. nop
  4764. @GroupIsEmpty:
  4765. {Try all groups greater than this group}
  4766. mov eax, -2
  4767. mov ecx, edx
  4768. shl eax, cl
  4769. and eax, MediumBlockBinGroupBitmap
  4770. jz @TrySequentialFeedMedium
  4771. {There is a suitable group with space: get the bin number}
  4772. bsf edx, eax
  4773. {Get the bin in the group with free blocks}
  4774. mov eax, dword ptr [MediumBlockBinBitmaps + edx * 4]
  4775. bsf ecx, eax
  4776. mov eax, edx
  4777. shl eax, 5
  4778. or ecx, eax
  4779. jmp @GotBinAndGroup
  4780. {Align branch target}
  4781. nop
  4782. @TrySequentialFeedMedium:
  4783. mov ecx, MediumSequentialFeedBytesLeft
  4784. {Block can be fed sequentially?}
  4785. sub ecx, ebx
  4786. jc @AllocateNewSequentialFeedForMedium
  4787. {Get the block address}
  4788. mov eax, LastSequentiallyFedMediumBlock
  4789. sub eax, ebx
  4790. mov LastSequentiallyFedMediumBlock, eax
  4791. {Store the remaining bytes}
  4792. mov MediumSequentialFeedBytesLeft, ecx
  4793. {Set the flags for the block}
  4794. or ebx, IsMediumBlockFlag
  4795. mov [eax - 4], ebx
  4796. jmp @MediumBlockGetDone
  4797. {Align branch target}
  4798. @AllocateNewSequentialFeedForMedium:
  4799. mov eax, ebx
  4800. call AllocNewSequentialFeedMediumPool
  4801. @MediumBlockGetDone:
  4802. mov MediumBlocksLocked, False
  4803. pop ebx
  4804. ret
  4805. {Align branch target}
  4806. @GotBinAndGroup:
  4807. {ebx = block size, ecx = bin number, edx = group number}
  4808. push esi
  4809. push edi
  4810. {Get a pointer to the bin in edi}
  4811. lea edi, [MediumBlockBins + ecx * 8]
  4812. {Get the free block in esi}
  4813. mov esi, TMediumFreeBlock[edi].NextFreeBlock
  4814. {Remove the first block from the linked list (LIFO)}
  4815. mov eax, TMediumFreeBlock[esi].NextFreeBlock
  4816. mov TMediumFreeBlock[edi].NextFreeBlock, eax
  4817. mov TMediumFreeBlock[eax].PreviousFreeBlock, edi
  4818. {Is this bin now empty?}
  4819. cmp edi, eax
  4820. jne @MediumBinNotEmptyForMedium
  4821. {eax = bin group number, ecx = bin number, edi = @bin, esi = free block, ebx = block size}
  4822. {Flag this bin as empty}
  4823. mov eax, -2
  4824. rol eax, cl
  4825. and dword ptr [MediumBlockBinBitmaps + edx * 4], eax
  4826. jnz @MediumBinNotEmptyForMedium
  4827. {Flag the group as empty}
  4828. btr MediumBlockBinGroupBitmap, edx
  4829. @MediumBinNotEmptyForMedium:
  4830. {esi = free block, ebx = block size}
  4831. {Get the size of the available medium block in edi}
  4832. mov edi, DropMediumAndLargeFlagsMask
  4833. and edi, [esi - 4]
  4834. {Get the size of the second split in edx}
  4835. mov edx, edi
  4836. sub edx, ebx
  4837. jz @UseWholeBlockForMedium
  4838. {Split the block in two}
  4839. lea eax, [esi + ebx]
  4840. lea ecx, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  4841. mov [eax - 4], ecx
  4842. {Store the size of the second split as the second last dword}
  4843. mov [eax + edx - 8], edx
  4844. {Put the remainder in a bin}
  4845. cmp edx, MinimumMediumBlockSize
  4846. jb @GotMediumBlockForMedium
  4847. call InsertMediumBlockIntoBin
  4848. jmp @GotMediumBlockForMedium
  4849. {Align branch target}
  4850. nop
  4851. nop
  4852. nop
  4853. @UseWholeBlockForMedium:
  4854. {Mark this block as used in the block following it}
  4855. and byte ptr [esi + edi - 4], not PreviousMediumBlockIsFreeFlag
  4856. @GotMediumBlockForMedium:
  4857. {Set the size and flags for this block}
  4858. lea ecx, [ebx + IsMediumBlockFlag]
  4859. mov [esi - 4], ecx
  4860. {Unlock medium blocks}
  4861. mov MediumBlocksLocked, False
  4862. mov eax, esi
  4863. pop edi
  4864. pop esi
  4865. pop ebx
  4866. ret
  4867. {-------------------Large block allocation-------------------}
  4868. {Align branch target}
  4869. @IsALargeBlockRequest:
  4870. pop ebx
  4871. test eax, eax
  4872. jns AllocateLargeBlock
  4873. xor eax, eax
  4874. end;
  4875. {$else}
  4876. {64-bit BASM implementation}
  4877. asm
  4878. {On entry:
  4879. rcx = ASize}
  4880. .params 2
  4881. .pushnv rbx
  4882. .pushnv rsi
  4883. .pushnv rdi
  4884. {Since most allocations are for small blocks, determine the small block type
  4885. index so long}
  4886. lea edx, [ecx + BlockHeaderSize - 1]
  4887. {$ifdef Align16Bytes}
  4888. shr edx, 4
  4889. {$else}
  4890. shr edx, 3
  4891. {$endif}
  4892. {Preload the addresses of some small block structures}
  4893. lea r8, AllocSize2SmallBlockTypeIndX4
  4894. lea rbx, SmallBlockTypes
  4895. {$ifndef AssumeMultiThreaded}
  4896. {Get the IsMultiThread variable so long}
  4897. movzx esi, IsMultiThread
  4898. {$endif}
  4899. {Is it a small block?}
  4900. cmp rcx, (MaximumSmallBlockSize - BlockHeaderSize)
  4901. ja @NotASmallBlock
  4902. {Get the small block type pointer in rbx}
  4903. movzx ecx, byte ptr [r8 + rdx]
  4904. shl ecx, 4 //SizeOf(TSmallBlockType) = 64
  4905. add rbx, rcx
  4906. {Do we need to lock the block type?}
  4907. {$ifndef AssumeMultiThreaded}
  4908. test esi, esi
  4909. jnz @LockBlockTypeLoop
  4910. {$else}
  4911. jmp @LockBlockTypeLoop
  4912. {$endif}
  4913. @GotLockOnSmallBlockType:
  4914. {Find the next free block: Get the first pool with free blocks in rdx}
  4915. mov rdx, TSmallBlockType[rbx].NextPartiallyFreePool
  4916. {Get the first free block (or the next sequential feed address if rdx = rbx)}
  4917. mov rax, TSmallBlockPoolHeader[rdx].FirstFreeBlock
  4918. {Get the drop flags mask in rcx so long}
  4919. mov rcx, DropSmallFlagsMask
  4920. {Is there a pool with free blocks?}
  4921. cmp rdx, rbx
  4922. je @TrySmallSequentialFeed
  4923. {Increment the number of used blocks}
  4924. add TSmallBlockPoolHeader[rdx].BlocksInUse, 1
  4925. {Get the new first free block}
  4926. and rcx, [rax - BlockHeaderSize]
  4927. {Set the new first free block}
  4928. mov TSmallBlockPoolHeader[rdx].FirstFreeBlock, rcx
  4929. {Set the block header}
  4930. mov [rax - BlockHeaderSize], rdx
  4931. {Is the chunk now full?}
  4932. jz @RemoveSmallPool
  4933. {Unlock the block type}
  4934. mov TSmallBlockType[rbx].BlockTypeLocked, False
  4935. jmp @Done
  4936. @TrySmallSequentialFeed:
  4937. {Try to feed a small block sequentially: Get the sequential feed block pool}
  4938. mov rdx, TSmallBlockType[rbx].CurrentSequentialFeedPool
  4939. {Get the next sequential feed address so long}
  4940. movzx ecx, TSmallBlockType[rbx].BlockSize
  4941. add rcx, rax
  4942. {Can another block fit?}
  4943. cmp rax, TSmallBlockType[rbx].MaxSequentialFeedBlockAddress
  4944. ja @AllocateSmallBlockPool
  4945. {Increment the number of used blocks in the sequential feed pool}
  4946. add TSmallBlockPoolHeader[rdx].BlocksInUse, 1
  4947. {Store the next sequential feed block address}
  4948. mov TSmallBlockType[rbx].NextSequentialFeedBlockAddress, rcx
  4949. {Unlock the block type}
  4950. mov TSmallBlockType[rbx].BlockTypeLocked, False
  4951. {Set the block header}
  4952. mov [rax - BlockHeaderSize], rdx
  4953. jmp @Done
  4954. @RemoveSmallPool:
  4955. {Pool is full - remove it from the partially free list}
  4956. mov rcx, TSmallBlockPoolHeader[rdx].NextPartiallyFreePool
  4957. mov TSmallBlockPoolHeader[rcx].PreviousPartiallyFreePool, rbx
  4958. mov TSmallBlockType[rbx].NextPartiallyFreePool, rcx
  4959. {Unlock the block type}
  4960. mov TSmallBlockType[rbx].BlockTypeLocked, False
  4961. jmp @Done
  4962. @LockBlockTypeLoop:
  4963. mov eax, $100
  4964. {Attempt to grab the block type}
  4965. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  4966. je @GotLockOnSmallBlockType
  4967. {Try the next size}
  4968. add rbx, Type(TSmallBlockType)
  4969. mov eax, $100
  4970. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  4971. je @GotLockOnSmallBlockType
  4972. {Try the next size (up to two sizes larger)}
  4973. add rbx, Type(TSmallBlockType)
  4974. mov eax, $100
  4975. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  4976. je @GotLockOnSmallBlockType
  4977. {Block type and two sizes larger are all locked - give up and sleep}
  4978. sub rbx, 2 * Type(TSmallBlockType)
  4979. {$ifdef NeverSleepOnThreadContention}
  4980. {Pause instruction (improves performance on P4)}
  4981. pause
  4982. {$ifdef UseSwitchToThread}
  4983. call SwitchToThread
  4984. {$endif}
  4985. {Try again}
  4986. jmp @LockBlockTypeLoop
  4987. {$else}
  4988. {Couldn't grab the block type - sleep and try again}
  4989. mov ecx, InitialSleepTime
  4990. call Sleep
  4991. {Try again}
  4992. mov eax, $100
  4993. {Attempt to grab the block type}
  4994. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  4995. je @GotLockOnSmallBlockType
  4996. {Couldn't grab the block type - sleep and try again}
  4997. mov ecx, AdditionalSleepTime
  4998. call Sleep
  4999. {Try again}
  5000. jmp @LockBlockTypeLoop
  5001. {$endif}
  5002. @AllocateSmallBlockPool:
  5003. {Do we need to lock the medium blocks?}
  5004. {$ifndef AssumeMultiThreaded}
  5005. test esi, esi
  5006. jz @MediumBlocksLockedForPool
  5007. {$endif}
  5008. call LockMediumBlocks
  5009. @MediumBlocksLockedForPool:
  5010. {Are there any available blocks of a suitable size?}
  5011. movsx esi, TSmallBlockType[rbx].AllowedGroupsForBlockPoolBitmap
  5012. and esi, MediumBlockBinGroupBitmap
  5013. jz @NoSuitableMediumBlocks
  5014. {Get the bin group number with free blocks in eax}
  5015. bsf eax, esi
  5016. {Get the bin number in ecx}
  5017. lea r8, MediumBlockBinBitmaps
  5018. lea r9, [rax * 4]
  5019. mov ecx, [r8 + r9]
  5020. bsf ecx, ecx
  5021. lea ecx, [ecx + r9d * 8]
  5022. {Get a pointer to the bin in edi}
  5023. lea rdi, MediumBlockBins
  5024. lea esi, [ecx * 8]
  5025. lea rdi, [rdi + rsi * 2] //SizeOf(TMediumBlockBin) = 16
  5026. {Get the free block in rsi}
  5027. mov rsi, TMediumFreeBlock[rdi].NextFreeBlock
  5028. {Remove the first block from the linked list (LIFO)}
  5029. mov rdx, TMediumFreeBlock[rsi].NextFreeBlock
  5030. mov TMediumFreeBlock[rdi].NextFreeBlock, rdx
  5031. mov TMediumFreeBlock[rdx].PreviousFreeBlock, rdi
  5032. {Is this bin now empty?}
  5033. cmp rdi, rdx
  5034. jne @MediumBinNotEmpty
  5035. {r8 = @MediumBlockBinBitmaps, eax = bin group number,
  5036. r9 = bin group number * 4, ecx = bin number, edi = @bin, esi = free block,
  5037. ebx = block type}
  5038. {Flag this bin as empty}
  5039. mov edx, -2
  5040. rol edx, cl
  5041. and [r8 + r9], edx
  5042. jnz @MediumBinNotEmpty
  5043. {Flag the group as empty}
  5044. btr MediumBlockBinGroupBitmap, eax
  5045. @MediumBinNotEmpty:
  5046. {esi = free block, ebx = block type}
  5047. {Get the size of the available medium block in edi}
  5048. mov rdi, DropMediumAndLargeFlagsMask
  5049. and rdi, [rsi - BlockHeaderSize]
  5050. cmp edi, MaximumSmallBlockPoolSize
  5051. jb @UseWholeBlock
  5052. {Split the block: get the size of the second part, new block size is the
  5053. optimal size}
  5054. mov edx, edi
  5055. movzx edi, TSmallBlockType[rbx].OptimalBlockPoolSize
  5056. sub edx, edi
  5057. {Split the block in two}
  5058. lea rcx, [rsi + rdi]
  5059. lea rax, [rdx + IsMediumBlockFlag + IsFreeBlockFlag]
  5060. mov [rcx - BlockHeaderSize], rax
  5061. {Store the size of the second split as the second last qword}
  5062. mov [rcx + rdx - BlockHeaderSize * 2], rdx
  5063. {Put the remainder in a bin (it will be big enough)}
  5064. call InsertMediumBlockIntoBin
  5065. jmp @GotMediumBlock
  5066. @NoSuitableMediumBlocks:
  5067. {Check the sequential feed medium block pool for space}
  5068. movzx ecx, TSmallBlockType[rbx].MinimumBlockPoolSize
  5069. mov edi, MediumSequentialFeedBytesLeft
  5070. cmp edi, ecx
  5071. jb @AllocateNewSequentialFeed
  5072. {Get the address of the last block that was fed}
  5073. mov rsi, LastSequentiallyFedMediumBlock
  5074. {Enough sequential feed space: Will the remainder be usable?}
  5075. movzx ecx, TSmallBlockType[rbx].OptimalBlockPoolSize
  5076. lea edx, [ecx + MinimumMediumBlockSize]
  5077. cmp edi, edx
  5078. jb @NotMuchSpace
  5079. mov edi, ecx
  5080. @NotMuchSpace:
  5081. sub rsi, rdi
  5082. {Update the sequential feed parameters}
  5083. sub MediumSequentialFeedBytesLeft, edi
  5084. mov LastSequentiallyFedMediumBlock, rsi
  5085. {Get the block pointer}
  5086. jmp @GotMediumBlock
  5087. {Align branch target}
  5088. @AllocateNewSequentialFeed:
  5089. {Need to allocate a new sequential feed medium block pool: use the
  5090. optimal size for this small block pool}
  5091. movzx ecx, TSmallBlockType[rbx].OptimalBlockPoolSize
  5092. mov edi, ecx
  5093. {Allocate the medium block pool}
  5094. call AllocNewSequentialFeedMediumPool
  5095. mov rsi, rax
  5096. test rax, rax
  5097. jnz @GotMediumBlock
  5098. mov MediumBlocksLocked, al
  5099. mov TSmallBlockType[rbx].BlockTypeLocked, al
  5100. jmp @Done
  5101. @UseWholeBlock:
  5102. {rsi = free block, rbx = block type, edi = block size}
  5103. {Mark this block as used in the block following it}
  5104. and byte ptr [rsi + rdi - BlockHeaderSize], not PreviousMediumBlockIsFreeFlag
  5105. @GotMediumBlock:
  5106. {rsi = free block, rbx = block type, edi = block size}
  5107. {Set the size and flags for this block}
  5108. lea ecx, [edi + IsMediumBlockFlag + IsSmallBlockPoolInUseFlag]
  5109. mov [rsi - BlockHeaderSize], rcx
  5110. {Unlock medium blocks}
  5111. xor eax, eax
  5112. mov MediumBlocksLocked, al
  5113. {Set up the block pool}
  5114. mov TSmallBlockPoolHeader[rsi].BlockType, rbx
  5115. mov TSmallBlockPoolHeader[rsi].FirstFreeBlock, rax
  5116. mov TSmallBlockPoolHeader[rsi].BlocksInUse, 1
  5117. {Set it up for sequential block serving}
  5118. mov TSmallBlockType[rbx].CurrentSequentialFeedPool, rsi
  5119. {Return the pointer to the first block}
  5120. lea rax, [rsi + SmallBlockPoolHeaderSize]
  5121. movzx ecx, TSmallBlockType[rbx].BlockSize
  5122. lea rdx, [rax + rcx]
  5123. mov TSmallBlockType[rbx].NextSequentialFeedBlockAddress, rdx
  5124. add rdi, rsi
  5125. sub rdi, rcx
  5126. mov TSmallBlockType[rbx].MaxSequentialFeedBlockAddress, rdi
  5127. {Unlock the small block type}
  5128. mov TSmallBlockType[rbx].BlockTypeLocked, False
  5129. {Set the small block header}
  5130. mov [rax - BlockHeaderSize], rsi
  5131. jmp @Done
  5132. {-------------------Medium block allocation-------------------}
  5133. @NotASmallBlock:
  5134. cmp rcx, (MaximumMediumBlockSize - BlockHeaderSize)
  5135. ja @IsALargeBlockRequest
  5136. {Get the bin size for this block size. Block sizes are
  5137. rounded up to the next bin size.}
  5138. lea ebx, [ecx + MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset]
  5139. and ebx, -MediumBlockGranularity
  5140. add ebx, MediumBlockSizeOffset
  5141. {Do we need to lock the medium blocks?}
  5142. {$ifndef AssumeMultiThreaded}
  5143. test esi, esi
  5144. jz @MediumBlocksLocked
  5145. {$endif}
  5146. call LockMediumBlocks
  5147. @MediumBlocksLocked:
  5148. {Get the bin number in ecx and the group number in edx}
  5149. lea edx, [ebx - MinimumMediumBlockSize]
  5150. mov ecx, edx
  5151. shr edx, 8 + 5
  5152. shr ecx, 8
  5153. {Is there a suitable block inside this group?}
  5154. mov eax, -1
  5155. shl eax, cl
  5156. lea r8, MediumBlockBinBitmaps
  5157. and eax, [r8 + rdx * 4]
  5158. jz @GroupIsEmpty
  5159. {Get the actual bin number}
  5160. and ecx, -32
  5161. bsf eax, eax
  5162. or ecx, eax
  5163. jmp @GotBinAndGroup
  5164. @GroupIsEmpty:
  5165. {Try all groups greater than this group}
  5166. mov eax, -2
  5167. mov ecx, edx
  5168. shl eax, cl
  5169. and eax, MediumBlockBinGroupBitmap
  5170. jz @TrySequentialFeedMedium
  5171. {There is a suitable group with space: get the bin number}
  5172. bsf edx, eax
  5173. {Get the bin in the group with free blocks}
  5174. mov eax, [r8 + rdx * 4]
  5175. bsf ecx, eax
  5176. mov eax, edx
  5177. shl eax, 5
  5178. or ecx, eax
  5179. jmp @GotBinAndGroup
  5180. @TrySequentialFeedMedium:
  5181. mov ecx, MediumSequentialFeedBytesLeft
  5182. {Block can be fed sequentially?}
  5183. sub ecx, ebx
  5184. jc @AllocateNewSequentialFeedForMedium
  5185. {Get the block address}
  5186. mov rax, LastSequentiallyFedMediumBlock
  5187. sub rax, rbx
  5188. mov LastSequentiallyFedMediumBlock, rax
  5189. {Store the remaining bytes}
  5190. mov MediumSequentialFeedBytesLeft, ecx
  5191. {Set the flags for the block}
  5192. or rbx, IsMediumBlockFlag
  5193. mov [rax - BlockHeaderSize], rbx
  5194. jmp @MediumBlockGetDone
  5195. @AllocateNewSequentialFeedForMedium:
  5196. mov ecx, ebx
  5197. call AllocNewSequentialFeedMediumPool
  5198. @MediumBlockGetDone:
  5199. xor cl, cl
  5200. mov MediumBlocksLocked, cl //workaround for QC99023
  5201. jmp @Done
  5202. @GotBinAndGroup:
  5203. {ebx = block size, ecx = bin number, edx = group number}
  5204. {Get a pointer to the bin in edi}
  5205. lea rdi, MediumBlockBins
  5206. lea eax, [ecx + ecx]
  5207. lea rdi, [rdi + rax * 8]
  5208. {Get the free block in esi}
  5209. mov rsi, TMediumFreeBlock[rdi].NextFreeBlock
  5210. {Remove the first block from the linked list (LIFO)}
  5211. mov rax, TMediumFreeBlock[rsi].NextFreeBlock
  5212. mov TMediumFreeBlock[rdi].NextFreeBlock, rax
  5213. mov TMediumFreeBlock[rax].PreviousFreeBlock, rdi
  5214. {Is this bin now empty?}
  5215. cmp rdi, rax
  5216. jne @MediumBinNotEmptyForMedium
  5217. {edx = bin group number, ecx = bin number, rdi = @bin, rsi = free block, ebx = block size}
  5218. {Flag this bin as empty}
  5219. mov eax, -2
  5220. rol eax, cl
  5221. lea r8, MediumBlockBinBitmaps
  5222. and [r8 + rdx * 4], eax
  5223. jnz @MediumBinNotEmptyForMedium
  5224. {Flag the group as empty}
  5225. btr MediumBlockBinGroupBitmap, edx
  5226. @MediumBinNotEmptyForMedium:
  5227. {rsi = free block, ebx = block size}
  5228. {Get the size of the available medium block in edi}
  5229. mov rdi, DropMediumAndLargeFlagsMask
  5230. and rdi, [rsi - BlockHeaderSize]
  5231. {Get the size of the second split in edx}
  5232. mov edx, edi
  5233. sub edx, ebx
  5234. jz @UseWholeBlockForMedium
  5235. {Split the block in two}
  5236. lea rcx, [rsi + rbx]
  5237. lea rax, [rdx + IsMediumBlockFlag + IsFreeBlockFlag]
  5238. mov [rcx - BlockHeaderSize], rax
  5239. {Store the size of the second split as the second last dword}
  5240. mov [rcx + rdx - BlockHeaderSize * 2], rdx
  5241. {Put the remainder in a bin}
  5242. cmp edx, MinimumMediumBlockSize
  5243. jb @GotMediumBlockForMedium
  5244. call InsertMediumBlockIntoBin
  5245. jmp @GotMediumBlockForMedium
  5246. @UseWholeBlockForMedium:
  5247. {Mark this block as used in the block following it}
  5248. and byte ptr [rsi + rdi - BlockHeaderSize], not PreviousMediumBlockIsFreeFlag
  5249. @GotMediumBlockForMedium:
  5250. {Set the size and flags for this block}
  5251. lea rcx, [rbx + IsMediumBlockFlag]
  5252. mov [rsi - BlockHeaderSize], rcx
  5253. {Unlock medium blocks}
  5254. xor cl, cl
  5255. mov MediumBlocksLocked, cl //workaround for QC99023
  5256. mov rax, rsi
  5257. jmp @Done
  5258. {-------------------Large block allocation-------------------}
  5259. @IsALargeBlockRequest:
  5260. xor rax, rax
  5261. test rcx, rcx
  5262. js @Done
  5263. call AllocateLargeBlock
  5264. @Done:
  5265. end;
  5266. {$endif}
  5267. {$endif}
  5268. {$ifndef ASMVersion}
  5269. {Frees a medium block, returning 0 on success, -1 otherwise}
  5270. function FreeMediumBlock(APointer: Pointer): Integer;
  5271. var
  5272. LNextMediumBlock{$ifndef FullDebugMode}, LPreviousMediumBlock{$endif}: PMediumFreeBlock;
  5273. LNextMediumBlockSizeAndFlags: NativeUInt;
  5274. LBlockSize{$ifndef FullDebugMode}, LPreviousMediumBlockSize{$endif}: Cardinal;
  5275. {$ifndef FullDebugMode}
  5276. LPPreviousMediumBlockPoolHeader, LPNextMediumBlockPoolHeader: PMediumBlockPoolHeader;
  5277. {$endif}
  5278. LBlockHeader: NativeUInt;
  5279. begin
  5280. {Get the block header}
  5281. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  5282. {Get the medium block size}
  5283. LBlockSize := LBlockHeader and DropMediumAndLargeFlagsMask;
  5284. {Lock the medium blocks}
  5285. LockMediumBlocks;
  5286. {Can we combine this block with the next free block?}
  5287. LNextMediumBlock := PMediumFreeBlock(PByte(APointer) + LBlockSize);
  5288. LNextMediumBlockSizeAndFlags := PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^;
  5289. {$ifndef FullDebugMode}
  5290. {$ifdef CheckHeapForCorruption}
  5291. {Check that this block was flagged as in use in the next block}
  5292. if (LNextMediumBlockSizeAndFlags and PreviousMediumBlockIsFreeFlag) <> 0 then
  5293. {$ifdef BCB6OrDelphi7AndUp}
  5294. System.Error(reInvalidPtr);
  5295. {$else}
  5296. System.RunError(reInvalidPtr);
  5297. {$endif}
  5298. {$endif}
  5299. if (LNextMediumBlockSizeAndFlags and IsFreeBlockFlag) <> 0 then
  5300. begin
  5301. {Increase the size of this block}
  5302. Inc(LBlockSize, LNextMediumBlockSizeAndFlags and DropMediumAndLargeFlagsMask);
  5303. {Remove the next block as well}
  5304. if LNextMediumBlockSizeAndFlags >= MinimumMediumBlockSize then
  5305. RemoveMediumFreeBlock(LNextMediumBlock);
  5306. end
  5307. else
  5308. begin
  5309. {$endif}
  5310. {Reset the "previous in use" flag of the next block}
  5311. PNativeUInt(PByte(LNextMediumBlock) - BlockHeaderSize)^ := LNextMediumBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
  5312. {$ifndef FullDebugMode}
  5313. end;
  5314. {Can we combine this block with the previous free block? We need to
  5315. re-read the flags since it could have changed before we could lock the
  5316. medium blocks.}
  5317. if (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and PreviousMediumBlockIsFreeFlag) <> 0 then
  5318. begin
  5319. {Get the size of the free block just before this one}
  5320. LPreviousMediumBlockSize := PNativeUInt(PByte(APointer) - 2 * BlockHeaderSize)^;
  5321. {Get the start of the previous block}
  5322. LPreviousMediumBlock := PMediumFreeBlock(PByte(APointer) - LPreviousMediumBlockSize);
  5323. {$ifdef CheckHeapForCorruption}
  5324. {Check that the previous block is actually free}
  5325. if (PNativeUInt(PByte(LPreviousMediumBlock) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag) then
  5326. {$ifdef BCB6OrDelphi7AndUp}
  5327. System.Error(reInvalidPtr);
  5328. {$else}
  5329. System.RunError(reInvalidPtr);
  5330. {$endif}
  5331. {$endif}
  5332. {Set the new block size}
  5333. Inc(LBlockSize, LPreviousMediumBlockSize);
  5334. {This is the new current block}
  5335. APointer := LPreviousMediumBlock;
  5336. {Remove the previous block from the linked list}
  5337. if LPreviousMediumBlockSize >= MinimumMediumBlockSize then
  5338. RemoveMediumFreeBlock(LPreviousMediumBlock);
  5339. end;
  5340. {$ifdef CheckHeapForCorruption}
  5341. {Check that the previous block is currently flagged as in use}
  5342. if (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and PreviousMediumBlockIsFreeFlag) <> 0 then
  5343. {$ifdef BCB6OrDelphi7AndUp}
  5344. System.Error(reInvalidPtr);
  5345. {$else}
  5346. System.RunError(reInvalidPtr);
  5347. {$endif}
  5348. {$endif}
  5349. {Is the entire medium block pool free, and there are other free blocks
  5350. that can fit the largest possible medium block? -> free it. (Except in
  5351. full debug mode where medium pools are never freed.)}
  5352. if (LBlockSize <> (MediumBlockPoolSize - MediumBlockPoolHeaderSize)) then
  5353. begin
  5354. {Store the size of the block as well as the flags}
  5355. PNativeUInt(PByte(APointer) - BlockHeaderSize)^ := LBlockSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  5356. {$else}
  5357. {Mark the block as free}
  5358. Inc(PNativeUInt(PByte(APointer) - BlockHeaderSize)^, IsFreeBlockFlag);
  5359. {$endif}
  5360. {Store the trailing size marker}
  5361. PNativeUInt(PByte(APointer) + LBlockSize - 2 * BlockHeaderSize)^ := LBlockSize;
  5362. {Insert this block back into the bins: Size check not required here,
  5363. since medium blocks that are in use are not allowed to be
  5364. shrunk smaller than MinimumMediumBlockSize}
  5365. InsertMediumBlockIntoBin(APointer, LBlockSize);
  5366. {$ifndef FullDebugMode}
  5367. {$ifdef CheckHeapForCorruption}
  5368. {Check that this block is actually free and the next and previous blocks are both in use.}
  5369. if ((PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask) <> (IsMediumBlockFlag or IsFreeBlockFlag))
  5370. or ((PNativeUInt(PByte(APointer) + (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and DropMediumAndLargeFlagsMask) - BlockHeaderSize)^ and IsFreeBlockFlag) <> 0) then
  5371. begin
  5372. {$ifdef BCB6OrDelphi7AndUp}
  5373. System.Error(reInvalidPtr);
  5374. {$else}
  5375. System.RunError(reInvalidPtr);
  5376. {$endif}
  5377. end;
  5378. {$endif}
  5379. {$endif}
  5380. {Unlock medium blocks}
  5381. MediumBlocksLocked := False;
  5382. {All OK}
  5383. Result := 0;
  5384. {$ifndef FullDebugMode}
  5385. end
  5386. else
  5387. begin
  5388. {Should this become the new sequential feed?}
  5389. if MediumSequentialFeedBytesLeft <> MediumBlockPoolSize - MediumBlockPoolHeaderSize then
  5390. begin
  5391. {Bin the current sequential feed}
  5392. BinMediumSequentialFeedRemainder;
  5393. {Set this medium pool up as the new sequential feed pool:
  5394. Store the sequential feed pool trailer}
  5395. PNativeUInt(PByte(APointer) + LBlockSize - BlockHeaderSize)^ := IsMediumBlockFlag;
  5396. {Store the number of bytes available in the sequential feed chunk}
  5397. MediumSequentialFeedBytesLeft := MediumBlockPoolSize - MediumBlockPoolHeaderSize;
  5398. {Set the last sequentially fed block}
  5399. LastSequentiallyFedMediumBlock := Pointer(PByte(APointer) + LBlockSize);
  5400. {Unlock medium blocks}
  5401. MediumBlocksLocked := False;
  5402. {Success}
  5403. Result := 0;
  5404. end
  5405. else
  5406. begin
  5407. {Remove this medium block pool from the linked list}
  5408. Dec(PByte(APointer), MediumBlockPoolHeaderSize);
  5409. LPPreviousMediumBlockPoolHeader := PMediumBlockPoolHeader(APointer).PreviousMediumBlockPoolHeader;
  5410. LPNextMediumBlockPoolHeader := PMediumBlockPoolHeader(APointer).NextMediumBlockPoolHeader;
  5411. LPPreviousMediumBlockPoolHeader.NextMediumBlockPoolHeader := LPNextMediumBlockPoolHeader;
  5412. LPNextMediumBlockPoolHeader.PreviousMediumBlockPoolHeader := LPPreviousMediumBlockPoolHeader;
  5413. {Unlock medium blocks}
  5414. MediumBlocksLocked := False;
  5415. {$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
  5416. FillChar(APointer^, MediumBlockPoolSize, 0);
  5417. {$endif}
  5418. {Free the medium block pool}
  5419. if VirtualFree(APointer, 0, MEM_RELEASE) then
  5420. Result := 0
  5421. else
  5422. Result := -1;
  5423. end;
  5424. end;
  5425. {$endif}
  5426. end;
  5427. {$endif}
  5428. {Replacement for SysFreeMem}
  5429. function FastFreeMem(APointer: Pointer): Integer;
  5430. {$ifndef ASMVersion}
  5431. var
  5432. LPSmallBlockPool{$ifndef FullDebugMode}, LPPreviousPool, LPNextPool{$endif},
  5433. LPOldFirstPool: PSmallBlockPoolHeader;
  5434. LPSmallBlockType: PSmallBlockType;
  5435. LOldFirstFreeBlock: Pointer;
  5436. LBlockHeader: NativeUInt;
  5437. begin
  5438. {Get the small block header: Is it actually a small block?}
  5439. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  5440. {Is it a small block that is in use?}
  5441. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
  5442. begin
  5443. {Get a pointer to the block pool}
  5444. LPSmallBlockPool := PSmallBlockPoolHeader(LBlockHeader);
  5445. {Get the block type}
  5446. LPSmallBlockType := LPSmallBlockPool.BlockType;
  5447. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5448. FillChar(APointer^, LPSmallBlockType.BlockSize - BlockHeaderSize, 0);
  5449. {$endif}
  5450. {Lock the block type}
  5451. {$ifndef AssumeMultiThreaded}
  5452. if IsMultiThread then
  5453. {$endif}
  5454. begin
  5455. while (LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) <> 0) do
  5456. begin
  5457. {$ifdef NeverSleepOnThreadContention}
  5458. {$ifdef UseSwitchToThread}
  5459. SwitchToThread;
  5460. {$endif}
  5461. {$else}
  5462. Sleep(InitialSleepTime);
  5463. if LockCmpxchg(0, 1, @LPSmallBlockType.BlockTypeLocked) = 0 then
  5464. Break;
  5465. Sleep(AdditionalSleepTime);
  5466. {$endif}
  5467. end;
  5468. end;
  5469. {Get the old first free block}
  5470. LOldFirstFreeBlock := LPSmallBlockPool.FirstFreeBlock;
  5471. {Was the pool manager previously full?}
  5472. if LOldFirstFreeBlock = nil then
  5473. begin
  5474. {Insert this as the first partially free pool for the block size}
  5475. LPOldFirstPool := LPSmallBlockType.NextPartiallyFreePool;
  5476. LPSmallBlockPool.NextPartiallyFreePool := LPOldFirstPool;
  5477. LPOldFirstPool.PreviousPartiallyFreePool := LPSmallBlockPool;
  5478. LPSmallBlockPool.PreviousPartiallyFreePool := PSmallBlockPoolHeader(LPSmallBlockType);
  5479. LPSmallBlockType.NextPartiallyFreePool := LPSmallBlockPool;
  5480. end;
  5481. {Store the old first free block}
  5482. PNativeUInt(PByte(APointer) - BlockHeaderSize)^ := UIntPtr(LOldFirstFreeBlock) or IsFreeBlockFlag;
  5483. {Store this as the new first free block}
  5484. LPSmallBlockPool.FirstFreeBlock := APointer;
  5485. {Decrement the number of allocated blocks}
  5486. Dec(LPSmallBlockPool.BlocksInUse);
  5487. {Small block pools are never freed in full debug mode. This increases the
  5488. likehood of success in catching objects still being used after being
  5489. destroyed.}
  5490. {$ifndef FullDebugMode}
  5491. {Is the entire pool now free? -> Free it.}
  5492. if LPSmallBlockPool.BlocksInUse = 0 then
  5493. begin
  5494. {Get the previous and next chunk managers}
  5495. LPPreviousPool := LPSmallBlockPool.PreviousPartiallyFreePool;
  5496. LPNextPool := LPSmallBlockPool.NextPartiallyFreePool;
  5497. {Remove this manager}
  5498. LPPreviousPool.NextPartiallyFreePool := LPNextPool;
  5499. LPNextPool.PreviousPartiallyFreePool := LPPreviousPool;
  5500. {Is this the sequential feed pool? If so, stop sequential feeding}
  5501. if (LPSmallBlockType.CurrentSequentialFeedPool = LPSmallBlockPool) then
  5502. LPSmallBlockType.MaxSequentialFeedBlockAddress := nil;
  5503. {Unlock this block type}
  5504. LPSmallBlockType.BlockTypeLocked := False;
  5505. {Free the block pool}
  5506. FreeMediumBlock(LPSmallBlockPool);
  5507. end
  5508. else
  5509. begin
  5510. {$endif}
  5511. {Unlock this block type}
  5512. LPSmallBlockType.BlockTypeLocked := False;
  5513. {$ifndef FullDebugMode}
  5514. end;
  5515. {$endif}
  5516. {No error}
  5517. Result := 0;
  5518. end
  5519. else
  5520. begin
  5521. {Is this a medium block or a large block?}
  5522. if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
  5523. begin
  5524. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5525. {Get the block header, extract the block size and clear the block it.}
  5526. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  5527. FillChar(APointer^,
  5528. (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize, 0);
  5529. {$endif}
  5530. Result := FreeMediumBlock(APointer);
  5531. end
  5532. else
  5533. begin
  5534. {Validate: Is this actually a Large block, or is it an attempt to free an
  5535. already freed small block?}
  5536. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
  5537. Result := FreeLargeBlock(APointer)
  5538. else
  5539. Result := -1;
  5540. end;
  5541. end;
  5542. end;
  5543. {$else}
  5544. {$ifdef 32Bit}
  5545. asm
  5546. {Get the block header in edx}
  5547. mov edx, [eax - 4]
  5548. {Is it a small block in use?}
  5549. test dl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
  5550. {Save the pointer in ecx}
  5551. mov ecx, eax
  5552. {Save ebx}
  5553. push ebx
  5554. {Get the IsMultiThread variable in bl}
  5555. {$ifndef AssumeMultiThreaded}
  5556. mov bl, IsMultiThread
  5557. {$endif}
  5558. {Is it a small block that is in use?}
  5559. jnz @NotSmallBlockInUse
  5560. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5561. push edx
  5562. push ecx
  5563. mov edx, TSmallBlockPoolHeader[edx].BlockType
  5564. movzx edx, TSmallBlockType(edx).BlockSize
  5565. sub edx, BlockHeaderSize
  5566. xor ecx, ecx
  5567. call System.@FillChar
  5568. pop ecx
  5569. pop edx
  5570. {$endif}
  5571. {Do we need to lock the block type?}
  5572. {$ifndef AssumeMultiThreaded}
  5573. test bl, bl
  5574. {$endif}
  5575. {Get the small block type in ebx}
  5576. mov ebx, TSmallBlockPoolHeader[edx].BlockType
  5577. {Do we need to lock the block type?}
  5578. {$ifndef AssumeMultiThreaded}
  5579. jnz @LockBlockTypeLoop
  5580. {$else}
  5581. jmp @LockBlockTypeLoop
  5582. {Align branch target}
  5583. nop
  5584. {$endif}
  5585. @GotLockOnSmallBlockType:
  5586. {Current state: edx = @SmallBlockPoolHeader, ecx = APointer, ebx = @SmallBlockType}
  5587. {Decrement the number of blocks in use}
  5588. sub TSmallBlockPoolHeader[edx].BlocksInUse, 1
  5589. {Get the old first free block}
  5590. mov eax, TSmallBlockPoolHeader[edx].FirstFreeBlock
  5591. {Is the pool now empty?}
  5592. jz @PoolIsNowEmpty
  5593. {Was the pool full?}
  5594. test eax, eax
  5595. {Store this as the new first free block}
  5596. mov TSmallBlockPoolHeader[edx].FirstFreeBlock, ecx
  5597. {Store the previous first free block as the block header}
  5598. lea eax, [eax + IsFreeBlockFlag]
  5599. mov [ecx - 4], eax
  5600. {Insert the pool back into the linked list if it was full}
  5601. jz @SmallPoolWasFull
  5602. {All ok}
  5603. xor eax, eax
  5604. {Unlock the block type}
  5605. mov TSmallBlockType[ebx].BlockTypeLocked, al
  5606. {Restore registers}
  5607. pop ebx
  5608. {Done}
  5609. ret
  5610. {Align branch target}
  5611. {$ifndef AssumeMultiThreaded}
  5612. nop
  5613. {$endif}
  5614. @SmallPoolWasFull:
  5615. {Insert this as the first partially free pool for the block size}
  5616. mov ecx, TSmallBlockType[ebx].NextPartiallyFreePool
  5617. mov TSmallBlockPoolHeader[edx].PreviousPartiallyFreePool, ebx
  5618. mov TSmallBlockPoolHeader[edx].NextPartiallyFreePool, ecx
  5619. mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, edx
  5620. mov TSmallBlockType[ebx].NextPartiallyFreePool, edx
  5621. {Unlock the block type}
  5622. mov TSmallBlockType[ebx].BlockTypeLocked, False
  5623. {All ok}
  5624. xor eax, eax
  5625. {Restore registers}
  5626. pop ebx
  5627. {Done}
  5628. ret
  5629. {Align branch target}
  5630. nop
  5631. nop
  5632. @PoolIsNowEmpty:
  5633. {Was this pool actually in the linked list of pools with space? If not, it
  5634. can only be the sequential feed pool (it is the only pool that may contain
  5635. only one block, i.e. other blocks have not been split off yet)}
  5636. test eax, eax
  5637. jz @IsSequentialFeedPool
  5638. {Pool is now empty: Remove it from the linked list and free it}
  5639. mov eax, TSmallBlockPoolHeader[edx].PreviousPartiallyFreePool
  5640. mov ecx, TSmallBlockPoolHeader[edx].NextPartiallyFreePool
  5641. {Remove this manager}
  5642. mov TSmallBlockPoolHeader[eax].NextPartiallyFreePool, ecx
  5643. mov TSmallBlockPoolHeader[ecx].PreviousPartiallyFreePool, eax
  5644. {Zero out eax}
  5645. xor eax, eax
  5646. {Is this the sequential feed pool? If so, stop sequential feeding}
  5647. cmp TSmallBlockType[ebx].CurrentSequentialFeedPool, edx
  5648. jne @NotSequentialFeedPool
  5649. @IsSequentialFeedPool:
  5650. mov TSmallBlockType[ebx].MaxSequentialFeedBlockAddress, eax
  5651. @NotSequentialFeedPool:
  5652. {Unlock the block type}
  5653. mov TSmallBlockType[ebx].BlockTypeLocked, al
  5654. {Release this pool}
  5655. mov eax, edx
  5656. mov edx, [edx - 4]
  5657. {$ifndef AssumeMultiThreaded}
  5658. mov bl, IsMultiThread
  5659. {$endif}
  5660. jmp @FreeMediumBlock
  5661. {Align branch target}
  5662. {$ifndef AssumeMultiThreaded}
  5663. nop
  5664. nop
  5665. {$endif}
  5666. nop
  5667. @LockBlockTypeLoop:
  5668. mov eax, $100
  5669. {Attempt to grab the block type}
  5670. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  5671. je @GotLockOnSmallBlockType
  5672. {$ifdef NeverSleepOnThreadContention}
  5673. {Pause instruction (improves performance on P4)}
  5674. rep nop
  5675. {$ifdef UseSwitchToThread}
  5676. push ecx
  5677. push edx
  5678. call SwitchToThread
  5679. pop edx
  5680. pop ecx
  5681. {$endif}
  5682. {Try again}
  5683. jmp @LockBlockTypeLoop
  5684. {Align branch target}
  5685. {$ifndef UseSwitchToThread}
  5686. nop
  5687. {$endif}
  5688. {$else}
  5689. {Couldn't grab the block type - sleep and try again}
  5690. push ecx
  5691. push edx
  5692. push InitialSleepTime
  5693. call Sleep
  5694. pop edx
  5695. pop ecx
  5696. {Try again}
  5697. mov eax, $100
  5698. {Attempt to grab the block type}
  5699. lock cmpxchg TSmallBlockType([ebx]).BlockTypeLocked, ah
  5700. je @GotLockOnSmallBlockType
  5701. {Couldn't grab the block type - sleep and try again}
  5702. push ecx
  5703. push edx
  5704. push AdditionalSleepTime
  5705. call Sleep
  5706. pop edx
  5707. pop ecx
  5708. {Try again}
  5709. jmp @LockBlockTypeLoop
  5710. {Align branch target}
  5711. nop
  5712. nop
  5713. {$endif}
  5714. {---------------------Medium blocks------------------------------}
  5715. {Align branch target}
  5716. @NotSmallBlockInUse:
  5717. {Not a small block in use: is it a medium or large block?}
  5718. test dl, IsFreeBlockFlag + IsLargeBlockFlag
  5719. jnz @NotASmallOrMediumBlock
  5720. @FreeMediumBlock:
  5721. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5722. push eax
  5723. push edx
  5724. and edx, DropMediumAndLargeFlagsMask
  5725. sub edx, BlockHeaderSize
  5726. xor ecx, ecx
  5727. call System.@FillChar
  5728. pop edx
  5729. pop eax
  5730. {$endif}
  5731. {Drop the flags}
  5732. and edx, DropMediumAndLargeFlagsMask
  5733. {Free the medium block pointed to by eax, header in edx, bl = IsMultiThread}
  5734. {$ifndef AssumeMultiThreaded}
  5735. {Do we need to lock the medium blocks?}
  5736. test bl, bl
  5737. {$endif}
  5738. {Block size in ebx}
  5739. mov ebx, edx
  5740. {Save registers}
  5741. push esi
  5742. {Pointer in esi}
  5743. mov esi, eax
  5744. {Do we need to lock the medium blocks?}
  5745. {$ifndef AssumeMultiThreaded}
  5746. jz @MediumBlocksLocked
  5747. {$endif}
  5748. call LockMediumBlocks
  5749. @MediumBlocksLocked:
  5750. {Can we combine this block with the next free block?}
  5751. test dword ptr [esi + ebx - 4], IsFreeBlockFlag
  5752. {Get the next block size and flags in ecx}
  5753. mov ecx, [esi + ebx - 4]
  5754. jnz @NextBlockIsFree
  5755. {Set the "PreviousIsFree" flag in the next block}
  5756. or ecx, PreviousMediumBlockIsFreeFlag
  5757. mov [esi + ebx - 4], ecx
  5758. @NextBlockChecked:
  5759. {Can we combine this block with the previous free block? We need to
  5760. re-read the flags since it could have changed before we could lock the
  5761. medium blocks.}
  5762. test byte ptr [esi - 4], PreviousMediumBlockIsFreeFlag
  5763. jnz @PreviousBlockIsFree
  5764. @PreviousBlockChecked:
  5765. {Is the entire medium block pool free, and there are other free blocks
  5766. that can fit the largest possible medium block -> free it.}
  5767. cmp ebx, (MediumBlockPoolSize - MediumBlockPoolHeaderSize)
  5768. je @EntireMediumPoolFree
  5769. @BinFreeMediumBlock:
  5770. {Store the size of the block as well as the flags}
  5771. lea eax, [ebx + IsMediumBlockFlag + IsFreeBlockFlag]
  5772. mov [esi - 4], eax
  5773. {Store the trailing size marker}
  5774. mov [esi + ebx - 8], ebx
  5775. {Insert this block back into the bins: Size check not required here,
  5776. since medium blocks that are in use are not allowed to be
  5777. shrunk smaller than MinimumMediumBlockSize}
  5778. mov eax, esi
  5779. mov edx, ebx
  5780. {Insert into bin}
  5781. call InsertMediumBlockIntoBin
  5782. {Unlock medium blocks}
  5783. mov MediumBlocksLocked, False;
  5784. {All OK}
  5785. xor eax, eax
  5786. {Restore registers}
  5787. pop esi
  5788. pop ebx
  5789. {Return}
  5790. ret
  5791. {Align branch target}
  5792. @NextBlockIsFree:
  5793. {Get the next block address in eax}
  5794. lea eax, [esi + ebx]
  5795. {Increase the size of this block}
  5796. and ecx, DropMediumAndLargeFlagsMask
  5797. add ebx, ecx
  5798. {Was the block binned?}
  5799. cmp ecx, MinimumMediumBlockSize
  5800. jb @NextBlockChecked
  5801. call RemoveMediumFreeBlock
  5802. jmp @NextBlockChecked
  5803. {Align branch target}
  5804. nop
  5805. @PreviousBlockIsFree:
  5806. {Get the size of the free block just before this one}
  5807. mov ecx, [esi - 8]
  5808. {Include the previous block}
  5809. sub esi, ecx
  5810. {Set the new block size}
  5811. add ebx, ecx
  5812. {Remove the previous block from the linked list}
  5813. cmp ecx, MinimumMediumBlockSize
  5814. jb @PreviousBlockChecked
  5815. mov eax, esi
  5816. call RemoveMediumFreeBlock
  5817. jmp @PreviousBlockChecked
  5818. {Align branch target}
  5819. @EntireMediumPoolFree:
  5820. {Should we make this the new sequential feed medium block pool? If the
  5821. current sequential feed pool is not entirely free, we make this the new
  5822. sequential feed pool.}
  5823. cmp MediumSequentialFeedBytesLeft, MediumBlockPoolSize - MediumBlockPoolHeaderSize
  5824. jne @MakeEmptyMediumPoolSequentialFeed
  5825. {Point esi to the medium block pool header}
  5826. sub esi, MediumBlockPoolHeaderSize
  5827. {Remove this medium block pool from the linked list}
  5828. mov eax, TMediumBlockPoolHeader[esi].PreviousMediumBlockPoolHeader
  5829. mov edx, TMediumBlockPoolHeader[esi].NextMediumBlockPoolHeader
  5830. mov TMediumBlockPoolHeader[eax].NextMediumBlockPoolHeader, edx
  5831. mov TMediumBlockPoolHeader[edx].PreviousMediumBlockPoolHeader, eax
  5832. {Unlock medium blocks}
  5833. mov MediumBlocksLocked, False;
  5834. {$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
  5835. mov eax, esi
  5836. mov edx, MediumBlockPoolSize
  5837. xor ecx, ecx
  5838. call System.@FillChar
  5839. {$endif}
  5840. {Free the medium block pool}
  5841. push MEM_RELEASE
  5842. push 0
  5843. push esi
  5844. call VirtualFree
  5845. {VirtualFree returns >0 if all is ok}
  5846. cmp eax, 1
  5847. {Return 0 on all ok}
  5848. sbb eax, eax
  5849. {Restore registers}
  5850. pop esi
  5851. pop ebx
  5852. ret
  5853. {Align branch target}
  5854. nop
  5855. nop
  5856. nop
  5857. @MakeEmptyMediumPoolSequentialFeed:
  5858. {Get a pointer to the end-marker block}
  5859. lea ebx, [esi + MediumBlockPoolSize - MediumBlockPoolHeaderSize]
  5860. {Bin the current sequential feed pool}
  5861. call BinMediumSequentialFeedRemainder
  5862. {Set this medium pool up as the new sequential feed pool:
  5863. Store the sequential feed pool trailer}
  5864. mov dword ptr [ebx - BlockHeaderSize], IsMediumBlockFlag
  5865. {Store the number of bytes available in the sequential feed chunk}
  5866. mov MediumSequentialFeedBytesLeft, MediumBlockPoolSize - MediumBlockPoolHeaderSize
  5867. {Set the last sequentially fed block}
  5868. mov LastSequentiallyFedMediumBlock, ebx
  5869. {Unlock medium blocks}
  5870. mov MediumBlocksLocked, False;
  5871. {Success}
  5872. xor eax, eax
  5873. {Restore registers}
  5874. pop esi
  5875. pop ebx
  5876. ret
  5877. {Align branch target}
  5878. nop
  5879. nop
  5880. @NotASmallOrMediumBlock:
  5881. {Restore ebx}
  5882. pop ebx
  5883. {Is it in fact a large block?}
  5884. test dl, IsFreeBlockFlag + IsMediumBlockFlag
  5885. jz FreeLargeBlock
  5886. {Attempt to free an already free block}
  5887. mov eax, -1
  5888. end;
  5889. {$else}
  5890. {---------------64-bit BASM FastFreeMem---------------}
  5891. asm
  5892. .params 3
  5893. .pushnv rbx
  5894. .pushnv rsi
  5895. {Get the block header in rdx}
  5896. mov rdx, [rcx - BlockHeaderSize]
  5897. {Is it a small block in use?}
  5898. test dl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
  5899. {Get the IsMultiThread variable in bl}
  5900. {$ifndef AssumeMultiThreaded}
  5901. mov bl, IsMultiThread
  5902. {$endif}
  5903. {Is it a small block that is in use?}
  5904. jnz @NotSmallBlockInUse
  5905. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  5906. mov rsi, rcx
  5907. mov rdx, TSmallBlockPoolHeader[rdx].BlockType
  5908. movzx edx, TSmallBlockType(rdx).BlockSize
  5909. sub edx, BlockHeaderSize
  5910. xor r8, r8
  5911. call System.@FillChar
  5912. mov rcx, rsi
  5913. mov rdx, [rcx - BlockHeaderSize]
  5914. {$endif}
  5915. {Do we need to lock the block type?}
  5916. {$ifndef AssumeMultiThreaded}
  5917. test bl, bl
  5918. {$endif}
  5919. {Get the small block type in rbx}
  5920. mov rbx, TSmallBlockPoolHeader[rdx].BlockType
  5921. {Do we need to lock the block type?}
  5922. {$ifndef AssumeMultiThreaded}
  5923. jnz @LockBlockTypeLoop
  5924. {$else}
  5925. jmp @LockBlockTypeLoop
  5926. {$endif}
  5927. @GotLockOnSmallBlockType:
  5928. {Current state: rdx = @SmallBlockPoolHeader, rcx = APointer, rbx = @SmallBlockType}
  5929. {Decrement the number of blocks in use}
  5930. sub TSmallBlockPoolHeader[rdx].BlocksInUse, 1
  5931. {Get the old first free block}
  5932. mov rax, TSmallBlockPoolHeader[rdx].FirstFreeBlock
  5933. {Is the pool now empty?}
  5934. jz @PoolIsNowEmpty
  5935. {Was the pool full?}
  5936. test rax, rax
  5937. {Store this as the new first free block}
  5938. mov TSmallBlockPoolHeader[rdx].FirstFreeBlock, rcx
  5939. {Store the previous first free block as the block header}
  5940. lea rax, [rax + IsFreeBlockFlag]
  5941. mov [rcx - BlockHeaderSize], rax
  5942. {Insert the pool back into the linked list if it was full}
  5943. jz @SmallPoolWasFull
  5944. {All ok}
  5945. xor eax, eax
  5946. {Unlock the block type}
  5947. mov TSmallBlockType[rbx].BlockTypeLocked, al
  5948. jmp @Done
  5949. @SmallPoolWasFull:
  5950. {Insert this as the first partially free pool for the block size}
  5951. mov rcx, TSmallBlockType[rbx].NextPartiallyFreePool
  5952. mov TSmallBlockPoolHeader[rdx].PreviousPartiallyFreePool, rbx
  5953. mov TSmallBlockPoolHeader[rdx].NextPartiallyFreePool, rcx
  5954. mov TSmallBlockPoolHeader[rcx].PreviousPartiallyFreePool, rdx
  5955. mov TSmallBlockType[rbx].NextPartiallyFreePool, rdx
  5956. {Unlock the block type}
  5957. mov TSmallBlockType[rbx].BlockTypeLocked, False
  5958. {All ok}
  5959. xor eax, eax
  5960. jmp @Done
  5961. @PoolIsNowEmpty:
  5962. {Was this pool actually in the linked list of pools with space? If not, it
  5963. can only be the sequential feed pool (it is the only pool that may contain
  5964. only one block, i.e. other blocks have not been split off yet)}
  5965. test rax, rax
  5966. jz @IsSequentialFeedPool
  5967. {Pool is now empty: Remove it from the linked list and free it}
  5968. mov rax, TSmallBlockPoolHeader[rdx].PreviousPartiallyFreePool
  5969. mov rcx, TSmallBlockPoolHeader[rdx].NextPartiallyFreePool
  5970. {Remove this manager}
  5971. mov TSmallBlockPoolHeader[rax].NextPartiallyFreePool, rcx
  5972. mov TSmallBlockPoolHeader[rcx].PreviousPartiallyFreePool, rax
  5973. {Zero out eax}
  5974. xor rax, rax
  5975. {Is this the sequential feed pool? If so, stop sequential feeding}
  5976. cmp TSmallBlockType[rbx].CurrentSequentialFeedPool, rdx
  5977. jne @NotSequentialFeedPool
  5978. @IsSequentialFeedPool:
  5979. mov TSmallBlockType[rbx].MaxSequentialFeedBlockAddress, rax
  5980. @NotSequentialFeedPool:
  5981. {Unlock the block type}
  5982. mov TSmallBlockType[rbx].BlockTypeLocked, al
  5983. {Release this pool}
  5984. mov rcx, rdx
  5985. mov rdx, [rdx - BlockHeaderSize]
  5986. {$ifndef AssumeMultiThreaded}
  5987. mov bl, IsMultiThread
  5988. {$endif}
  5989. jmp @FreeMediumBlock
  5990. @LockBlockTypeLoop:
  5991. mov eax, $100
  5992. {Attempt to grab the block type}
  5993. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  5994. je @GotLockOnSmallBlockType
  5995. {$ifdef NeverSleepOnThreadContention}
  5996. {Pause instruction (improves performance on P4)}
  5997. pause
  5998. {$ifdef UseSwitchToThread}
  5999. mov rsi, rcx
  6000. call SwitchToThread
  6001. mov rcx, rsi
  6002. mov rdx, [rcx - BlockHeaderSize]
  6003. {$endif}
  6004. {Try again}
  6005. jmp @LockBlockTypeLoop
  6006. {$else}
  6007. {Couldn't grab the block type - sleep and try again}
  6008. mov rsi, rcx
  6009. mov ecx, InitialSleepTime
  6010. call Sleep
  6011. mov rcx, rsi
  6012. mov rdx, [rcx - BlockHeaderSize]
  6013. {Try again}
  6014. mov eax, $100
  6015. {Attempt to grab the block type}
  6016. lock cmpxchg TSmallBlockType([rbx]).BlockTypeLocked, ah
  6017. je @GotLockOnSmallBlockType
  6018. {Couldn't grab the block type - sleep and try again}
  6019. mov rsi, rcx
  6020. mov ecx, AdditionalSleepTime
  6021. call Sleep
  6022. mov rcx, rsi
  6023. mov rdx, [rcx - BlockHeaderSize]
  6024. {Try again}
  6025. jmp @LockBlockTypeLoop
  6026. {$endif}
  6027. {---------------------Medium blocks------------------------------}
  6028. @NotSmallBlockInUse:
  6029. {Not a small block in use: is it a medium or large block?}
  6030. test dl, IsFreeBlockFlag + IsLargeBlockFlag
  6031. jnz @NotASmallOrMediumBlock
  6032. @FreeMediumBlock:
  6033. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  6034. mov rsi, rcx
  6035. and rdx, DropMediumAndLargeFlagsMask
  6036. sub rdx, BlockHeaderSize
  6037. xor r8, r8
  6038. call System.@FillChar
  6039. mov rcx, rsi
  6040. mov rdx, [rcx - BlockHeaderSize]
  6041. {$endif}
  6042. {Drop the flags}
  6043. and rdx, DropMediumAndLargeFlagsMask
  6044. {Free the medium block pointed to by eax, header in edx, bl = IsMultiThread}
  6045. {$ifndef AssumeMultiThreaded}
  6046. {Do we need to lock the medium blocks?}
  6047. test bl, bl
  6048. {$endif}
  6049. {Block size in rbx}
  6050. mov rbx, rdx
  6051. {Pointer in rsi}
  6052. mov rsi, rcx
  6053. {Do we need to lock the medium blocks?}
  6054. {$ifndef AssumeMultiThreaded}
  6055. jz @MediumBlocksLocked
  6056. {$endif}
  6057. call LockMediumBlocks
  6058. @MediumBlocksLocked:
  6059. {Can we combine this block with the next free block?}
  6060. test qword ptr [rsi + rbx - BlockHeaderSize], IsFreeBlockFlag
  6061. {Get the next block size and flags in rcx}
  6062. mov rcx, [rsi + rbx - BlockHeaderSize]
  6063. jnz @NextBlockIsFree
  6064. {Set the "PreviousIsFree" flag in the next block}
  6065. or rcx, PreviousMediumBlockIsFreeFlag
  6066. mov [rsi + rbx - BlockHeaderSize], rcx
  6067. @NextBlockChecked:
  6068. {Can we combine this block with the previous free block? We need to
  6069. re-read the flags since it could have changed before we could lock the
  6070. medium blocks.}
  6071. test byte ptr [rsi - BlockHeaderSize], PreviousMediumBlockIsFreeFlag
  6072. jnz @PreviousBlockIsFree
  6073. @PreviousBlockChecked:
  6074. {Is the entire medium block pool free, and there are other free blocks
  6075. that can fit the largest possible medium block -> free it.}
  6076. cmp ebx, (MediumBlockPoolSize - MediumBlockPoolHeaderSize)
  6077. je @EntireMediumPoolFree
  6078. @BinFreeMediumBlock:
  6079. {Store the size of the block as well as the flags}
  6080. lea rax, [rbx + IsMediumBlockFlag + IsFreeBlockFlag]
  6081. mov [rsi - BlockHeaderSize], rax
  6082. {Store the trailing size marker}
  6083. mov [rsi + rbx - 2 * BlockHeaderSize], rbx
  6084. {Insert this block back into the bins: Size check not required here,
  6085. since medium blocks that are in use are not allowed to be
  6086. shrunk smaller than MinimumMediumBlockSize}
  6087. mov rcx, rsi
  6088. mov rdx, rbx
  6089. {Insert into bin}
  6090. call InsertMediumBlockIntoBin
  6091. {All OK}
  6092. xor eax, eax
  6093. {Unlock medium blocks}
  6094. mov MediumBlocksLocked, al
  6095. jmp @Done
  6096. @NextBlockIsFree:
  6097. {Get the next block address in rax}
  6098. lea rax, [rsi + rbx]
  6099. {Increase the size of this block}
  6100. and rcx, DropMediumAndLargeFlagsMask
  6101. add rbx, rcx
  6102. {Was the block binned?}
  6103. cmp rcx, MinimumMediumBlockSize
  6104. jb @NextBlockChecked
  6105. mov rcx, rax
  6106. call RemoveMediumFreeBlock
  6107. jmp @NextBlockChecked
  6108. @PreviousBlockIsFree:
  6109. {Get the size of the free block just before this one}
  6110. mov rcx, [rsi - 2 * BlockHeaderSize]
  6111. {Include the previous block}
  6112. sub rsi, rcx
  6113. {Set the new block size}
  6114. add rbx, rcx
  6115. {Remove the previous block from the linked list}
  6116. cmp ecx, MinimumMediumBlockSize
  6117. jb @PreviousBlockChecked
  6118. mov rcx, rsi
  6119. call RemoveMediumFreeBlock
  6120. jmp @PreviousBlockChecked
  6121. @EntireMediumPoolFree:
  6122. {Should we make this the new sequential feed medium block pool? If the
  6123. current sequential feed pool is not entirely free, we make this the new
  6124. sequential feed pool.}
  6125. lea r8, MediumSequentialFeedBytesLeft
  6126. cmp dword ptr [r8], MediumBlockPoolSize - MediumBlockPoolHeaderSize //workaround for QC99023
  6127. jne @MakeEmptyMediumPoolSequentialFeed
  6128. {Point esi to the medium block pool header}
  6129. sub rsi, MediumBlockPoolHeaderSize
  6130. {Remove this medium block pool from the linked list}
  6131. mov rax, TMediumBlockPoolHeader[rsi].PreviousMediumBlockPoolHeader
  6132. mov rdx, TMediumBlockPoolHeader[rsi].NextMediumBlockPoolHeader
  6133. mov TMediumBlockPoolHeader[rax].NextMediumBlockPoolHeader, rdx
  6134. mov TMediumBlockPoolHeader[rdx].PreviousMediumBlockPoolHeader, rax
  6135. {Unlock medium blocks}
  6136. xor eax, eax
  6137. mov MediumBlocksLocked, al
  6138. {$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
  6139. mov rcx, rsi
  6140. mov edx, MediumBlockPoolSize
  6141. xor r8, r8
  6142. call System.@FillChar
  6143. {$endif}
  6144. {Free the medium block pool}
  6145. mov rcx, rsi
  6146. xor edx, edx
  6147. mov r8d, MEM_RELEASE
  6148. call VirtualFree
  6149. {VirtualFree returns >0 if all is ok}
  6150. cmp eax, 1
  6151. {Return 0 on all ok}
  6152. sbb eax, eax
  6153. jmp @Done
  6154. @MakeEmptyMediumPoolSequentialFeed:
  6155. {Get a pointer to the end-marker block}
  6156. lea rbx, [rsi + MediumBlockPoolSize - MediumBlockPoolHeaderSize]
  6157. {Bin the current sequential feed pool}
  6158. call BinMediumSequentialFeedRemainder
  6159. {Set this medium pool up as the new sequential feed pool:
  6160. Store the sequential feed pool trailer}
  6161. mov qword ptr [rbx - BlockHeaderSize], IsMediumBlockFlag
  6162. {Store the number of bytes available in the sequential feed chunk}
  6163. lea rax, MediumSequentialFeedBytesLeft
  6164. mov dword ptr [rax], MediumBlockPoolSize - MediumBlockPoolHeaderSize //QC99023 workaround
  6165. {Set the last sequentially fed block}
  6166. mov LastSequentiallyFedMediumBlock, rbx
  6167. {Success}
  6168. xor eax, eax
  6169. {Unlock medium blocks}
  6170. mov MediumBlocksLocked, al
  6171. jmp @Done
  6172. @NotASmallOrMediumBlock:
  6173. {Attempt to free an already free block?}
  6174. mov eax, -1
  6175. {Is it in fact a large block?}
  6176. test dl, IsFreeBlockFlag + IsMediumBlockFlag
  6177. jnz @Done
  6178. call FreeLargeBlock
  6179. @Done:
  6180. end;
  6181. {$endif}
  6182. {$endif}
  6183. {$ifndef FullDebugMode}
  6184. {Replacement for SysReallocMem}
  6185. function FastReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  6186. {$ifndef ASMVersion}
  6187. var
  6188. LBlockHeader, LNextBlockSizeAndFlags, LNewAllocSize, LBlockFlags,
  6189. LOldAvailableSize, LNextBlockSize, LNewAvailableSize, LMinimumUpsize,
  6190. LSecondSplitSize, LNewBlockSize: NativeUInt;
  6191. LPSmallBlockType: PSmallBlockType;
  6192. LPNextBlock, LPNextBlockHeader: Pointer;
  6193. {Upsizes a large block in-place. The following variables are assumed correct:
  6194. LBlockFlags, LOldAvailableSize, LPNextBlock, LNextBlockSizeAndFlags,
  6195. LNextBlockSize, LNewAvailableSize. Medium blocks must be locked on entry if
  6196. required.}
  6197. procedure MediumBlockInPlaceUpsize;
  6198. begin
  6199. {Remove the next block}
  6200. if LNextBlockSizeAndFlags >= MinimumMediumBlockSize then
  6201. RemoveMediumFreeBlock(LPNextBlock);
  6202. {Add 25% for medium block in-place upsizes}
  6203. LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
  6204. if NativeUInt(ANewSize) < LMinimumUpsize then
  6205. LNewAllocSize := LMinimumUpsize
  6206. else
  6207. LNewAllocSize := NativeUInt(ANewSize);
  6208. {Round up to the nearest block size granularity}
  6209. LNewBlockSize := ((LNewAllocSize + (BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset))
  6210. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  6211. {Calculate the size of the second split}
  6212. LSecondSplitSize := LNewAvailableSize + BlockHeaderSize - LNewBlockSize;
  6213. {Does it fit?}
  6214. if NativeInt(LSecondSplitSize) <= 0 then
  6215. begin
  6216. {The block size is the full available size plus header}
  6217. LNewBlockSize := LNewAvailableSize + BlockHeaderSize;
  6218. {Grab the whole block: Mark it as used in the block following it}
  6219. LPNextBlockHeader := Pointer(PByte(APointer) + LNewAvailableSize);
  6220. PNativeUInt(LPNextBlockHeader)^ :=
  6221. PNativeUInt(LPNextBlockHeader)^ and (not PreviousMediumBlockIsFreeFlag);
  6222. end
  6223. else
  6224. begin
  6225. {Split the block in two}
  6226. LPNextBlock := PMediumFreeBlock(PByte(APointer) + LNewBlockSize);
  6227. {Set the size of the second split}
  6228. PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  6229. {Store the size of the second split before the header of the next block}
  6230. PNativeUInt(PByte(LPNextBlock) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
  6231. {Put the remainder in a bin if it is big enough}
  6232. if LSecondSplitSize >= MinimumMediumBlockSize then
  6233. InsertMediumBlockIntoBin(LPNextBlock, LSecondSplitSize);
  6234. end;
  6235. {Set the size and flags for this block}
  6236. PNativeUInt(PByte(APointer) - BlockHeaderSize)^ := LNewBlockSize or LBlockFlags;
  6237. end;
  6238. {In-place downsize of a medium block. On entry Size must be less than half of
  6239. LOldAvailableSize.}
  6240. procedure MediumBlockInPlaceDownsize;
  6241. begin
  6242. {Round up to the next medium block size}
  6243. LNewBlockSize := ((ANewSize + (BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset))
  6244. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  6245. {Get the size of the second split}
  6246. LSecondSplitSize := (LOldAvailableSize + BlockHeaderSize) - LNewBlockSize;
  6247. {Lock the medium blocks}
  6248. LockMediumBlocks;
  6249. {Set the new size}
  6250. PNativeUInt(PByte(APointer) - BlockHeaderSize)^ :=
  6251. (PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask)
  6252. or LNewBlockSize;
  6253. {Is the next block in use?}
  6254. LPNextBlock := PNativeUInt(PByte(APointer) + LOldAvailableSize + BlockHeaderSize);
  6255. LNextBlockSizeAndFlags := PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^;
  6256. if LNextBlockSizeAndFlags and IsFreeBlockFlag = 0 then
  6257. begin
  6258. {The next block is in use: flag its previous block as free}
  6259. PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^ :=
  6260. LNextBlockSizeAndFlags or PreviousMediumBlockIsFreeFlag;
  6261. end
  6262. else
  6263. begin
  6264. {The next block is free: combine it}
  6265. LNextBlockSizeAndFlags := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  6266. Inc(LSecondSplitSize, LNextBlockSizeAndFlags);
  6267. if LNextBlockSizeAndFlags >= MinimumMediumBlockSize then
  6268. RemoveMediumFreeBlock(LPNextBlock);
  6269. end;
  6270. {Set the split}
  6271. LPNextBlock := PNativeUInt(PByte(APointer) + LNewBlockSize);
  6272. {Store the free part's header}
  6273. PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^ := LSecondSplitSize or (IsMediumBlockFlag or IsFreeBlockFlag);
  6274. {Store the trailing size field}
  6275. PNativeUInt(PByte(LPNextBlock) + LSecondSplitSize - 2 * BlockHeaderSize)^ := LSecondSplitSize;
  6276. {Bin this free block}
  6277. if LSecondSplitSize >= MinimumMediumBlockSize then
  6278. InsertMediumBlockIntoBin(LPNextBlock, LSecondSplitSize);
  6279. {Unlock the medium blocks}
  6280. MediumBlocksLocked := False;
  6281. end;
  6282. begin
  6283. {Get the block header: Is it actually a small block?}
  6284. LBlockHeader := PNativeUInt(PByte(APointer) - BlockHeaderSize)^;
  6285. {Is it a small block that is in use?}
  6286. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
  6287. begin
  6288. {-----------------------------------Small block-------------------------------------}
  6289. {The block header is a pointer to the block pool: Get the block type}
  6290. LPSmallBlockType := PSmallBlockPoolHeader(LBlockHeader).BlockType;
  6291. {Get the available size inside blocks of this type.}
  6292. LOldAvailableSize := LPSmallBlockType.BlockSize - BlockHeaderSize;
  6293. {Is it an upsize or a downsize?}
  6294. if LOldAvailableSize >= NativeUInt(ANewSize) then
  6295. begin
  6296. {It's a downsize. Do we need to allocate a smaller block? Only if the new
  6297. block size is less than a quarter of the available size less
  6298. SmallBlockDownsizeCheckAdder bytes}
  6299. if (NativeUInt(ANewSize) * 4 + SmallBlockDownsizeCheckAdder) >= LOldAvailableSize then
  6300. begin
  6301. {In-place downsize - return the pointer}
  6302. Result := APointer;
  6303. Exit;
  6304. end
  6305. else
  6306. begin
  6307. {Allocate a smaller block}
  6308. Result := FastGetMem(ANewSize);
  6309. {Allocated OK?}
  6310. if Result <> nil then
  6311. begin
  6312. {Move the data across}
  6313. {$ifdef UseCustomVariableSizeMoveRoutines}
  6314. {$ifdef Align16Bytes}
  6315. MoveX16LP(APointer^, Result^, ANewSize);
  6316. {$else}
  6317. MoveX8LP(APointer^, Result^, ANewSize);
  6318. {$endif}
  6319. {$else}
  6320. System.Move(APointer^, Result^, ANewSize);
  6321. {$endif}
  6322. {Free the old pointer}
  6323. FastFreeMem(APointer);
  6324. end;
  6325. end;
  6326. end
  6327. else
  6328. begin
  6329. {This pointer is being reallocated to a larger block and therefore it is
  6330. logical to assume that it may be enlarged again. Since reallocations are
  6331. expensive, there is a minimum upsize percentage to avoid unnecessary
  6332. future move operations.}
  6333. {Must grow with at least 100% + x bytes}
  6334. LNewAllocSize := LOldAvailableSize * 2 + SmallBlockUpsizeAdder;
  6335. {Still not large enough?}
  6336. if LNewAllocSize < NativeUInt(ANewSize) then
  6337. LNewAllocSize := NativeUInt(ANewSize);
  6338. {Allocate the new block}
  6339. Result := FastGetMem(LNewAllocSize);
  6340. {Allocated OK?}
  6341. if Result <> nil then
  6342. begin
  6343. {Do we need to store the requested size? Only large blocks store the
  6344. requested size.}
  6345. if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
  6346. PLargeBlockHeader(PByte(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  6347. {Move the data across}
  6348. {$ifdef UseCustomFixedSizeMoveRoutines}
  6349. LPSmallBlockType.UpsizeMoveProcedure(APointer^, Result^, LOldAvailableSize);
  6350. {$else}
  6351. System.Move(APointer^, Result^, LOldAvailableSize);
  6352. {$endif}
  6353. {Free the old pointer}
  6354. FastFreeMem(APointer);
  6355. end;
  6356. end;
  6357. end
  6358. else
  6359. begin
  6360. {Is this a medium block or a large block?}
  6361. if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
  6362. begin
  6363. {-------------------------------Medium block--------------------------------------}
  6364. {What is the available size in the block being reallocated?}
  6365. LOldAvailableSize := (LBlockHeader and DropMediumAndLargeFlagsMask);
  6366. {Get a pointer to the next block}
  6367. LPNextBlock := PNativeUInt(PByte(APointer) + LOldAvailableSize);
  6368. {Subtract the block header size from the old available size}
  6369. Dec(LOldAvailableSize, BlockHeaderSize);
  6370. {Is it an upsize or a downsize?}
  6371. if NativeUInt(ANewSize) > LOldAvailableSize then
  6372. begin
  6373. {Can we do an in-place upsize?}
  6374. LNextBlockSizeAndFlags := PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^;
  6375. {Is the next block free?}
  6376. if LNextBlockSizeAndFlags and IsFreeBlockFlag <> 0 then
  6377. begin
  6378. LNextBlockSize := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  6379. {The available size including the next block}
  6380. LNewAvailableSize := LOldAvailableSize + LNextBlockSize;
  6381. {Can the block fit?}
  6382. if NativeUInt(ANewSize) <= LNewAvailableSize then
  6383. begin
  6384. {The next block is free and there is enough space to grow this
  6385. block in place.}
  6386. {$ifndef AssumeMultiThreaded}
  6387. if IsMultiThread then
  6388. begin
  6389. {$endif}
  6390. {Multi-threaded application - lock medium blocks and re-read the
  6391. information on the blocks.}
  6392. LockMediumBlocks;
  6393. {Re-read the info for this block}
  6394. LBlockFlags := PNativeUInt(PByte(APointer) - BlockHeaderSize)^ and ExtractMediumAndLargeFlagsMask;
  6395. {Re-read the info for the next block}
  6396. LNextBlockSizeAndFlags := PNativeUInt(PByte(LPNextBlock) - BlockHeaderSize)^;
  6397. {Recalculate the next block size}
  6398. LNextBlockSize := LNextBlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  6399. {The available size including the next block}
  6400. LNewAvailableSize := LOldAvailableSize + LNextBlockSize;
  6401. {Is the next block still free and the size still sufficient?}
  6402. if (LNextBlockSizeAndFlags and IsFreeBlockFlag <> 0)
  6403. and (NativeUInt(ANewSize) <= LNewAvailableSize) then
  6404. begin
  6405. {Upsize the block in-place}
  6406. MediumBlockInPlaceUpsize;
  6407. {Unlock the medium blocks}
  6408. MediumBlocksLocked := False;
  6409. {Return the result}
  6410. Result := APointer;
  6411. {Done}
  6412. Exit;
  6413. end;
  6414. {Couldn't use the block: Unlock the medium blocks}
  6415. MediumBlocksLocked := False;
  6416. {$ifndef AssumeMultiThreaded}
  6417. end
  6418. else
  6419. begin
  6420. {Extract the block flags}
  6421. LBlockFlags := ExtractMediumAndLargeFlagsMask and LBlockHeader;
  6422. {Upsize the block in-place}
  6423. MediumBlockInPlaceUpsize;
  6424. {Return the result}
  6425. Result := APointer;
  6426. {Done}
  6427. Exit;
  6428. end;
  6429. {$endif}
  6430. end;
  6431. end;
  6432. {Couldn't upsize in place. Grab a new block and move the data across:
  6433. If we have to reallocate and move medium blocks, we grow by at
  6434. least 25%}
  6435. LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);
  6436. if NativeUInt(ANewSize) < LMinimumUpsize then
  6437. LNewAllocSize := LMinimumUpsize
  6438. else
  6439. LNewAllocSize := NativeUInt(ANewSize);
  6440. {Allocate the new block}
  6441. Result := FastGetMem(LNewAllocSize);
  6442. if Result <> nil then
  6443. begin
  6444. {If it's a large block - store the actual user requested size}
  6445. if LNewAllocSize > (MaximumMediumBlockSize - BlockHeaderSize) then
  6446. PLargeBlockHeader(PByte(Result) - LargeBlockHeaderSize).UserAllocatedSize := ANewSize;
  6447. {Move the data across}
  6448. {$ifdef UseCustomVariableSizeMoveRoutines}
  6449. MoveX16LP(APointer^, Result^, LOldAvailableSize);
  6450. {$else}
  6451. System.Move(APointer^, Result^, LOldAvailableSize);
  6452. {$endif}
  6453. {Free the old block}
  6454. FastFreeMem(APointer);
  6455. end;
  6456. end
  6457. else
  6458. begin
  6459. {Must be less than half the current size or we don't bother resizing.}
  6460. if NativeUInt(ANewSize * 2) >= LOldAvailableSize then
  6461. begin
  6462. Result := APointer;
  6463. end
  6464. else
  6465. begin
  6466. {In-place downsize? Balance the cost of moving the data vs. the cost
  6467. of fragmenting the memory pool. Medium blocks in use may never be
  6468. smaller than MinimumMediumBlockSize.}
  6469. if NativeUInt(ANewSize) >= (MinimumMediumBlockSize - BlockHeaderSize) then
  6470. begin
  6471. MediumBlockInPlaceDownsize;
  6472. Result := APointer;
  6473. end
  6474. else
  6475. begin
  6476. {The requested size is less than the minimum medium block size. If
  6477. the requested size is less than the threshold value (currently a
  6478. quarter of the minimum medium block size), move the data to a small
  6479. block, otherwise shrink the medium block to the minimum allowable
  6480. medium block size.}
  6481. if NativeUInt(ANewSize) >= MediumInPlaceDownsizeLimit then
  6482. begin
  6483. {The request is for a size smaller than the minimum medium block
  6484. size, but not small enough to justify moving data: Reduce the
  6485. block size to the minimum medium block size}
  6486. ANewSize := MinimumMediumBlockSize - BlockHeaderSize;
  6487. {Is it already at the minimum medium block size?}
  6488. if LOldAvailableSize > NativeUInt(ANewSize) then
  6489. MediumBlockInPlaceDownsize;
  6490. Result := APointer;
  6491. end
  6492. else
  6493. begin
  6494. {Allocate the new block}
  6495. Result := FastGetMem(ANewSize);
  6496. if Result <> nil then
  6497. begin
  6498. {Move the data across}
  6499. {$ifdef UseCustomVariableSizeMoveRoutines}
  6500. {$ifdef Align16Bytes}
  6501. MoveX16LP(APointer^, Result^, ANewSize);
  6502. {$else}
  6503. MoveX8LP(APointer^, Result^, ANewSize);
  6504. {$endif}
  6505. {$else}
  6506. System.Move(APointer^, Result^, ANewSize);
  6507. {$endif}
  6508. {Free the old block}
  6509. FastFreeMem(APointer);
  6510. end;
  6511. end;
  6512. end;
  6513. end;
  6514. end;
  6515. end
  6516. else
  6517. begin
  6518. {Is this a valid large block?}
  6519. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
  6520. begin
  6521. {-----------------------Large block------------------------------}
  6522. Result := ReallocateLargeBlock(APointer, ANewSize);
  6523. end
  6524. else
  6525. begin
  6526. {-----------------------Invalid block------------------------------}
  6527. {Bad pointer: probably an attempt to reallocate a free memory block.}
  6528. Result := nil;
  6529. end;
  6530. end;
  6531. end;
  6532. end;
  6533. {$else}
  6534. {$ifdef 32Bit}
  6535. asm
  6536. {On entry: eax = APointer; edx = ANewSize}
  6537. {Get the block header: Is it actually a small block?}
  6538. mov ecx, [eax - 4]
  6539. {Is it a small block?}
  6540. test cl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
  6541. {Save ebx}
  6542. push ebx
  6543. {Save esi}
  6544. push esi
  6545. {Save the original pointer in esi}
  6546. mov esi, eax
  6547. {Is it a small block?}
  6548. jnz @NotASmallBlock
  6549. {-----------------------------------Small block-------------------------------------}
  6550. {Get the block type in ebx}
  6551. mov ebx, TSmallBlockPoolHeader[ecx].BlockType
  6552. {Get the available size inside blocks of this type.}
  6553. movzx ecx, TSmallBlockType[ebx].BlockSize
  6554. sub ecx, 4
  6555. {Is it an upsize or a downsize?}
  6556. cmp ecx, edx
  6557. jb @SmallUpsize
  6558. {It's a downsize. Do we need to allocate a smaller block? Only if the new
  6559. size is less than a quarter of the available size less
  6560. SmallBlockDownsizeCheckAdder bytes}
  6561. lea ebx, [edx * 4 + SmallBlockDownsizeCheckAdder]
  6562. cmp ebx, ecx
  6563. jb @NotSmallInPlaceDownsize
  6564. {In-place downsize - return the original pointer}
  6565. pop esi
  6566. pop ebx
  6567. ret
  6568. {Align branch target}
  6569. nop
  6570. @NotSmallInPlaceDownsize:
  6571. {Save the requested size}
  6572. mov ebx, edx
  6573. {Allocate a smaller block}
  6574. mov eax, edx
  6575. call FastGetMem
  6576. {Allocated OK?}
  6577. test eax, eax
  6578. jz @SmallDownsizeDone
  6579. {Move data across: count in ecx}
  6580. mov ecx, ebx
  6581. {Destination in edx}
  6582. mov edx, eax
  6583. {Save the result in ebx}
  6584. mov ebx, eax
  6585. {Original pointer in eax}
  6586. mov eax, esi
  6587. {Move the data across}
  6588. {$ifdef UseCustomVariableSizeMoveRoutines}
  6589. {$ifdef Align16Bytes}
  6590. call MoveX16LP
  6591. {$else}
  6592. call MoveX8LP
  6593. {$endif}
  6594. {$else}
  6595. call System.Move
  6596. {$endif}
  6597. {Free the original pointer}
  6598. mov eax, esi
  6599. call FastFreeMem
  6600. {Return the pointer}
  6601. mov eax, ebx
  6602. @SmallDownsizeDone:
  6603. pop esi
  6604. pop ebx
  6605. ret
  6606. {Align branch target}
  6607. nop
  6608. nop
  6609. @SmallUpsize:
  6610. {State: esi = APointer, edx = ANewSize, ecx = Current Block Size, ebx = Current Block Type}
  6611. {This pointer is being reallocated to a larger block and therefore it is
  6612. logical to assume that it may be enlarged again. Since reallocations are
  6613. expensive, there is a minimum upsize percentage to avoid unnecessary
  6614. future move operations.}
  6615. {Small blocks always grow with at least 100% + SmallBlockUpsizeAdder bytes}
  6616. lea ecx, [ecx + ecx + SmallBlockUpsizeAdder]
  6617. {save edi}
  6618. push edi
  6619. {Save the requested size in edi}
  6620. mov edi, edx
  6621. {New allocated size is the maximum of the requested size and the minimum
  6622. upsize}
  6623. xor eax, eax
  6624. sub ecx, edx
  6625. adc eax, -1
  6626. and eax, ecx
  6627. add eax, edx
  6628. {Allocate the new block}
  6629. call FastGetMem
  6630. {Allocated OK?}
  6631. test eax, eax
  6632. jz @SmallUpsizeDone
  6633. {Do we need to store the requested size? Only large blocks store the
  6634. requested size.}
  6635. cmp edi, MaximumMediumBlockSize - BlockHeaderSize
  6636. jbe @NotSmallUpsizeToLargeBlock
  6637. {Store the user requested size}
  6638. mov [eax - 8], edi
  6639. @NotSmallUpsizeToLargeBlock:
  6640. {Get the size to move across}
  6641. movzx ecx, TSmallBlockType[ebx].BlockSize
  6642. sub ecx, BlockHeaderSize
  6643. {Move to the new block}
  6644. mov edx, eax
  6645. {Save the result in edi}
  6646. mov edi, eax
  6647. {Move from the old block}
  6648. mov eax, esi
  6649. {Move the data across}
  6650. {$ifdef UseCustomFixedSizeMoveRoutines}
  6651. call TSmallBlockType[ebx].UpsizeMoveProcedure
  6652. {$else}
  6653. call System.Move
  6654. {$endif}
  6655. {Free the old pointer}
  6656. mov eax, esi
  6657. call FastFreeMem
  6658. {Done}
  6659. mov eax, edi
  6660. @SmallUpsizeDone:
  6661. pop edi
  6662. pop esi
  6663. pop ebx
  6664. ret
  6665. {Align branch target}
  6666. nop
  6667. @NotASmallBlock:
  6668. {Is this a medium block or a large block?}
  6669. test cl, IsFreeBlockFlag + IsLargeBlockFlag
  6670. jnz @PossibleLargeBlock
  6671. {-------------------------------Medium block--------------------------------------}
  6672. {Status: ecx = Current Block Size + Flags, eax/esi = APointer,
  6673. edx = Requested Size}
  6674. mov ebx, ecx
  6675. {Drop the flags from the header}
  6676. and ecx, DropMediumAndLargeFlagsMask
  6677. {Save edi}
  6678. push edi
  6679. {Get a pointer to the next block in edi}
  6680. lea edi, [eax + ecx]
  6681. {Subtract the block header size from the old available size}
  6682. sub ecx, BlockHeaderSize
  6683. {Get the complete flags in ebx}
  6684. and ebx, ExtractMediumAndLargeFlagsMask
  6685. {Is it an upsize or a downsize?}
  6686. cmp edx, ecx
  6687. {Save ebp}
  6688. push ebp
  6689. {Is it an upsize or a downsize?}
  6690. ja @MediumBlockUpsize
  6691. {Status: ecx = Current Block Size - 4, bl = Current Block Flags,
  6692. edi = @Next Block, eax/esi = APointer, edx = Requested Size}
  6693. {Must be less than half the current size or we don't bother resizing.}
  6694. lea ebp, [edx + edx]
  6695. cmp ebp, ecx
  6696. jb @MediumMustDownsize
  6697. @MediumNoResize:
  6698. {Restore registers}
  6699. pop ebp
  6700. pop edi
  6701. pop esi
  6702. pop ebx
  6703. {Return}
  6704. ret
  6705. {Align branch target}
  6706. nop
  6707. nop
  6708. nop
  6709. @MediumMustDownsize:
  6710. {In-place downsize? Balance the cost of moving the data vs. the cost of
  6711. fragmenting the memory pool. Medium blocks in use may never be smaller
  6712. than MinimumMediumBlockSize.}
  6713. cmp edx, MinimumMediumBlockSize - BlockHeaderSize
  6714. jae @MediumBlockInPlaceDownsize
  6715. {The requested size is less than the minimum medium block size. If the
  6716. requested size is less than the threshold value (currently a quarter of the
  6717. minimum medium block size), move the data to a small block, otherwise shrink
  6718. the medium block to the minimum allowable medium block size.}
  6719. cmp edx, MediumInPlaceDownsizeLimit
  6720. jb @MediumDownsizeRealloc
  6721. {The request is for a size smaller than the minimum medium block size, but
  6722. not small enough to justify moving data: Reduce the block size to the
  6723. minimum medium block size}
  6724. mov edx, MinimumMediumBlockSize - BlockHeaderSize
  6725. {Is it already at the minimum medium block size?}
  6726. cmp ecx, edx
  6727. jna @MediumNoResize
  6728. @MediumBlockInPlaceDownsize:
  6729. {Round up to the next medium block size}
  6730. lea ebp, [edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
  6731. and ebp, -MediumBlockGranularity;
  6732. add ebp, MediumBlockSizeOffset
  6733. {Get the size of the second split}
  6734. add ecx, BlockHeaderSize
  6735. sub ecx, ebp
  6736. {Lock the medium blocks}
  6737. {$ifndef AssumeMultiThreaded}
  6738. cmp IsMultiThread, False
  6739. je @DoMediumInPlaceDownsize
  6740. {$endif}
  6741. @DoMediumLockForDownsize:
  6742. {Lock the medium blocks (ecx *must* be preserved)}
  6743. call LockMediumBlocks
  6744. {Reread the flags - they may have changed before medium blocks could be
  6745. locked.}
  6746. mov ebx, ExtractMediumAndLargeFlagsMask
  6747. and ebx, [esi - 4]
  6748. @DoMediumInPlaceDownsize:
  6749. {Set the new size}
  6750. or ebx, ebp
  6751. mov [esi - 4], ebx
  6752. {Get the second split size in ebx}
  6753. mov ebx, ecx
  6754. {Is the next block in use?}
  6755. mov edx, [edi - 4]
  6756. test dl, IsFreeBlockFlag
  6757. jnz @MediumDownsizeNextBlockFree
  6758. {The next block is in use: flag its previous block as free}
  6759. or edx, PreviousMediumBlockIsFreeFlag
  6760. mov [edi - 4], edx
  6761. jmp @MediumDownsizeDoSplit
  6762. {Align branch target}
  6763. nop
  6764. nop
  6765. {$ifdef AssumeMultiThreaded}
  6766. nop
  6767. {$endif}
  6768. @MediumDownsizeNextBlockFree:
  6769. {The next block is free: combine it}
  6770. mov eax, edi
  6771. and edx, DropMediumAndLargeFlagsMask
  6772. add ebx, edx
  6773. add edi, edx
  6774. cmp edx, MinimumMediumBlockSize
  6775. jb @MediumDownsizeDoSplit
  6776. call RemoveMediumFreeBlock
  6777. @MediumDownsizeDoSplit:
  6778. {Store the trailing size field}
  6779. mov [edi - 8], ebx
  6780. {Store the free part's header}
  6781. lea eax, [ebx + IsMediumBlockFlag + IsFreeBlockFlag];
  6782. mov [esi + ebp - 4], eax
  6783. {Bin this free block}
  6784. cmp ebx, MinimumMediumBlockSize
  6785. jb @MediumBlockDownsizeDone
  6786. lea eax, [esi + ebp]
  6787. mov edx, ebx
  6788. call InsertMediumBlockIntoBin
  6789. @MediumBlockDownsizeDone:
  6790. {Unlock the medium blocks}
  6791. mov MediumBlocksLocked, False
  6792. {Result = old pointer}
  6793. mov eax, esi
  6794. {Restore registers}
  6795. pop ebp
  6796. pop edi
  6797. pop esi
  6798. pop ebx
  6799. {Return}
  6800. ret
  6801. {Align branch target}
  6802. @MediumDownsizeRealloc:
  6803. {Save the requested size}
  6804. mov edi, edx
  6805. mov eax, edx
  6806. {Allocate the new block}
  6807. call FastGetMem
  6808. test eax, eax
  6809. jz @MediumBlockDownsizeExit
  6810. {Save the result}
  6811. mov ebp, eax
  6812. mov edx, eax
  6813. mov eax, esi
  6814. mov ecx, edi
  6815. {Move the data across}
  6816. {$ifdef UseCustomVariableSizeMoveRoutines}
  6817. {$ifdef Align16Bytes}
  6818. call MoveX16LP
  6819. {$else}
  6820. call MoveX8LP
  6821. {$endif}
  6822. {$else}
  6823. call System.Move
  6824. {$endif}
  6825. mov eax, esi
  6826. call FastFreeMem
  6827. {Return the result}
  6828. mov eax, ebp
  6829. @MediumBlockDownsizeExit:
  6830. pop ebp
  6831. pop edi
  6832. pop esi
  6833. pop ebx
  6834. ret
  6835. {Align branch target}
  6836. @MediumBlockUpsize:
  6837. {Status: ecx = Current Block Size - 4, bl = Current Block Flags,
  6838. edi = @Next Block, eax/esi = APointer, edx = Requested Size}
  6839. {Can we do an in-place upsize?}
  6840. mov eax, [edi - 4]
  6841. test al, IsFreeBlockFlag
  6842. jz @CannotUpsizeMediumBlockInPlace
  6843. {Get the total available size including the next block}
  6844. and eax, DropMediumAndLargeFlagsMask
  6845. {ebp = total available size including the next block (excluding the header)}
  6846. lea ebp, [eax + ecx]
  6847. {Can the block fit?}
  6848. cmp edx, ebp
  6849. ja @CannotUpsizeMediumBlockInPlace
  6850. {The next block is free and there is enough space to grow this
  6851. block in place.}
  6852. {$ifndef AssumeMultiThreaded}
  6853. cmp IsMultiThread, False
  6854. je @DoMediumInPlaceUpsize
  6855. {$endif}
  6856. @DoMediumLockForUpsize:
  6857. {Lock the medium blocks (ecx and edx *must* be preserved}
  6858. call LockMediumBlocks
  6859. {Re-read the info for this block (since it may have changed before the medium
  6860. blocks could be locked)}
  6861. mov ebx, ExtractMediumAndLargeFlagsMask
  6862. and ebx, [esi - 4]
  6863. {Re-read the info for the next block}
  6864. mov eax, [edi - 4]
  6865. {Next block still free?}
  6866. test al, IsFreeBlockFlag
  6867. jz @NextMediumBlockChanged
  6868. {Recalculate the next block size}
  6869. and eax, DropMediumAndLargeFlagsMask
  6870. {The available size including the next block}
  6871. lea ebp, [eax + ecx]
  6872. {Can the block still fit?}
  6873. cmp edx, ebp
  6874. ja @NextMediumBlockChanged
  6875. @DoMediumInPlaceUpsize:
  6876. {Is the next block binnable?}
  6877. cmp eax, MinimumMediumBlockSize
  6878. {Remove the next block}
  6879. jb @MediumInPlaceNoNextRemove
  6880. mov eax, edi
  6881. push ecx
  6882. push edx
  6883. call RemoveMediumFreeBlock
  6884. pop edx
  6885. pop ecx
  6886. @MediumInPlaceNoNextRemove:
  6887. {Medium blocks grow a minimum of 25% in in-place upsizes}
  6888. mov eax, ecx
  6889. shr eax, 2
  6890. add eax, ecx
  6891. {Get the maximum of the requested size and the minimum growth size}
  6892. xor edi, edi
  6893. sub eax, edx
  6894. adc edi, -1
  6895. and eax, edi
  6896. {Round up to the nearest block size granularity}
  6897. lea eax, [eax + edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
  6898. and eax, -MediumBlockGranularity
  6899. add eax, MediumBlockSizeOffset
  6900. {Calculate the size of the second split}
  6901. lea edx, [ebp + BlockHeaderSize]
  6902. sub edx, eax
  6903. {Does it fit?}
  6904. ja @MediumInPlaceUpsizeSplit
  6905. {Grab the whole block: Mark it as used in the block following it}
  6906. and dword ptr [esi + ebp], not PreviousMediumBlockIsFreeFlag
  6907. {The block size is the full available size plus header}
  6908. add ebp, 4
  6909. {Upsize done}
  6910. jmp @MediumUpsizeInPlaceDone
  6911. {Align branch target}
  6912. {$ifndef AssumeMultiThreaded}
  6913. nop
  6914. nop
  6915. nop
  6916. {$endif}
  6917. @MediumInPlaceUpsizeSplit:
  6918. {Store the size of the second split as the second last dword}
  6919. mov [esi + ebp - 4], edx
  6920. {Set the second split header}
  6921. lea edi, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  6922. mov [esi + eax - 4], edi
  6923. mov ebp, eax
  6924. cmp edx, MinimumMediumBlockSize
  6925. jb @MediumUpsizeInPlaceDone
  6926. add eax, esi
  6927. call InsertMediumBlockIntoBin
  6928. @MediumUpsizeInPlaceDone:
  6929. {Set the size and flags for this block}
  6930. or ebp, ebx
  6931. mov [esi - 4], ebp
  6932. {Unlock the medium blocks}
  6933. mov MediumBlocksLocked, False
  6934. {Result = old pointer}
  6935. mov eax, esi
  6936. @MediumBlockResizeDone2:
  6937. {Restore registers}
  6938. pop ebp
  6939. pop edi
  6940. pop esi
  6941. pop ebx
  6942. {Return}
  6943. ret
  6944. {Align branch target for "@CannotUpsizeMediumBlockInPlace"}
  6945. nop
  6946. nop
  6947. @NextMediumBlockChanged:
  6948. {The next medium block changed while the medium blocks were being locked}
  6949. mov MediumBlocksLocked, False
  6950. @CannotUpsizeMediumBlockInPlace:
  6951. {Couldn't upsize in place. Grab a new block and move the data across:
  6952. If we have to reallocate and move medium blocks, we grow by at
  6953. least 25%}
  6954. mov eax, ecx
  6955. shr eax, 2
  6956. add eax, ecx
  6957. {Get the maximum of the requested size and the minimum growth size}
  6958. xor edi, edi
  6959. sub eax, edx
  6960. adc edi, -1
  6961. and eax, edi
  6962. add eax, edx
  6963. {Save the size to allocate}
  6964. mov ebp, eax
  6965. {Save the size to move across}
  6966. mov edi, ecx
  6967. {Get the block}
  6968. push edx
  6969. call FastGetMem
  6970. pop edx
  6971. {Success?}
  6972. test eax, eax
  6973. jz @MediumBlockResizeDone2
  6974. {If it's a Large block - store the actual user requested size}
  6975. cmp ebp, MaximumMediumBlockSize - BlockHeaderSize
  6976. jbe @MediumUpsizeNotLarge
  6977. mov [eax - 8], edx
  6978. @MediumUpsizeNotLarge:
  6979. {Save the result}
  6980. mov ebp, eax
  6981. {Move the data across}
  6982. mov edx, eax
  6983. mov eax, esi
  6984. mov ecx, edi
  6985. {$ifdef UseCustomVariableSizeMoveRoutines}
  6986. call MoveX16LP
  6987. {$else}
  6988. call System.Move
  6989. {$endif}
  6990. {Free the old block}
  6991. mov eax, esi
  6992. call FastFreeMem
  6993. {Restore the result}
  6994. mov eax, ebp
  6995. {Restore registers}
  6996. pop ebp
  6997. pop edi
  6998. pop esi
  6999. pop ebx
  7000. {Return}
  7001. ret
  7002. {Align branch target}
  7003. nop
  7004. @PossibleLargeBlock:
  7005. {-----------------------Large block------------------------------}
  7006. {Restore registers}
  7007. pop esi
  7008. pop ebx
  7009. {Is this a valid large block?}
  7010. test cl, IsFreeBlockFlag + IsMediumBlockFlag
  7011. jz ReallocateLargeBlock
  7012. {-----------------------Invalid block------------------------------}
  7013. xor eax, eax
  7014. end;
  7015. {$else}
  7016. {-----------------64-bit BASM FastReallocMem-----------------}
  7017. asm
  7018. .params 3
  7019. .pushnv rbx
  7020. .pushnv rsi
  7021. .pushnv rdi
  7022. .pushnv r14
  7023. .pushnv r15
  7024. {On entry: rcx = APointer; rdx = ANewSize}
  7025. {Save the original pointer in rsi}
  7026. mov rsi, rcx
  7027. {Get the block header}
  7028. mov rcx, [rcx - BlockHeaderSize]
  7029. {Is it a small block?}
  7030. test cl, IsFreeBlockFlag + IsMediumBlockFlag + IsLargeBlockFlag
  7031. jnz @NotASmallBlock
  7032. {-----------------------------------Small block-------------------------------------}
  7033. {Get the block type in rbx}
  7034. mov rbx, TSmallBlockPoolHeader[rcx].BlockType
  7035. {Get the available size inside blocks of this type.}
  7036. movzx ecx, TSmallBlockType[rbx].BlockSize
  7037. sub ecx, BlockHeaderSize
  7038. {Is it an upsize or a downsize?}
  7039. cmp rcx, rdx
  7040. jb @SmallUpsize
  7041. {It's a downsize. Do we need to allocate a smaller block? Only if the new
  7042. size is less than a quarter of the available size less
  7043. SmallBlockDownsizeCheckAdder bytes}
  7044. lea ebx, [edx * 4 + SmallBlockDownsizeCheckAdder]
  7045. cmp ebx, ecx
  7046. jb @NotSmallInPlaceDownsize
  7047. {In-place downsize - return the original pointer}
  7048. mov rax, rsi
  7049. jmp @Done
  7050. @NotSmallInPlaceDownsize:
  7051. {Save the requested size}
  7052. mov rbx, rdx
  7053. {Allocate a smaller block}
  7054. mov rcx, rdx
  7055. call FastGetMem
  7056. {Allocated OK?}
  7057. test rax, rax
  7058. jz @Done
  7059. {Move data across: count in r8}
  7060. mov r8, rbx
  7061. {Destination in edx}
  7062. mov rdx, rax
  7063. {Save the result in ebx}
  7064. mov rbx, rax
  7065. {Original pointer in ecx}
  7066. mov rcx, rsi
  7067. {Move the data across}
  7068. {$ifdef UseCustomVariableSizeMoveRoutines}
  7069. {$ifdef Align16Bytes}
  7070. call MoveX16LP
  7071. {$else}
  7072. call MoveX8LP
  7073. {$endif}
  7074. {$else}
  7075. call System.Move
  7076. {$endif}
  7077. {Free the original pointer}
  7078. mov rcx, rsi
  7079. call FastFreeMem
  7080. {Return the pointer}
  7081. mov rax, rbx
  7082. jmp @Done
  7083. @SmallUpsize:
  7084. {State: rsi = APointer, rdx = ANewSize, rcx = Current Block Size, rbx = Current Block Type}
  7085. {This pointer is being reallocated to a larger block and therefore it is
  7086. logical to assume that it may be enlarged again. Since reallocations are
  7087. expensive, there is a minimum upsize percentage to avoid unnecessary
  7088. future move operations.}
  7089. {Small blocks always grow with at least 100% + SmallBlockUpsizeAdder bytes}
  7090. lea ecx, [ecx + ecx + SmallBlockUpsizeAdder]
  7091. {Save the requested size in rdi}
  7092. mov rdi, rdx
  7093. {New allocated size is the maximum of the requested size and the minimum
  7094. upsize}
  7095. xor rax, rax
  7096. sub rcx, rdx
  7097. adc rax, -1
  7098. and rcx, rax
  7099. add rcx, rdx
  7100. {Allocate the new block}
  7101. call FastGetMem
  7102. {Allocated OK?}
  7103. test rax, rax
  7104. jz @Done
  7105. {Do we need to store the requested size? Only large blocks store the
  7106. requested size.}
  7107. cmp rdi, MaximumMediumBlockSize - BlockHeaderSize
  7108. jbe @NotSmallUpsizeToLargeBlock
  7109. {Store the user requested size}
  7110. mov [rax - 2 * BlockHeaderSize], rdi
  7111. @NotSmallUpsizeToLargeBlock:
  7112. {Get the size to move across}
  7113. movzx r8d, TSmallBlockType[rbx].BlockSize
  7114. sub r8d, BlockHeaderSize
  7115. {Move to the new block}
  7116. mov rdx, rax
  7117. {Save the result in edi}
  7118. mov rdi, rax
  7119. {Move from the old block}
  7120. mov rcx, rsi
  7121. {Move the data across}
  7122. {$ifdef UseCustomFixedSizeMoveRoutines}
  7123. call TSmallBlockType[rbx].UpsizeMoveProcedure
  7124. {$else}
  7125. call System.Move
  7126. {$endif}
  7127. {Free the old pointer}
  7128. mov rcx, rsi
  7129. call FastFreeMem
  7130. {Done}
  7131. mov rax, rdi
  7132. jmp @Done
  7133. @NotASmallBlock:
  7134. {Is this a medium block or a large block?}
  7135. test cl, IsFreeBlockFlag + IsLargeBlockFlag
  7136. jnz @PossibleLargeBlock
  7137. {-------------------------------Medium block--------------------------------------}
  7138. {Status: rcx = Current Block Size + Flags, rsi = APointer,
  7139. rdx = Requested Size}
  7140. mov rbx, rcx
  7141. {Drop the flags from the header}
  7142. and ecx, DropMediumAndLargeFlagsMask
  7143. {Get a pointer to the next block in rdi}
  7144. lea rdi, [rsi + rcx]
  7145. {Subtract the block header size from the old available size}
  7146. sub ecx, BlockHeaderSize
  7147. {Get the complete flags in ebx}
  7148. and ebx, ExtractMediumAndLargeFlagsMask
  7149. {Is it an upsize or a downsize?}
  7150. cmp rdx, rcx
  7151. ja @MediumBlockUpsize
  7152. {Status: ecx = Current Block Size - BlockHeaderSize, bl = Current Block Flags,
  7153. rdi = @Next Block, rsi = APointer, rdx = Requested Size}
  7154. {Must be less than half the current size or we don't bother resizing.}
  7155. lea r15, [rdx + rdx]
  7156. cmp r15, rcx
  7157. jb @MediumMustDownsize
  7158. @MediumNoResize:
  7159. mov rax, rsi
  7160. jmp @Done
  7161. @MediumMustDownsize:
  7162. {In-place downsize? Balance the cost of moving the data vs. the cost of
  7163. fragmenting the memory pool. Medium blocks in use may never be smaller
  7164. than MinimumMediumBlockSize.}
  7165. cmp edx, MinimumMediumBlockSize - BlockHeaderSize
  7166. jae @MediumBlockInPlaceDownsize
  7167. {The requested size is less than the minimum medium block size. If the
  7168. requested size is less than the threshold value (currently a quarter of the
  7169. minimum medium block size), move the data to a small block, otherwise shrink
  7170. the medium block to the minimum allowable medium block size.}
  7171. cmp edx, MediumInPlaceDownsizeLimit
  7172. jb @MediumDownsizeRealloc
  7173. {The request is for a size smaller than the minimum medium block size, but
  7174. not small enough to justify moving data: Reduce the block size to the
  7175. minimum medium block size}
  7176. mov edx, MinimumMediumBlockSize - BlockHeaderSize
  7177. {Is it already at the minimum medium block size?}
  7178. cmp ecx, edx
  7179. jna @MediumNoResize
  7180. @MediumBlockInPlaceDownsize:
  7181. {Round up to the next medium block size}
  7182. lea r15, [rdx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
  7183. and r15, -MediumBlockGranularity
  7184. add r15, MediumBlockSizeOffset
  7185. {Get the size of the second split}
  7186. add ecx, BlockHeaderSize
  7187. sub ecx, r15d
  7188. {Lock the medium blocks}
  7189. {$ifndef AssumeMultiThreaded}
  7190. lea r8, IsMultiThread
  7191. cmp byte ptr [r8], False
  7192. je @DoMediumInPlaceDownsize
  7193. {$endif}
  7194. @DoMediumLockForDownsize:
  7195. {Lock the medium blocks}
  7196. mov ebx, ecx
  7197. call LockMediumBlocks
  7198. mov ecx, ebx
  7199. {Reread the flags - they may have changed before medium blocks could be
  7200. locked.}
  7201. mov rbx, ExtractMediumAndLargeFlagsMask
  7202. and rbx, [rsi - BlockHeaderSize]
  7203. @DoMediumInPlaceDownsize:
  7204. {Set the new size}
  7205. or rbx, r15
  7206. mov [rsi - BlockHeaderSize], rbx
  7207. {Get the second split size in ebx}
  7208. mov ebx, ecx
  7209. {Is the next block in use?}
  7210. mov rdx, [rdi - BlockHeaderSize]
  7211. test dl, IsFreeBlockFlag
  7212. jnz @MediumDownsizeNextBlockFree
  7213. {The next block is in use: flag its previous block as free}
  7214. or rdx, PreviousMediumBlockIsFreeFlag
  7215. mov [rdi - BlockHeaderSize], rdx
  7216. jmp @MediumDownsizeDoSplit
  7217. @MediumDownsizeNextBlockFree:
  7218. {The next block is free: combine it}
  7219. mov rcx, rdi
  7220. and rdx, DropMediumAndLargeFlagsMask
  7221. add rbx, rdx
  7222. add rdi, rdx
  7223. cmp edx, MinimumMediumBlockSize
  7224. jb @MediumDownsizeDoSplit
  7225. call RemoveMediumFreeBlock
  7226. @MediumDownsizeDoSplit:
  7227. {Store the trailing size field}
  7228. mov [rdi - 2 * BlockHeaderSize], rbx
  7229. {Store the free part's header}
  7230. lea rcx, [rbx + IsMediumBlockFlag + IsFreeBlockFlag];
  7231. mov [rsi + r15 - BlockHeaderSize], rcx
  7232. {Bin this free block}
  7233. cmp rbx, MinimumMediumBlockSize
  7234. jb @MediumBlockDownsizeDone
  7235. lea rcx, [rsi + r15]
  7236. mov rdx, rbx
  7237. call InsertMediumBlockIntoBin
  7238. @MediumBlockDownsizeDone:
  7239. {Unlock the medium blocks}
  7240. lea rax, MediumBlocksLocked
  7241. mov byte ptr [rax], False
  7242. {Result = old pointer}
  7243. mov rax, rsi
  7244. jmp @Done
  7245. @MediumDownsizeRealloc:
  7246. {Save the requested size}
  7247. mov rdi, rdx
  7248. mov rcx, rdx
  7249. {Allocate the new block}
  7250. call FastGetMem
  7251. test rax, rax
  7252. jz @Done
  7253. {Save the result}
  7254. mov r15, rax
  7255. mov rdx, rax
  7256. mov rcx, rsi
  7257. mov r8, rdi
  7258. {Move the data across}
  7259. {$ifdef UseCustomVariableSizeMoveRoutines}
  7260. {$ifdef Align16Bytes}
  7261. call MoveX16LP
  7262. {$else}
  7263. call MoveX8LP
  7264. {$endif}
  7265. {$else}
  7266. call System.Move
  7267. {$endif}
  7268. mov rcx, rsi
  7269. call FastFreeMem
  7270. {Return the result}
  7271. mov rax, r15
  7272. jmp @Done
  7273. @MediumBlockUpsize:
  7274. {Status: ecx = Current Block Size - BlockHeaderSize, bl = Current Block Flags,
  7275. rdi = @Next Block, rsi = APointer, rdx = Requested Size}
  7276. {Can we do an in-place upsize?}
  7277. mov rax, [rdi - BlockHeaderSize]
  7278. test al, IsFreeBlockFlag
  7279. jz @CannotUpsizeMediumBlockInPlace
  7280. {Get the total available size including the next block}
  7281. and rax, DropMediumAndLargeFlagsMask
  7282. {r15 = total available size including the next block (excluding the header)}
  7283. lea r15, [rax + rcx]
  7284. {Can the block fit?}
  7285. cmp rdx, r15
  7286. ja @CannotUpsizeMediumBlockInPlace
  7287. {The next block is free and there is enough space to grow this
  7288. block in place.}
  7289. {$ifndef AssumeMultiThreaded}
  7290. lea r8, IsMultiThread
  7291. cmp byte ptr [r8], False
  7292. je @DoMediumInPlaceUpsize
  7293. {$endif}
  7294. @DoMediumLockForUpsize:
  7295. {Lock the medium blocks.}
  7296. mov rbx, rcx
  7297. mov r15, rdx
  7298. call LockMediumBlocks
  7299. mov rcx, rbx
  7300. mov rdx, r15
  7301. {Re-read the info for this block (since it may have changed before the medium
  7302. blocks could be locked)}
  7303. mov rbx, ExtractMediumAndLargeFlagsMask
  7304. and rbx, [rsi - BlockHeaderSize]
  7305. {Re-read the info for the next block}
  7306. mov rax, [rdi - BlockheaderSize]
  7307. {Next block still free?}
  7308. test al, IsFreeBlockFlag
  7309. jz @NextMediumBlockChanged
  7310. {Recalculate the next block size}
  7311. and eax, DropMediumAndLargeFlagsMask
  7312. {The available size including the next block}
  7313. lea r15, [rax + rcx]
  7314. {Can the block still fit?}
  7315. cmp rdx, r15
  7316. ja @NextMediumBlockChanged
  7317. @DoMediumInPlaceUpsize:
  7318. {Is the next block binnable?}
  7319. cmp eax, MinimumMediumBlockSize
  7320. {Remove the next block}
  7321. jb @MediumInPlaceNoNextRemove
  7322. mov r14, rcx
  7323. mov rcx, rdi
  7324. mov rdi, rdx
  7325. call RemoveMediumFreeBlock
  7326. mov rcx, r14
  7327. mov rdx, rdi
  7328. @MediumInPlaceNoNextRemove:
  7329. {Medium blocks grow a minimum of 25% in in-place upsizes}
  7330. mov eax, ecx
  7331. shr eax, 2
  7332. add eax, ecx
  7333. {Get the maximum of the requested size and the minimum growth size}
  7334. xor edi, edi
  7335. sub eax, edx
  7336. adc edi, -1
  7337. and eax, edi
  7338. {Round up to the nearest block size granularity}
  7339. lea eax, [eax + edx + BlockHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset]
  7340. and eax, -MediumBlockGranularity
  7341. add eax, MediumBlockSizeOffset
  7342. {Calculate the size of the second split}
  7343. lea rdx, [r15 + BlockHeaderSize]
  7344. sub edx, eax
  7345. {Does it fit?}
  7346. ja @MediumInPlaceUpsizeSplit
  7347. {Grab the whole block: Mark it as used in the block following it}
  7348. and qword ptr [rsi + r15], not PreviousMediumBlockIsFreeFlag
  7349. {The block size is the full available size plus header}
  7350. add r15, BlockHeaderSize
  7351. {Upsize done}
  7352. jmp @MediumUpsizeInPlaceDone
  7353. @MediumInPlaceUpsizeSplit:
  7354. {Store the size of the second split as the second last dword}
  7355. mov [rsi + r15 - BlockHeaderSize], rdx
  7356. {Set the second split header}
  7357. lea edi, [edx + IsMediumBlockFlag + IsFreeBlockFlag]
  7358. mov [rsi + rax - BlockHeaderSize], rdi
  7359. mov r15, rax
  7360. cmp edx, MinimumMediumBlockSize
  7361. jb @MediumUpsizeInPlaceDone
  7362. lea rcx, [rsi + rax]
  7363. call InsertMediumBlockIntoBin
  7364. @MediumUpsizeInPlaceDone:
  7365. {Set the size and flags for this block}
  7366. or r15, rbx
  7367. mov [rsi - BlockHeaderSize], r15
  7368. {Unlock the medium blocks}
  7369. lea rax, MediumBlocksLocked
  7370. mov byte ptr [rax], False
  7371. {Result = old pointer}
  7372. mov rax, rsi
  7373. jmp @Done
  7374. @NextMediumBlockChanged:
  7375. {The next medium block changed while the medium blocks were being locked}
  7376. lea rax, MediumBlocksLocked
  7377. mov byte ptr [rax], False
  7378. @CannotUpsizeMediumBlockInPlace:
  7379. {Couldn't upsize in place. Grab a new block and move the data across:
  7380. If we have to reallocate and move medium blocks, we grow by at
  7381. least 25%}
  7382. mov eax, ecx
  7383. shr eax, 2
  7384. add eax, ecx
  7385. {Get the maximum of the requested size and the minimum growth size}
  7386. xor rdi, rdi
  7387. sub rax, rdx
  7388. adc rdi, -1
  7389. and rax, rdi
  7390. add rax, rdx
  7391. {Save the size to allocate}
  7392. mov r15, rax
  7393. {Save the size to move across}
  7394. mov edi, ecx
  7395. {Save the requested size}
  7396. mov rbx, rdx
  7397. {Get the block}
  7398. mov rcx, rax
  7399. call FastGetMem
  7400. mov rdx, rbx
  7401. {Success?}
  7402. test eax, eax
  7403. jz @Done
  7404. {If it's a Large block - store the actual user requested size}
  7405. cmp r15, MaximumMediumBlockSize - BlockHeaderSize
  7406. jbe @MediumUpsizeNotLarge
  7407. mov [rax - 2 * BlockHeaderSize], rdx
  7408. @MediumUpsizeNotLarge:
  7409. {Save the result}
  7410. mov r15, rax
  7411. {Move the data across}
  7412. mov rdx, rax
  7413. mov rcx, rsi
  7414. mov r8, rdi
  7415. {$ifdef UseCustomVariableSizeMoveRoutines}
  7416. call MoveX16LP
  7417. {$else}
  7418. call System.Move
  7419. {$endif}
  7420. {Free the old block}
  7421. mov rcx, rsi
  7422. call FastFreeMem
  7423. {Restore the result}
  7424. mov rax, r15
  7425. jmp @Done
  7426. @PossibleLargeBlock:
  7427. {-----------------------Large block------------------------------}
  7428. {Is this a valid large block?}
  7429. test cl, IsFreeBlockFlag + IsMediumBlockFlag
  7430. jnz @Error
  7431. mov rcx, rsi
  7432. call ReallocateLargeBlock
  7433. jmp @Done
  7434. {-----------------------Invalid block------------------------------}
  7435. @Error:
  7436. xor eax, eax
  7437. @Done:
  7438. end;
  7439. {$endif}
  7440. {$endif}
  7441. {$endif}
  7442. {Allocates a block and fills it with zeroes}
  7443. function FastAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  7444. {$ifndef ASMVersion}
  7445. begin
  7446. Result := FastGetMem(ASize);
  7447. {Large blocks are already zero filled}
  7448. if (Result <> nil) and (ASize <= (MaximumMediumBlockSize - BlockHeaderSize)) then
  7449. FillChar(Result^, ASize, 0);
  7450. end;
  7451. {$else}
  7452. {$ifdef 32Bit}
  7453. asm
  7454. push ebx
  7455. {Get the size rounded down to the previous multiple of 4 into ebx}
  7456. lea ebx, [eax - 1]
  7457. and ebx, -4
  7458. {Get the block}
  7459. call FastGetMem
  7460. {Could a block be allocated? ecx = 0 if yes, $ffffffff if no}
  7461. cmp eax, 1
  7462. sbb ecx, ecx
  7463. {Point edx to the last dword}
  7464. lea edx, [eax + ebx]
  7465. {ebx = $ffffffff if no block could be allocated, otherwise size rounded down
  7466. to previous multiple of 4. If ebx = 0 then the block size is 1..4 bytes and
  7467. the FPU based clearing loop should not be used (since it clears 8 bytes per
  7468. iteration).}
  7469. or ebx, ecx
  7470. jz @ClearLastDWord
  7471. {Large blocks are already zero filled}
  7472. cmp ebx, MaximumMediumBlockSize - BlockHeaderSize
  7473. jae @Done
  7474. {Make the counter negative based}
  7475. neg ebx
  7476. {Load zero into st(0)}
  7477. fldz
  7478. {Clear groups of 8 bytes. Block sizes are always four less than a multiple
  7479. of 8.}
  7480. @FillLoop:
  7481. fst qword ptr [edx + ebx]
  7482. add ebx, 8
  7483. js @FillLoop
  7484. {Clear st(0)}
  7485. ffree st(0)
  7486. {Correct the stack top}
  7487. fincstp
  7488. {Clear the last four bytes}
  7489. @ClearLastDWord:
  7490. mov [edx], ecx
  7491. @Done:
  7492. pop ebx
  7493. end;
  7494. {$else}
  7495. {---------------64-bit BASM FastAllocMem---------------}
  7496. asm
  7497. .params 1
  7498. .pushnv rbx
  7499. {Get the size rounded down to the previous multiple of SizeOf(Pointer) into
  7500. ebx}
  7501. lea rbx, [rcx - 1]
  7502. and rbx, -8
  7503. {Get the block}
  7504. call FastGetMem
  7505. {Could a block be allocated? rcx = 0 if yes, -1 if no}
  7506. cmp rax, 1
  7507. sbb rcx, rcx
  7508. {Point rdx to the last dword}
  7509. lea rdx, [rax + rbx]
  7510. {rbx = -1 if no block could be allocated, otherwise size rounded down
  7511. to previous multiple of 8. If rbx = 0 then the block size is 1..8 bytes and
  7512. the SSE2 based clearing loop should not be used (since it clears 16 bytes per
  7513. iteration).}
  7514. or rbx, rcx
  7515. jz @ClearLastQWord
  7516. {Large blocks are already zero filled}
  7517. cmp rbx, MaximumMediumBlockSize - BlockHeaderSize
  7518. jae @Done
  7519. {Make the counter negative based}
  7520. neg rbx
  7521. {Load zero into xmm0}
  7522. pxor xmm0, xmm0
  7523. {Clear groups of 16 bytes. Block sizes are always 8 less than a multiple of
  7524. 16.}
  7525. @FillLoop:
  7526. movdqa [rdx + rbx], xmm0
  7527. add rbx, 16
  7528. js @FillLoop
  7529. {Clear the last 8 bytes}
  7530. @ClearLastQWord:
  7531. xor rcx, rcx
  7532. mov [rdx], rcx
  7533. @Done:
  7534. end;
  7535. {$endif}
  7536. {$endif}
  7537. {-----------------Post Uninstall GetMem/FreeMem/ReallocMem-------------------}
  7538. {$ifdef DetectMMOperationsAfterUninstall}
  7539. function InvalidGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  7540. {$ifndef NoMessageBoxes}
  7541. var
  7542. LErrorMessageTitle: array[0..1023] of AnsiChar;
  7543. {$endif}
  7544. begin
  7545. {$ifdef UseOutputDebugString}
  7546. OutputDebugStringA(InvalidGetMemMsg);
  7547. {$endif}
  7548. {$ifndef NoMessageBoxes}
  7549. AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
  7550. ShowMessageBox(InvalidGetMemMsg, LErrorMessageTitle);
  7551. {$endif}
  7552. Result := nil;
  7553. end;
  7554. function InvalidFreeMem(APointer: Pointer): Integer;
  7555. {$ifndef NoMessageBoxes}
  7556. var
  7557. LErrorMessageTitle: array[0..1023] of AnsiChar;
  7558. {$endif}
  7559. begin
  7560. {$ifdef UseOutputDebugString}
  7561. OutputDebugStringA(InvalidFreeMemMsg);
  7562. {$endif}
  7563. {$ifndef NoMessageBoxes}
  7564. AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
  7565. ShowMessageBox(InvalidFreeMemMsg, LErrorMessageTitle);
  7566. {$endif}
  7567. Result := -1;
  7568. end;
  7569. function InvalidReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  7570. {$ifndef NoMessageBoxes}
  7571. var
  7572. LErrorMessageTitle: array[0..1023] of AnsiChar;
  7573. {$endif}
  7574. begin
  7575. {$ifdef UseOutputDebugString}
  7576. OutputDebugStringA(InvalidReallocMemMsg);
  7577. {$endif}
  7578. {$ifndef NoMessageBoxes}
  7579. AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
  7580. ShowMessageBox(InvalidReallocMemMsg, LErrorMessageTitle);
  7581. {$endif}
  7582. Result := nil;
  7583. end;
  7584. function InvalidAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  7585. {$ifndef NoMessageBoxes}
  7586. var
  7587. LErrorMessageTitle: array[0..1023] of AnsiChar;
  7588. {$endif}
  7589. begin
  7590. {$ifdef UseOutputDebugString}
  7591. OutputDebugStringA(InvalidAllocMemMsg);
  7592. {$endif}
  7593. {$ifndef NoMessageBoxes}
  7594. AppendStringToModuleName(InvalidOperationTitle, LErrorMessageTitle);
  7595. ShowMessageBox(InvalidAllocMemMsg, LErrorMessageTitle);
  7596. {$endif}
  7597. Result := nil;
  7598. end;
  7599. function InvalidRegisterAndUnRegisterMemoryLeak(APointer: Pointer): Boolean;
  7600. begin
  7601. Result := False;
  7602. end;
  7603. {$endif}
  7604. {-----------------Full Debug Mode Memory Manager Interface--------------------}
  7605. {$ifdef FullDebugMode}
  7606. {Compare [AAddress], CompareVal:
  7607. If Equal: [AAddress] := NewVal and result = CompareVal
  7608. If Unequal: Result := [AAddress]}
  7609. function LockCmpxchg32(CompareVal, NewVal: Integer; AAddress: PInteger): Integer;
  7610. asm
  7611. {$ifdef 32Bit}
  7612. {On entry:
  7613. eax = CompareVal,
  7614. edx = NewVal,
  7615. ecx = AAddress}
  7616. lock cmpxchg [ecx], edx
  7617. {$else}
  7618. .noframe
  7619. {On entry:
  7620. ecx = CompareVal,
  7621. edx = NewVal,
  7622. r8 = AAddress}
  7623. mov eax, ecx
  7624. lock cmpxchg [r8], edx
  7625. {$endif}
  7626. end;
  7627. {Called by DebugGetMem, DebugFreeMem and DebugReallocMem in order to block a
  7628. free block scan operation while the memory pool is being modified.}
  7629. procedure StartChangingFullDebugModeBlock;
  7630. var
  7631. LOldCount: Integer;
  7632. begin
  7633. while True do
  7634. begin
  7635. {Get the old thread count}
  7636. LOldCount := ThreadsInFullDebugModeRoutine;
  7637. if (LOldCount >= 0)
  7638. and (LockCmpxchg32(LOldCount, LOldCount + 1, @ThreadsInFullDebugModeRoutine) = LOldCount) then
  7639. begin
  7640. Break;
  7641. end;
  7642. {$ifdef NeverSleepOnThreadContention}
  7643. {$ifdef UseSwitchToThread}
  7644. SwitchToThread;
  7645. {$endif}
  7646. {$else}
  7647. Sleep(InitialSleepTime);
  7648. {Try again}
  7649. LOldCount := ThreadsInFullDebugModeRoutine;
  7650. if (LOldCount >= 0)
  7651. and (LockCmpxchg32(LOldCount, LOldCount + 1, @ThreadsInFullDebugModeRoutine) = LOldCount) then
  7652. begin
  7653. Break;
  7654. end;
  7655. Sleep(AdditionalSleepTime);
  7656. {$endif}
  7657. end;
  7658. end;
  7659. procedure DoneChangingFullDebugModeBlock;
  7660. asm
  7661. {$ifdef 32Bit}
  7662. lock dec ThreadsInFullDebugModeRoutine
  7663. {$else}
  7664. .noframe
  7665. lea rax, ThreadsInFullDebugModeRoutine
  7666. lock dec dword ptr [rax]
  7667. {$endif}
  7668. end;
  7669. {Increments the allocation number}
  7670. procedure IncrementAllocationNumber;
  7671. asm
  7672. {$ifdef 32Bit}
  7673. lock inc CurrentAllocationNumber
  7674. {$else}
  7675. .noframe
  7676. lea rax, CurrentAllocationNumber
  7677. lock inc dword ptr [rax]
  7678. {$endif}
  7679. end;
  7680. {Called by a routine wanting to lock the entire memory pool in FullDebugMode, e.g. before scanning the memory
  7681. pool for corruptions.}
  7682. procedure BlockFullDebugModeMMRoutines;
  7683. begin
  7684. while True do
  7685. begin
  7686. {Get the old thread count}
  7687. if LockCmpxchg32(0, -1, @ThreadsInFullDebugModeRoutine) = 0 then
  7688. Break;
  7689. {$ifdef NeverSleepOnThreadContention}
  7690. {$ifdef UseSwitchToThread}
  7691. SwitchToThread;
  7692. {$endif}
  7693. {$else}
  7694. Sleep(InitialSleepTime);
  7695. {Try again}
  7696. if LockCmpxchg32(0, -1, @ThreadsInFullDebugModeRoutine) = 0 then
  7697. Break;
  7698. Sleep(AdditionalSleepTime);
  7699. {$endif}
  7700. end;
  7701. end;
  7702. procedure UnblockFullDebugModeMMRoutines;
  7703. begin
  7704. {Currently blocked? If so, unblock the FullDebugMode routines.}
  7705. if ThreadsInFullDebugModeRoutine = -1 then
  7706. ThreadsInFullDebugModeRoutine := 0;
  7707. end;
  7708. procedure DeleteEventLog;
  7709. begin
  7710. {Delete the file}
  7711. DeleteFileA(MMLogFileName);
  7712. end;
  7713. {Finds the start and length of the file name given a full path.}
  7714. procedure ExtractFileName(APFullPath: PAnsiChar; var APFileNameStart: PAnsiChar; var AFileNameLength: Integer);
  7715. var
  7716. LChar: AnsiChar;
  7717. begin
  7718. {Initialize}
  7719. APFileNameStart := APFullPath;
  7720. AFileNameLength := 0;
  7721. {Find the file }
  7722. while True do
  7723. begin
  7724. {Get the next character}
  7725. LChar := APFullPath^;
  7726. {End of the path string?}
  7727. if LChar = #0 then
  7728. Break;
  7729. {Advance the buffer position}
  7730. Inc(APFullPath);
  7731. {Found a backslash? -> May be the start of the file name}
  7732. if LChar = '\' then
  7733. APFileNameStart := APFullPath;
  7734. end;
  7735. {Calculate the length of the file name}
  7736. AFileNameLength := IntPtr(APFullPath) - IntPtr(APFileNameStart);
  7737. end;
  7738. procedure AppendEventLog(ABuffer: Pointer; ACount: Cardinal);
  7739. const
  7740. {Declared here, because it is not declared in the SHFolder.pas unit of some older Delphi versions.}
  7741. SHGFP_TYPE_CURRENT = 0;
  7742. var
  7743. LFileHandle, LBytesWritten: Cardinal;
  7744. LEventHeader: array[0..1023] of AnsiChar;
  7745. LAlternateLogFileName: array[0..2047] of AnsiChar;
  7746. LPathLen, LNameLength: Integer;
  7747. LMsgPtr, LPFileName: PAnsiChar;
  7748. LSystemTime: TSystemTime;
  7749. begin
  7750. {Try to open the log file in read/write mode.}
  7751. LFileHandle := CreateFileA(MMLogFileName, GENERIC_READ or GENERIC_WRITE,
  7752. 0, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  7753. {Did log file creation fail? If so, the destination folder is perhaps read-only:
  7754. Try to redirect logging to a file in the user's "My Documents" folder.}
  7755. if (LFileHandle = INVALID_HANDLE_VALUE)
  7756. {$ifdef Delphi4or5}
  7757. and SHGetSpecialFolderPathA(0, @LAlternateLogFileName, CSIDL_PERSONAL, True) then
  7758. {$else}
  7759. and (SHGetFolderPathA(0, CSIDL_PERSONAL or CSIDL_FLAG_CREATE, 0,
  7760. SHGFP_TYPE_CURRENT, @LAlternateLogFileName) = S_OK) then
  7761. {$endif}
  7762. begin
  7763. {Extract the filename part from MMLogFileName and append it to the path of
  7764. the "My Documents" folder.}
  7765. LPathLen := StrLen(LAlternateLogFileName);
  7766. {Ensure that there is a trailing backslash in the path}
  7767. if (LPathLen = 0) or (LAlternateLogFileName[LPathLen - 1] <> '\') then
  7768. begin
  7769. LAlternateLogFileName[LPathLen] := '\';
  7770. Inc(LPathLen);
  7771. end;
  7772. {Add the filename to the path}
  7773. ExtractFileName(@MMLogFileName, LPFileName, LNameLength);
  7774. System.Move(LPFileName^, LAlternateLogFileName[LPathLen], LNameLength + 1);
  7775. {Try to open the alternate log file}
  7776. LFileHandle := CreateFileA(LAlternateLogFileName, GENERIC_READ or GENERIC_WRITE,
  7777. 0, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  7778. end;
  7779. {Was the log file opened/created successfully?}
  7780. if LFileHandle <> INVALID_HANDLE_VALUE then
  7781. begin
  7782. {Seek to the end of the file}
  7783. SetFilePointer(LFileHandle, 0, nil, FILE_END);
  7784. {Set the separator}
  7785. LMsgPtr := AppendStringToBuffer(CRLF, @LEventHeader[0], Length(CRLF));
  7786. LMsgPtr := AppendStringToBuffer(EventSeparator, LMsgPtr, Length(EventSeparator));
  7787. {Set the date & time}
  7788. GetLocalTime(LSystemTime);
  7789. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wYear, LMsgPtr);
  7790. LMsgPtr^ := '/';
  7791. Inc(LMsgPtr);
  7792. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wMonth, LMsgPtr);
  7793. LMsgPtr^ := '/';
  7794. Inc(LMsgPtr);
  7795. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wDay, LMsgPtr);
  7796. LMsgPtr^ := ' ';
  7797. Inc(LMsgPtr);
  7798. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wHour, LMsgPtr);
  7799. LMsgPtr^ := ':';
  7800. Inc(LMsgPtr);
  7801. if LSystemTime.wMinute < 10 then
  7802. begin
  7803. LMsgPtr^ := '0';
  7804. Inc(LMsgPtr);
  7805. end;
  7806. LMsgPtr := NativeUIntToStrBuf(LSystemTime.wMinute, LMsgPtr);
  7807. LMsgPtr^ := ':';
  7808. Inc(LMsgPtr);
  7809. if LSystemTime.wSecond < 10 then
  7810. begin
  7811. LMsgPtr^ := '0';
  7812. Inc(LMsgPtr);
  7813. end;
  7814. LMsgPtr := NativeUIntToStrBuf(LSystemTime.WSecond, LMsgPtr);
  7815. {Write the header}
  7816. LMsgPtr := AppendStringToBuffer(EventSeparator, LMsgPtr, Length(EventSeparator));
  7817. LMsgPtr := AppendStringToBuffer(CRLF, LMsgPtr, Length(CRLF));
  7818. WriteFile(LFileHandle, LEventHeader[0], NativeUInt(LMsgPtr) - NativeUInt(@LEventHeader[0]), LBytesWritten, nil);
  7819. {Write the data}
  7820. WriteFile(LFileHandle, ABuffer^, ACount, LBytesWritten, nil);
  7821. {Close the file}
  7822. CloseHandle(LFileHandle);
  7823. end;
  7824. end;
  7825. {Sets the default log filename}
  7826. procedure SetDefaultMMLogFileName;
  7827. const
  7828. LogFileExtAnsi: PAnsiChar = LogFileExtension;
  7829. var
  7830. LEnvVarLength, LModuleNameLength: Cardinal;
  7831. LPathOverride: array[0..2047] of AnsiChar;
  7832. LPFileName: PAnsiChar;
  7833. LFileNameLength: Integer;
  7834. begin
  7835. {Get the name of the application}
  7836. LModuleNameLength := AppendModuleFileName(@MMLogFileName[0]);
  7837. {Replace the last few characters of the module name, and optionally override
  7838. the path.}
  7839. if LModuleNameLength > 0 then
  7840. begin
  7841. {Change the filename}
  7842. System.Move(LogFileExtAnsi^, MMLogFileName[LModuleNameLength - 4],
  7843. StrLen(LogFileExtAnsi) + 1);
  7844. {Try to read the FastMMLogFilePath environment variable}
  7845. LEnvVarLength := GetEnvironmentVariableA('FastMMLogFilePath',
  7846. @LPathOverride, 1023);
  7847. {Does the environment variable exist? If so, override the log file path.}
  7848. if LEnvVarLength > 0 then
  7849. begin
  7850. {Ensure that there's a trailing backslash.}
  7851. if LPathOverride[LEnvVarLength - 1] <> '\' then
  7852. begin
  7853. LPathOverride[LEnvVarLength] := '\';
  7854. Inc(LEnvVarLength);
  7855. end;
  7856. {Add the filename to the path override}
  7857. ExtractFileName(@MMLogFileName[0], LPFileName, LFileNameLength);
  7858. System.Move(LPFileName^, LPathOverride[LEnvVarLength], LFileNameLength + 1);
  7859. {Copy the override path back to the filename buffer}
  7860. System.Move(LPathOverride, MMLogFileName, SizeOf(MMLogFileName) - 1);
  7861. end;
  7862. end;
  7863. end;
  7864. {Specify the full path and name for the filename to be used for logging memory
  7865. errors, etc. If ALogFileName is nil or points to an empty string it will
  7866. revert to the default log file name.}
  7867. procedure SetMMLogFileName(ALogFileName: PAnsiChar = nil);
  7868. var
  7869. LLogFileNameLen: Integer;
  7870. begin
  7871. {Is ALogFileName valid?}
  7872. if (ALogFileName <> nil) and (ALogFileName^ <> #0) then
  7873. begin
  7874. LLogFileNameLen := StrLen(ALogFileName);
  7875. if LLogFileNameLen < Length(MMLogFileName) then
  7876. begin
  7877. {Set the log file name}
  7878. System.Move(ALogFileName^, MMLogFileName, LLogFileNameLen + 1);
  7879. Exit;
  7880. end;
  7881. end;
  7882. {Invalid log file name}
  7883. SetDefaultMMLogFileName;
  7884. end;
  7885. {Returns the current "allocation group". Whenever a GetMem request is serviced
  7886. in FullDebugMode, the current "allocation group" is stored in the block header.
  7887. This may help with debugging. Note that if a block is subsequently reallocated
  7888. that it keeps its original "allocation group" and "allocation number" (all
  7889. allocations are also numbered sequentially).}
  7890. function GetCurrentAllocationGroup: Cardinal;
  7891. begin
  7892. Result := AllocationGroupStack[AllocationGroupStackTop];
  7893. end;
  7894. {Allocation groups work in a stack like fashion. Group numbers are pushed onto
  7895. and popped off the stack. Note that the stack size is limited, so every push
  7896. should have a matching pop.}
  7897. procedure PushAllocationGroup(ANewCurrentAllocationGroup: Cardinal);
  7898. begin
  7899. if AllocationGroupStackTop < AllocationGroupStackSize - 1 then
  7900. begin
  7901. Inc(AllocationGroupStackTop);
  7902. AllocationGroupStack[AllocationGroupStackTop] := ANewCurrentAllocationGroup;
  7903. end
  7904. else
  7905. begin
  7906. {Raise a runtime error if the stack overflows}
  7907. {$ifdef BCB6OrDelphi7AndUp}
  7908. System.Error(reInvalidPtr);
  7909. {$else}
  7910. System.RunError(reInvalidPtr);
  7911. {$endif}
  7912. end;
  7913. end;
  7914. procedure PopAllocationGroup;
  7915. begin
  7916. if AllocationGroupStackTop > 0 then
  7917. begin
  7918. Dec(AllocationGroupStackTop);
  7919. end
  7920. else
  7921. begin
  7922. {Raise a runtime error if the stack underflows}
  7923. {$ifdef BCB6OrDelphi7AndUp}
  7924. System.Error(reInvalidPtr);
  7925. {$else}
  7926. System.RunError(reInvalidPtr);
  7927. {$endif}
  7928. end;
  7929. end;
  7930. {Sums all the dwords starting at the given address. ACount must be > 0 and a
  7931. multiple of SizeOf(Pointer).}
  7932. function SumNativeUInts(AStartValue: NativeUInt; APointer: PNativeUInt;
  7933. ACount: NativeUInt): NativeUInt;
  7934. asm
  7935. {$ifdef 32Bit}
  7936. {On entry: eax = AStartValue, edx = APointer; ecx = ACount}
  7937. add edx, ecx
  7938. neg ecx
  7939. @AddLoop:
  7940. add eax, [edx + ecx]
  7941. add ecx, 4
  7942. js @AddLoop
  7943. {$else}
  7944. {On entry: rcx = AStartValue, rdx = APointer; r8 = ACount}
  7945. add rdx, r8
  7946. neg r8
  7947. mov rax, rcx
  7948. @AddLoop:
  7949. add rax, [rdx + r8]
  7950. add r8, 8
  7951. js @AddLoop
  7952. {$endif}
  7953. end;
  7954. {Checks the memory starting at the given address for the fill pattern.
  7955. Returns True if all bytes are all valid. ACount must be >0 and a multiple of
  7956. SizeOf(Pointer).}
  7957. function CheckFillPattern(APointer: Pointer; ACount: NativeUInt;
  7958. AFillPattern: NativeUInt): Boolean;
  7959. asm
  7960. {$ifdef 32Bit}
  7961. {On entry: eax = APointer; edx = ACount; ecx = AFillPattern}
  7962. add eax, edx
  7963. neg edx
  7964. @CheckLoop:
  7965. cmp [eax + edx], ecx
  7966. jne @Done
  7967. add edx, 4
  7968. js @CheckLoop
  7969. @Done:
  7970. sete al
  7971. {$else}
  7972. {On entry: rcx = APointer; rdx = ACount; r8 = AFillPattern}
  7973. add rcx, rdx
  7974. neg rdx
  7975. @CheckLoop:
  7976. cmp [rcx + rdx], r8
  7977. jne @Done
  7978. add rdx, 8
  7979. js @CheckLoop
  7980. @Done:
  7981. sete al
  7982. {$endif}
  7983. end;
  7984. {Calculates the checksum for the debug header. Adds all dwords in the debug
  7985. header to the start address of the block.}
  7986. function CalculateHeaderCheckSum(APointer: PFullDebugBlockHeader): NativeUInt;
  7987. begin
  7988. Result := SumNativeUInts(
  7989. NativeUInt(APointer),
  7990. PNativeUInt(PByte(APointer) + 2 * SizeOf(Pointer)),
  7991. SizeOf(TFullDebugBlockHeader) - 2 * SizeOf(Pointer) - SizeOf(NativeUInt));
  7992. end;
  7993. procedure UpdateHeaderAndFooterCheckSums(APointer: PFullDebugBlockHeader);
  7994. var
  7995. LHeaderCheckSum: NativeUInt;
  7996. begin
  7997. LHeaderCheckSum := CalculateHeaderCheckSum(APointer);
  7998. APointer.HeaderCheckSum := LHeaderCheckSum;
  7999. PNativeUInt(PByte(APointer) + SizeOf(TFullDebugBlockHeader) + APointer.UserSize)^ := not LHeaderCheckSum;
  8000. end;
  8001. function LogCurrentThreadAndStackTrace(ASkipFrames: Cardinal; ABuffer: PAnsiChar): PAnsiChar;
  8002. var
  8003. LCurrentStackTrace: TStackTrace;
  8004. begin
  8005. {Get the current call stack}
  8006. GetStackTrace(@LCurrentStackTrace[0], StackTraceDepth, ASkipFrames);
  8007. {Log the thread ID}
  8008. Result := AppendStringToBuffer(CurrentThreadIDMsg, ABuffer, Length(CurrentThreadIDMsg));
  8009. Result := NativeUIntToHexBuf(GetThreadID, Result);
  8010. {List the stack trace}
  8011. Result := AppendStringToBuffer(CurrentStackTraceMsg, Result, Length(CurrentStackTraceMsg));
  8012. Result := LogStackTrace(@LCurrentStackTrace, StackTraceDepth, Result);
  8013. end;
  8014. {$ifndef DisableLoggingOfMemoryDumps}
  8015. function LogMemoryDump(APointer: PFullDebugBlockHeader; ABuffer: PAnsiChar): PAnsiChar;
  8016. var
  8017. LByteNum, LVal: Cardinal;
  8018. LDataPtr: PByte;
  8019. begin
  8020. Result := AppendStringToBuffer(MemoryDumpMsg, ABuffer, Length(MemoryDumpMsg));
  8021. Result := NativeUIntToHexBuf(NativeUInt(APointer) + SizeOf(TFullDebugBlockHeader), Result);
  8022. Result^ := ':';
  8023. Inc(Result);
  8024. {Add the bytes}
  8025. LDataPtr := PByte(PByte(APointer) + SizeOf(TFullDebugBlockHeader));
  8026. for LByteNum := 0 to 255 do
  8027. begin
  8028. if LByteNum and 31 = 0 then
  8029. begin
  8030. Result^ := #13;
  8031. Inc(Result);
  8032. Result^ := #10;
  8033. Inc(Result);
  8034. end
  8035. else
  8036. begin
  8037. Result^ := ' ';
  8038. Inc(Result);
  8039. end;
  8040. {Set the hex data}
  8041. LVal := Byte(LDataPtr^);
  8042. Result^ := HexTable[LVal shr 4];
  8043. Inc(Result);
  8044. Result^ := HexTable[LVal and $f];
  8045. Inc(Result);
  8046. {Next byte}
  8047. Inc(LDataPtr);
  8048. end;
  8049. {Dump ASCII}
  8050. LDataPtr := PByte(PByte(APointer) + SizeOf(TFullDebugBlockHeader));
  8051. for LByteNum := 0 to 255 do
  8052. begin
  8053. if LByteNum and 31 = 0 then
  8054. begin
  8055. Result^ := #13;
  8056. Inc(Result);
  8057. Result^ := #10;
  8058. Inc(Result);
  8059. end
  8060. else
  8061. begin
  8062. Result^ := ' ';
  8063. Inc(Result);
  8064. Result^ := ' ';
  8065. Inc(Result);
  8066. end;
  8067. {Set the hex data}
  8068. LVal := Byte(LDataPtr^);
  8069. if LVal < 32 then
  8070. Result^ := '.'
  8071. else
  8072. Result^ := AnsiChar(LVal);
  8073. Inc(Result);
  8074. {Next byte}
  8075. Inc(LDataPtr);
  8076. end;
  8077. end;
  8078. {$endif}
  8079. {Rotates AValue ABitCount bits to the right}
  8080. function RotateRight(AValue, ABitCount: NativeUInt): NativeUInt;
  8081. asm
  8082. {$ifdef 32Bit}
  8083. mov ecx, edx
  8084. ror eax, cl
  8085. {$else}
  8086. mov rax, rcx
  8087. mov rcx, rdx
  8088. ror rax, cl
  8089. {$endif}
  8090. end;
  8091. {Determines whether a byte in the user portion of the freed block has been modified. Does not work beyond
  8092. the end of the user portion (i.e. footer and beyond).}
  8093. function FreeBlockByteWasModified(APointer: PFullDebugBlockHeader; AUserOffset: NativeUInt): Boolean;
  8094. var
  8095. LFillPattern: NativeUInt;
  8096. begin
  8097. {Get the expected fill pattern}
  8098. if AUserOffset < SizeOf(Pointer) then
  8099. begin
  8100. LFillPattern := NativeUInt(@FreedObjectVMT.VMTMethods[0]);
  8101. end
  8102. else
  8103. begin
  8104. {$ifndef CatchUseOfFreedInterfaces}
  8105. LFillPattern := DebugFillPattern;
  8106. {$else}
  8107. LFillPattern := NativeUInt(@VMTBadInterface);
  8108. {$endif}
  8109. end;
  8110. {Compare the byte value}
  8111. Result := Byte(PByte(PByte(APointer) + SizeOf(TFullDebugBlockHeader) + AUserOffset)^) <>
  8112. Byte(RotateRight(LFillPattern, (AUserOffset and (SizeOf(Pointer) - 1)) * 8));
  8113. end;
  8114. function LogBlockChanges(APointer: PFullDebugBlockHeader; ABuffer: PAnsiChar): PAnsiChar;
  8115. var
  8116. LOffset, LChangeStart, LCount: NativeUInt;
  8117. LLogCount: Integer;
  8118. begin
  8119. {No errors logged so far}
  8120. LLogCount := 0;
  8121. {Log a maximum of 32 changes}
  8122. LOffset := 0;
  8123. while (LOffset < APointer.UserSize) and (LLogCount < 32) do
  8124. begin
  8125. {Has the byte been modified?}
  8126. if FreeBlockByteWasModified(APointer, LOffset) then
  8127. begin
  8128. {Found the start of a changed block, now find the length}
  8129. LChangeStart := LOffset;
  8130. LCount := 0;
  8131. while True do
  8132. begin
  8133. Inc(LCount);
  8134. Inc(LOffset);
  8135. if (LOffset >= APointer.UserSize)
  8136. or (not FreeBlockByteWasModified(APointer, LOffset)) then
  8137. begin
  8138. Break;
  8139. end;
  8140. end;
  8141. {Got the offset and length, now log it.}
  8142. if LLogCount = 0 then
  8143. begin
  8144. ABuffer := AppendStringToBuffer(FreeModifiedDetailMsg, ABuffer, Length(FreeModifiedDetailMsg));
  8145. end
  8146. else
  8147. begin
  8148. ABuffer^ := ',';
  8149. Inc(ABuffer);
  8150. ABuffer^ := ' ';
  8151. Inc(ABuffer);
  8152. end;
  8153. ABuffer := NativeUIntToStrBuf(LChangeStart, ABuffer);
  8154. ABuffer^ := '(';
  8155. Inc(ABuffer);
  8156. ABuffer := NativeUIntToStrBuf(LCount, ABuffer);
  8157. ABuffer^ := ')';
  8158. Inc(ABuffer);
  8159. {Increment the log count}
  8160. Inc(LLogCount);
  8161. end;
  8162. {Next byte}
  8163. Inc(LOffset);
  8164. end;
  8165. {Return the current buffer position}
  8166. Result := ABuffer;
  8167. end;
  8168. procedure LogBlockError(APointer: PFullDebugBlockHeader; AOperation: TBlockOperation; LHeaderValid, LFooterValid: Boolean);
  8169. var
  8170. LMsgPtr: PAnsiChar;
  8171. LErrorMessage: array[0..32767] of AnsiChar;
  8172. {$ifndef NoMessageBoxes}
  8173. LErrorMessageTitle: array[0..1023] of AnsiChar;
  8174. {$endif}
  8175. LClass: TClass;
  8176. {$ifdef CheckCppObjectTypeEnabled}
  8177. LCppObjectTypeName: PAnsiChar;
  8178. {$endif}
  8179. begin
  8180. {Display the error header and the operation type.}
  8181. LMsgPtr := AppendStringToBuffer(ErrorMsgHeader, @LErrorMessage[0], Length(ErrorMsgHeader));
  8182. case AOperation of
  8183. boGetMem: LMsgPtr := AppendStringToBuffer(GetMemMsg, LMsgPtr, Length(GetMemMsg));
  8184. boFreeMem: LMsgPtr := AppendStringToBuffer(FreeMemMsg, LMsgPtr, Length(FreeMemMsg));
  8185. boReallocMem: LMsgPtr := AppendStringToBuffer(ReallocMemMsg, LMsgPtr, Length(ReallocMemMsg));
  8186. boBlockCheck: LMsgPtr := AppendStringToBuffer(BlockCheckMsg, LMsgPtr, Length(BlockCheckMsg));
  8187. end;
  8188. LMsgPtr := AppendStringToBuffer(OperationMsg, LMsgPtr, Length(OperationMsg));
  8189. {Is the header still intact?}
  8190. if LHeaderValid then
  8191. begin
  8192. {Is the footer still valid?}
  8193. if LFooterValid then
  8194. begin
  8195. {A freed block has been modified, a double free has occurred, or an
  8196. attempt was made to free a memory block allocated by a different
  8197. instance of FastMM.}
  8198. if AOperation <= boGetMem then
  8199. begin
  8200. LMsgPtr := AppendStringToBuffer(FreeModifiedErrorMsg, LMsgPtr, Length(FreeModifiedErrorMsg));
  8201. {Log the exact changes that caused the error.}
  8202. LMsgPtr := LogBlockChanges(APointer, LMsgPtr);
  8203. end
  8204. else
  8205. begin
  8206. {It is either a double free, or an attempt was made to free a block
  8207. that was allocated via a different memory manager.}
  8208. if APointer.AllocatedByRoutine = nil then
  8209. LMsgPtr := AppendStringToBuffer(DoubleFreeErrorMsg, LMsgPtr, Length(DoubleFreeErrorMsg))
  8210. else
  8211. LMsgPtr := AppendStringToBuffer(WrongMMFreeErrorMsg, LMsgPtr, Length(WrongMMFreeErrorMsg));
  8212. end;
  8213. end
  8214. else
  8215. begin
  8216. LMsgPtr := AppendStringToBuffer(BlockFooterCorruptedMsg, LMsgPtr, Length(BlockFooterCorruptedMsg))
  8217. end;
  8218. {Set the block size message}
  8219. if AOperation <= boGetMem then
  8220. LMsgPtr := AppendStringToBuffer(PreviousBlockSizeMsg, LMsgPtr, Length(PreviousBlockSizeMsg))
  8221. else
  8222. LMsgPtr := AppendStringToBuffer(CurrentBlockSizeMsg, LMsgPtr, Length(CurrentBlockSizeMsg));
  8223. LMsgPtr := NativeUIntToStrBuf(APointer.UserSize, LMsgPtr);
  8224. {The header is still intact - display info about the this/previous allocation}
  8225. if APointer.AllocationStackTrace[0] <> 0 then
  8226. begin
  8227. if AOperation <= boGetMem then
  8228. LMsgPtr := AppendStringToBuffer(ThreadIDPrevAllocMsg, LMsgPtr, Length(ThreadIDPrevAllocMsg))
  8229. else
  8230. LMsgPtr := AppendStringToBuffer(ThreadIDAtAllocMsg, LMsgPtr, Length(ThreadIDAtAllocMsg));
  8231. LMsgPtr := NativeUIntToHexBuf(APointer.AllocatedByThread, LMsgPtr);
  8232. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  8233. LMsgPtr := LogStackTrace(@APointer.AllocationStackTrace, StackTraceDepth, LMsgPtr);
  8234. end;
  8235. {Get the class this block was used for previously}
  8236. LClass := DetectClassInstance(@APointer.PreviouslyUsedByClass);
  8237. if (LClass <> nil) and (IntPtr(LClass) <> IntPtr(@FreedObjectVMT.VMTMethods[0])) then
  8238. begin
  8239. LMsgPtr := AppendStringToBuffer(PreviousObjectClassMsg, LMsgPtr, Length(PreviousObjectClassMsg));
  8240. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8241. end;
  8242. {$ifdef CheckCppObjectTypeEnabled}
  8243. if (LClass = nil) and Assigned(GetCppVirtObjTypeNameByVTablePtrFunc) then
  8244. begin
  8245. LCppObjectTypeName := GetCppVirtObjTypeNameByVTablePtrFunc(Pointer(APointer.PreviouslyUsedByClass), 0);
  8246. if Assigned(LCppObjectTypeName) then
  8247. begin
  8248. LMsgPtr := AppendStringToBuffer(PreviousObjectClassMsg, LMsgPtr, Length(PreviousObjectClassMsg));
  8249. LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName));
  8250. end;
  8251. end;
  8252. {$endif}
  8253. {Get the current class for this block}
  8254. if (AOperation > boGetMem) and (APointer.AllocatedByRoutine <> nil) then
  8255. begin
  8256. LMsgPtr := AppendStringToBuffer(CurrentObjectClassMsg, LMsgPtr, Length(CurrentObjectClassMsg));
  8257. LClass := DetectClassInstance(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)));
  8258. if IntPtr(LClass) = IntPtr(@FreedObjectVMT.VMTMethods[0]) then
  8259. LClass := nil;
  8260. {$ifndef CheckCppObjectTypeEnabled}
  8261. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8262. {$else}
  8263. if (LClass = nil) and Assigned(GetCppVirtObjTypeNameFunc) then
  8264. begin
  8265. LCppObjectTypeName := GetCppVirtObjTypeNameFunc(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)),
  8266. APointer.UserSize);
  8267. if LCppObjectTypeName <> nil then
  8268. LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName))
  8269. else
  8270. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8271. end
  8272. else
  8273. begin
  8274. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8275. end;
  8276. {$endif}
  8277. {Log the allocation group}
  8278. if APointer.AllocationGroup > 0 then
  8279. begin
  8280. LMsgPtr := AppendStringToBuffer(CurrentAllocationGroupMsg, LMsgPtr, Length(CurrentAllocationGroupMsg));
  8281. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationGroup, LMsgPtr);
  8282. end;
  8283. {Log the allocation number}
  8284. LMsgPtr := AppendStringToBuffer(CurrentAllocationNumberMsg, LMsgPtr, Length(CurrentAllocationNumberMsg));
  8285. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationNumber, LMsgPtr);
  8286. end
  8287. else
  8288. begin
  8289. {Log the allocation group}
  8290. if APointer.AllocationGroup > 0 then
  8291. begin
  8292. LMsgPtr := AppendStringToBuffer(PreviousAllocationGroupMsg, LMsgPtr, Length(PreviousAllocationGroupMsg));
  8293. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationGroup, LMsgPtr);
  8294. end;
  8295. {Log the allocation number}
  8296. LMsgPtr := AppendStringToBuffer(PreviousAllocationNumberMsg, LMsgPtr, Length(PreviousAllocationNumberMsg));
  8297. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationNumber, LMsgPtr);
  8298. end;
  8299. {Get the call stack for the previous free}
  8300. if APointer.FreeStackTrace[0] <> 0 then
  8301. begin
  8302. LMsgPtr := AppendStringToBuffer(ThreadIDAtFreeMsg, LMsgPtr, Length(ThreadIDAtFreeMsg));
  8303. LMsgPtr := NativeUIntToHexBuf(APointer.FreedByThread, LMsgPtr);
  8304. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  8305. LMsgPtr := LogStackTrace(@APointer.FreeStackTrace, StackTraceDepth, LMsgPtr);
  8306. end;
  8307. end
  8308. else
  8309. begin
  8310. {Header has been corrupted}
  8311. LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedMsg, LMsgPtr, Length(BlockHeaderCorruptedMsg));
  8312. end;
  8313. {Add the current stack trace}
  8314. LMsgPtr := LogCurrentThreadAndStackTrace(3 + Ord(AOperation <> boGetMem) + Ord(AOperation = boReallocMem), LMsgPtr);
  8315. {$ifndef DisableLoggingOfMemoryDumps}
  8316. {Add the memory dump}
  8317. LMsgPtr := LogMemoryDump(APointer, LMsgPtr);
  8318. {$endif}
  8319. {Trailing CRLF}
  8320. LMsgPtr^ := #13;
  8321. Inc(LMsgPtr);
  8322. LMsgPtr^ := #10;
  8323. Inc(LMsgPtr);
  8324. {Trailing #0}
  8325. LMsgPtr^ := #0;
  8326. {$ifdef LogErrorsToFile}
  8327. {Log the error}
  8328. AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
  8329. {$endif}
  8330. {$ifdef UseOutputDebugString}
  8331. OutputDebugStringA(LErrorMessage);
  8332. {$endif}
  8333. {Show the message}
  8334. {$ifndef NoMessageBoxes}
  8335. AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
  8336. ShowMessageBox(LErrorMessage, LErrorMessageTitle);
  8337. {$endif}
  8338. end;
  8339. {Logs the stack traces for a memory leak to file}
  8340. procedure LogMemoryLeakOrAllocatedBlock(APointer: PFullDebugBlockHeader; IsALeak: Boolean);
  8341. var
  8342. LHeaderValid: Boolean;
  8343. LMsgPtr: PAnsiChar;
  8344. LErrorMessage: array[0..32767] of AnsiChar;
  8345. LClass: TClass;
  8346. {$ifdef CheckCppObjectTypeEnabled}
  8347. LCppObjectTypeName: PAnsiChar;
  8348. {$endif}
  8349. begin
  8350. {Display the error header and the operation type.}
  8351. if IsALeak then
  8352. LMsgPtr := AppendStringToBuffer(LeakLogHeader, @LErrorMessage[0], Length(LeakLogHeader))
  8353. else
  8354. LMsgPtr := AppendStringToBuffer(BlockScanLogHeader, @LErrorMessage[0], Length(BlockScanLogHeader));
  8355. LMsgPtr := NativeUIntToStrBuf(GetAvailableSpaceInBlock(APointer) - FullDebugBlockOverhead, LMsgPtr);
  8356. {Is the debug info surrounding the block valid?}
  8357. LHeaderValid := CalculateHeaderCheckSum(APointer) = APointer.HeaderCheckSum;
  8358. {Is the header still intact?}
  8359. if LHeaderValid then
  8360. begin
  8361. {The header is still intact - display info about this/previous allocation}
  8362. if APointer.AllocationStackTrace[0] <> 0 then
  8363. begin
  8364. LMsgPtr := AppendStringToBuffer(ThreadIDAtAllocMsg, LMsgPtr, Length(ThreadIDAtAllocMsg));
  8365. LMsgPtr := NativeUIntToHexBuf(APointer.AllocatedByThread, LMsgPtr);
  8366. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  8367. LMsgPtr := LogStackTrace(@APointer.AllocationStackTrace, StackTraceDepth, LMsgPtr);
  8368. end;
  8369. LMsgPtr := AppendStringToBuffer(CurrentObjectClassMsg, LMsgPtr, Length(CurrentObjectClassMsg));
  8370. {Get the current class for this block}
  8371. LClass := DetectClassInstance(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)));
  8372. if IntPtr(LClass) = IntPtr(@FreedObjectVMT.VMTMethods[0]) then
  8373. LClass := nil;
  8374. {$ifndef CheckCppObjectTypeEnabled}
  8375. if LClass <> nil then
  8376. begin
  8377. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8378. end
  8379. else
  8380. begin
  8381. case DetectStringData(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)), APointer.UserSize) of
  8382. stUnknown: LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
  8383. stAnsiString: LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
  8384. stUnicodeString: LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
  8385. end;
  8386. end;
  8387. {$else}
  8388. if (LClass = nil) and Assigned(GetCppVirtObjTypeNameFunc) then
  8389. begin
  8390. LCppObjectTypeName := GetCppVirtObjTypeNameFunc(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)),
  8391. APointer.UserSize);
  8392. if LCppObjectTypeName <> nil then
  8393. LMsgPtr := AppendStringToBuffer(LCppObjectTypeName, LMsgPtr, StrLen(LCppObjectTypeName))
  8394. else
  8395. begin
  8396. case DetectStringData(Pointer(PByte(APointer) + SizeOf(TFullDebugBlockHeader)), APointer.UserSize) of
  8397. stUnknown: LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
  8398. stAnsiString: LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
  8399. stUnicodeString: LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
  8400. end;
  8401. end;
  8402. end
  8403. else
  8404. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  8405. {$endif}
  8406. {Log the allocation group}
  8407. if APointer.AllocationGroup > 0 then
  8408. begin
  8409. LMsgPtr := AppendStringToBuffer(CurrentAllocationGroupMsg, LMsgPtr, Length(CurrentAllocationGroupMsg));
  8410. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationGroup, LMsgPtr);
  8411. end;
  8412. {Log the allocation number}
  8413. LMsgPtr := AppendStringToBuffer(CurrentAllocationNumberMsg, LMsgPtr, Length(CurrentAllocationNumberMsg));
  8414. LMsgPtr := NativeUIntToStrBuf(APointer.AllocationNumber, LMsgPtr);
  8415. end
  8416. else
  8417. begin
  8418. {Header has been corrupted}
  8419. LMsgPtr^ := '.';
  8420. Inc(LMsgPtr);
  8421. LMsgPtr^ := ' ';
  8422. Inc(LMsgPtr);
  8423. LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedMsg, LMsgPtr, Length(BlockHeaderCorruptedMsg));
  8424. end;
  8425. {$ifndef DisableLoggingOfMemoryDumps}
  8426. {Add the memory dump}
  8427. LMsgPtr := LogMemoryDump(APointer, LMsgPtr);
  8428. {$endif}
  8429. {Trailing CRLF}
  8430. LMsgPtr^ := #13;
  8431. Inc(LMsgPtr);
  8432. LMsgPtr^ := #10;
  8433. Inc(LMsgPtr);
  8434. {Trailing #0}
  8435. LMsgPtr^ := #0;
  8436. {Log the error}
  8437. AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
  8438. end;
  8439. {Checks that a free block is unmodified}
  8440. function CheckFreeBlockUnmodified(APBlock: PFullDebugBlockHeader; ABlockSize: NativeUInt;
  8441. AOperation: TBlockOperation): Boolean;
  8442. var
  8443. LHeaderCheckSum: NativeUInt;
  8444. LHeaderValid, LFooterValid, LBlockUnmodified: Boolean;
  8445. begin
  8446. LHeaderCheckSum := CalculateHeaderCheckSum(APBlock);
  8447. LHeaderValid := LHeaderCheckSum = APBlock.HeaderCheckSum;
  8448. {Is the footer itself still in place}
  8449. LFooterValid := LHeaderValid
  8450. and (PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + APBlock.UserSize)^ = (not LHeaderCheckSum));
  8451. {Is the footer and debug VMT in place? The debug VMT is only valid if the user size is greater than the size of a pointer.}
  8452. if LFooterValid
  8453. and (APBlock.UserSize < SizeOf(Pointer)) or (PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader))^ = NativeUInt(@FreedObjectVMT.VMTMethods[0])) then
  8454. begin
  8455. {Store the debug fill pattern in place of the footer in order to simplify
  8456. checking for block modifications.}
  8457. PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + APBlock.UserSize)^ :=
  8458. {$ifndef CatchUseOfFreedInterfaces}
  8459. DebugFillPattern;
  8460. {$else}
  8461. RotateRight(NativeUInt(@VMTBadInterface), (APBlock.UserSize and (SizeOf(Pointer) - 1)) * 8);
  8462. {$endif}
  8463. {Check that all the filler bytes are valid inside the block, except for
  8464. the "dummy" class header}
  8465. LBlockUnmodified := CheckFillPattern(PNativeUInt(PByte(APBlock) + (SizeOf(TFullDebugBlockHeader) + SizeOf(Pointer))),
  8466. ABlockSize - (FullDebugBlockOverhead + SizeOf(Pointer)),
  8467. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  8468. {Reset the old footer}
  8469. PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + APBlock.UserSize)^ := not LHeaderCheckSum;
  8470. end
  8471. else
  8472. LBlockUnmodified := False;
  8473. if (not LHeaderValid) or (not LFooterValid) or (not LBlockUnmodified) then
  8474. begin
  8475. LogBlockError(APBlock, AOperation, LHeaderValid, LFooterValid);
  8476. Result := False;
  8477. end
  8478. else
  8479. Result := True;
  8480. end;
  8481. function DebugGetMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  8482. begin
  8483. {Scan the entire memory pool first?}
  8484. if FullDebugModeScanMemoryPoolBeforeEveryOperation then
  8485. ScanMemoryPoolForCorruptions;
  8486. {Enter the memory manager: block scans may not be performed now}
  8487. StartChangingFullDebugModeBlock;
  8488. try
  8489. {We need extra space for (a) The debug header, (b) the block debug trailer
  8490. and (c) the trailing block size pointer for free blocks}
  8491. Result := FastGetMem(ASize + FullDebugBlockOverhead);
  8492. if Result <> nil then
  8493. begin
  8494. {Large blocks are always newly allocated (and never reused), so checking
  8495. for a modify-after-free is not necessary.}
  8496. if (ASize > (MaximumMediumBlockSize - BlockHeaderSize - FullDebugBlockOverhead))
  8497. or CheckFreeBlockUnmodified(Result, GetAvailableSpaceInBlock(Result) + BlockHeaderSize, boGetMem) then
  8498. begin
  8499. {Set the allocation call stack}
  8500. GetStackTrace(@PFullDebugBlockHeader(Result).AllocationStackTrace, StackTraceDepth, 1);
  8501. {Set the thread ID of the thread that allocated the block}
  8502. PFullDebugBlockHeader(Result).AllocatedByThread := GetThreadID;
  8503. {Block is now in use: It was allocated by this routine}
  8504. PFullDebugBlockHeader(Result).AllocatedByRoutine := @DebugGetMem;
  8505. {Set the group number}
  8506. PFullDebugBlockHeader(Result).AllocationGroup := AllocationGroupStack[AllocationGroupStackTop];
  8507. {Set the allocation number}
  8508. IncrementAllocationNumber;
  8509. PFullDebugBlockHeader(Result).AllocationNumber := CurrentAllocationNumber;
  8510. {Clear the previous block trailer}
  8511. PNativeUInt(PByte(Result) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(Result).UserSize)^ :=
  8512. {$ifndef CatchUseOfFreedInterfaces}
  8513. DebugFillPattern;
  8514. {$else}
  8515. RotateRight(NativeUInt(@VMTBadInterface), (PFullDebugBlockHeader(Result).UserSize and (SizeOf(Pointer) - 1)) * 8);
  8516. {$endif}
  8517. {Set the user size for the block}
  8518. PFullDebugBlockHeader(Result).UserSize := ASize;
  8519. {Set the checksums}
  8520. UpdateHeaderAndFooterCheckSums(Result);
  8521. {$ifdef FullDebugModeCallBacks}
  8522. if Assigned(OnDebugGetMemFinish) then
  8523. OnDebugGetMemFinish(PFullDebugBlockHeader(Result), ASize);
  8524. {$endif}
  8525. {Return the start of the actual block}
  8526. Result := Pointer(PByte(Result) + SizeOf(TFullDebugBlockHeader));
  8527. {$ifdef EnableMemoryLeakReporting}
  8528. {Should this block be marked as an expected leak automatically?}
  8529. if FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak then
  8530. RegisterExpectedMemoryLeak(Result);
  8531. {$endif}
  8532. end
  8533. else
  8534. begin
  8535. Result := nil;
  8536. end;
  8537. end;
  8538. finally
  8539. {Leaving the memory manager routine: Block scans may be performed again.}
  8540. DoneChangingFullDebugModeBlock;
  8541. end;
  8542. end;
  8543. function CheckBlockBeforeFreeOrRealloc(APBlock: PFullDebugBlockHeader;
  8544. AOperation: TBlockOperation): Boolean;
  8545. var
  8546. LHeaderValid, LFooterValid: Boolean;
  8547. LPFooter: PNativeUInt;
  8548. {$ifndef CatchUseOfFreedInterfaces}
  8549. LBlockSize: NativeUInt;
  8550. LPTrailingByte, LPFillPatternEnd: PByte;
  8551. {$endif}
  8552. begin
  8553. {Is the checksum for the block header valid?}
  8554. LHeaderValid := CalculateHeaderCheckSum(APBlock) = APBlock.HeaderCheckSum;
  8555. {If the header is corrupted then the footer is assumed to be corrupt too.}
  8556. if LHeaderValid then
  8557. begin
  8558. {Check the footer checksum: The footer checksum should equal the header
  8559. checksum with all bits inverted.}
  8560. LPFooter := PNativeUInt(PByte(APBlock) + SizeOf(TFullDebugBlockHeader) + PFullDebugBlockHeader(APBlock).UserSize);
  8561. if APBlock.HeaderCheckSum = (not (LPFooter^)) then
  8562. begin
  8563. LFooterValid := True;
  8564. {$ifndef CatchUseOfFreedInterfaces}
  8565. {Large blocks do not have the debug fill pattern, since they are never reused.}
  8566. if PNativeUInt(PByte(APBlock) - BlockHeaderSize)^ and (IsMediumBlockFlag or IsLargeBlockFlag) <> IsLargeBlockFlag then
  8567. begin
  8568. {Check that the application has not modified bytes beyond the block
  8569. footer. The $80 fill pattern should extend up to 2 nativeints before
  8570. the start of the next block (leaving space for the free block size and
  8571. next block header.)}
  8572. LBlockSize := GetAvailableSpaceInBlock(APBlock);
  8573. LPFillPatternEnd := PByte(PByte(APBlock) + LBlockSize - SizeOf(Pointer));
  8574. LPTrailingByte := PByte(PByte(LPFooter) + SizeOf(NativeUInt));
  8575. while UIntPtr(LPTrailingByte) < UIntPtr(LPFillPatternEnd) do
  8576. begin
  8577. if Byte(LPTrailingByte^) <> DebugFillByte then
  8578. begin
  8579. LFooterValid := False;
  8580. Break;
  8581. end;
  8582. Inc(LPTrailingByte);
  8583. end;
  8584. end;
  8585. {$endif}
  8586. end
  8587. else
  8588. LFooterValid := False;
  8589. end
  8590. else
  8591. LFooterValid := False;
  8592. {The header and footer must be intact and the block must have been allocated
  8593. by this memory manager instance.}
  8594. if LFooterValid and (APBlock.AllocatedByRoutine = @DebugGetMem) then
  8595. begin
  8596. Result := True;
  8597. end
  8598. else
  8599. begin
  8600. {Log the error}
  8601. LogBlockError(APBlock, AOperation, LHeaderValid, LFooterValid);
  8602. {Return an error}
  8603. Result := False;
  8604. end;
  8605. end;
  8606. function DebugFreeMem(APointer: Pointer): Integer;
  8607. var
  8608. LActualBlock: PFullDebugBlockHeader;
  8609. LBlockHeader: NativeUInt;
  8610. begin
  8611. {Scan the entire memory pool first?}
  8612. if FullDebugModeScanMemoryPoolBeforeEveryOperation then
  8613. ScanMemoryPoolForCorruptions;
  8614. {Get a pointer to the start of the actual block}
  8615. LActualBlock := PFullDebugBlockHeader(PByte(APointer)
  8616. - SizeOf(TFullDebugBlockHeader));
  8617. {Is the debug info surrounding the block valid?}
  8618. if CheckBlockBeforeFreeOrRealloc(LActualBlock, boFreeMem) then
  8619. begin
  8620. {Enter the memory manager: block scans may not be performed now}
  8621. StartChangingFullDebugModeBlock;
  8622. try
  8623. {$ifdef FullDebugModeCallBacks}
  8624. if Assigned(OnDebugFreeMemStart) then
  8625. OnDebugFreeMemStart(LActualBlock);
  8626. {$endif}
  8627. {Large blocks are never reused, so there is no point in updating their
  8628. headers and fill pattern.}
  8629. LBlockHeader := PNativeUInt(PByte(LActualBlock) - BlockHeaderSize)^;
  8630. if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) <> IsLargeBlockFlag then
  8631. begin
  8632. {Get the class the block was used for}
  8633. LActualBlock.PreviouslyUsedByClass := PNativeUInt(APointer)^;
  8634. {Set the free call stack}
  8635. GetStackTrace(@LActualBlock.FreeStackTrace, StackTraceDepth, 1);
  8636. {Set the thread ID of the thread that freed the block}
  8637. LActualBlock.FreedByThread := GetThreadID;
  8638. {Block is now free}
  8639. LActualBlock.AllocatedByRoutine := nil;
  8640. {Clear the user area of the block}
  8641. DebugFillMem(APointer^, LActualBlock.UserSize,
  8642. {$ifndef CatchUseOfFreedInterfaces}DebugFillPattern{$else}NativeUInt(@VMTBadInterface){$endif});
  8643. {Set a pointer to the dummy VMT}
  8644. PNativeUInt(APointer)^ := NativeUInt(@FreedObjectVMT.VMTMethods[0]);
  8645. {Recalculate the checksums}
  8646. UpdateHeaderAndFooterCheckSums(LActualBlock);
  8647. end;
  8648. {$ifdef EnableMemoryLeakReporting}
  8649. {Automatically deregister the expected memory leak?}
  8650. if FullDebugModeRegisterAllAllocsAsExpectedMemoryLeak then
  8651. UnregisterExpectedMemoryLeak(APointer);
  8652. {$endif}
  8653. {Free the actual block}
  8654. Result := FastFreeMem(LActualBlock);
  8655. {$ifdef FullDebugModeCallBacks}
  8656. if Assigned(OnDebugFreeMemFinish) then
  8657. OnDebugFreeMemFinish(LActualBlock, Result);
  8658. {$endif}
  8659. finally
  8660. {Leaving the memory manager routine: Block scans may be performed again.}
  8661. DoneChangingFullDebugModeBlock;
  8662. end;
  8663. end
  8664. else
  8665. begin
  8666. {$ifdef SuppressFreeMemErrorsInsideException}
  8667. if {$ifdef BDS2006AndUp}ExceptObject{$else}RaiseList{$endif} <> nil then
  8668. Result := 0
  8669. else
  8670. {$endif}
  8671. Result := -1;
  8672. end;
  8673. end;
  8674. function DebugReallocMem(APointer: Pointer; ANewSize: {$ifdef XE2AndUp}NativeInt{$else}Integer{$endif}): Pointer;
  8675. var
  8676. LMoveSize, LBlockSpace: NativeUInt;
  8677. LActualBlock, LNewActualBlock: PFullDebugBlockHeader;
  8678. begin
  8679. {Scan the entire memory pool first?}
  8680. if FullDebugModeScanMemoryPoolBeforeEveryOperation then
  8681. ScanMemoryPoolForCorruptions;
  8682. {Get a pointer to the start of the actual block}
  8683. LActualBlock := PFullDebugBlockHeader(PByte(APointer)
  8684. - SizeOf(TFullDebugBlockHeader));
  8685. {Is the debug info surrounding the block valid?}
  8686. if CheckBlockBeforeFreeOrRealloc(LActualBlock, boReallocMem) then
  8687. begin
  8688. {Get the current block size}
  8689. LBlockSpace := GetAvailableSpaceInBlock(LActualBlock);
  8690. {Can the block fit? We need space for the debug overhead and the block header
  8691. of the next block}
  8692. if LBlockSpace < (NativeUInt(ANewSize) + FullDebugBlockOverhead) then
  8693. begin
  8694. {Get a new block of the requested size.}
  8695. Result := DebugGetMem(ANewSize);
  8696. if Result <> nil then
  8697. begin
  8698. {Block scans may not be performed now}
  8699. StartChangingFullDebugModeBlock;
  8700. try
  8701. {$ifdef FullDebugModeCallBacks}
  8702. if Assigned(OnDebugReallocMemStart) then
  8703. OnDebugReallocMemStart(LActualBlock, ANewSize);
  8704. {$endif}
  8705. {We reuse the old allocation number. Since DebugGetMem always bumps
  8706. CurrentAllocationGroup, there may be gaps in the sequence of
  8707. allocation numbers.}
  8708. LNewActualBlock := PFullDebugBlockHeader(PByte(Result)
  8709. - SizeOf(TFullDebugBlockHeader));
  8710. LNewActualBlock.AllocationGroup := LActualBlock.AllocationGroup;
  8711. LNewActualBlock.AllocationNumber := LActualBlock.AllocationNumber;
  8712. {Recalculate the header and footer checksums}
  8713. UpdateHeaderAndFooterCheckSums(LNewActualBlock);
  8714. {$ifdef FullDebugModeCallBacks}
  8715. if Assigned(OnDebugReallocMemFinish) then
  8716. OnDebugReallocMemFinish(LNewActualBlock, ANewSize);
  8717. {$endif}
  8718. finally
  8719. {Block scans can again be performed safely}
  8720. DoneChangingFullDebugModeBlock;
  8721. end;
  8722. {How many bytes to move?}
  8723. LMoveSize := LActualBlock.UserSize;
  8724. if LMoveSize > NativeUInt(ANewSize) then
  8725. LMoveSize := ANewSize;
  8726. {Move the data across}
  8727. System.Move(APointer^, Result^, LMoveSize);
  8728. {Free the old block}
  8729. DebugFreeMem(APointer);
  8730. end
  8731. else
  8732. begin
  8733. Result := nil;
  8734. end;
  8735. end
  8736. else
  8737. begin
  8738. {Block scans may not be performed now}
  8739. StartChangingFullDebugModeBlock;
  8740. try
  8741. {$ifdef FullDebugModeCallBacks}
  8742. if Assigned(OnDebugReallocMemStart) then
  8743. OnDebugReallocMemStart(LActualBlock, ANewSize);
  8744. {$endif}
  8745. {Clear all data after the new end of the block up to the old end of the
  8746. block, including the trailer.}
  8747. DebugFillMem(Pointer(PByte(APointer) + NativeUInt(ANewSize) + SizeOf(NativeUInt))^,
  8748. NativeInt(LActualBlock.UserSize) - ANewSize,
  8749. {$ifndef CatchUseOfFreedInterfaces}
  8750. DebugFillPattern);
  8751. {$else}
  8752. RotateRight(NativeUInt(@VMTBadInterface), (ANewSize and (SizeOf(Pointer) - 1)) * 8));
  8753. {$endif}
  8754. {Update the user size}
  8755. LActualBlock.UserSize := ANewSize;
  8756. {Set the new checksums}
  8757. UpdateHeaderAndFooterCheckSums(LActualBlock);
  8758. {$ifdef FullDebugModeCallBacks}
  8759. if Assigned(OnDebugReallocMemFinish) then
  8760. OnDebugReallocMemFinish(LActualBlock, ANewSize);
  8761. {$endif}
  8762. finally
  8763. {Block scans can again be performed safely}
  8764. DoneChangingFullDebugModeBlock;
  8765. end;
  8766. {Return the old pointer}
  8767. Result := APointer;
  8768. end;
  8769. end
  8770. else
  8771. begin
  8772. Result := nil;
  8773. end;
  8774. end;
  8775. {Allocates a block and fills it with zeroes}
  8776. function DebugAllocMem(ASize: {$ifdef XE2AndUp}NativeInt{$else}Cardinal{$endif}): Pointer;
  8777. begin
  8778. Result := DebugGetMem(ASize);
  8779. {Clear the block}
  8780. if Result <> nil then
  8781. FillChar(Result^, ASize, 0);
  8782. end;
  8783. {Raises a runtime error if a memory corruption was encountered. Subroutine for
  8784. InternalScanMemoryPool and InternalScanSmallBlockPool.}
  8785. procedure RaiseMemoryCorruptionError;
  8786. begin
  8787. {Disable exhaustive checking in order to prevent recursive exceptions.}
  8788. FullDebugModeScanMemoryPoolBeforeEveryOperation := False;
  8789. {Unblock the memory manager in case the creation of the exception below
  8790. causes an attempt to be made to allocate memory.}
  8791. UnblockFullDebugModeMMRoutines;
  8792. {Raise the runtime error}
  8793. {$ifdef BCB6OrDelphi7AndUp}
  8794. System.Error(reOutOfMemory);
  8795. {$else}
  8796. System.RunError(reOutOfMemory);
  8797. {$endif}
  8798. end;
  8799. {Subroutine for InternalScanMemoryPool: Checks the given small block pool for
  8800. allocated blocks}
  8801. procedure InternalScanSmallBlockPool(APSmallBlockPool: PSmallBlockPoolHeader;
  8802. AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
  8803. var
  8804. LCurPtr, LEndPtr: Pointer;
  8805. begin
  8806. {Get the first and last pointer for the pool}
  8807. GetFirstAndLastSmallBlockInPool(APSmallBlockPool, LCurPtr, LEndPtr);
  8808. {Step through all blocks}
  8809. while UIntPtr(LCurPtr) <= UIntPtr(LEndPtr) do
  8810. begin
  8811. {Is this block in use? If so, is the debug info intact?}
  8812. if ((PNativeUInt(PByte(LCurPtr) - BlockHeaderSize)^ and IsFreeBlockFlag) = 0) then
  8813. begin
  8814. if CheckBlockBeforeFreeOrRealloc(LCurPtr, boBlockCheck) then
  8815. begin
  8816. if (PFullDebugBlockHeader(LCurPtr).AllocationGroup >= AFirstAllocationGroupToLog)
  8817. and (PFullDebugBlockHeader(LCurPtr).AllocationGroup <= ALastAllocationGroupToLog) then
  8818. begin
  8819. LogMemoryLeakOrAllocatedBlock(LCurPtr, False);
  8820. end;
  8821. end
  8822. else
  8823. RaiseMemoryCorruptionError;
  8824. end
  8825. else
  8826. begin
  8827. {Check that the block has not been modified since being freed}
  8828. if not CheckFreeBlockUnmodified(LCurPtr, APSmallBlockPool.BlockType.BlockSize, boBlockCheck) then
  8829. RaiseMemoryCorruptionError;
  8830. end;
  8831. {Next block}
  8832. Inc(PByte(LCurPtr), APSmallBlockPool.BlockType.BlockSize);
  8833. end;
  8834. end;
  8835. {Subroutine for LogAllocatedBlocksToFile and ScanMemoryPoolForCorruptions:
  8836. Scans the memory pool for corruptions and optionally logs allocated blocks
  8837. in the allocation group range.}
  8838. procedure InternalScanMemoryPool(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
  8839. var
  8840. LPLargeBlock: PLargeBlockHeader;
  8841. LPMediumBlock: Pointer;
  8842. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  8843. LMediumBlockHeader: NativeUInt;
  8844. begin
  8845. {Block all the memory manager routines while performing the scan. No memory
  8846. block may be allocated or freed, and no FullDebugMode block header or
  8847. footer may be modified, while the scan is in progress.}
  8848. BlockFullDebugModeMMRoutines;
  8849. try
  8850. {Step through all the medium block pools}
  8851. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  8852. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  8853. begin
  8854. LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
  8855. while LPMediumBlock <> nil do
  8856. begin
  8857. LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
  8858. {Is the block in use?}
  8859. if LMediumBlockHeader and IsFreeBlockFlag = 0 then
  8860. begin
  8861. {Block is in use: Is it a medium block or small block pool?}
  8862. if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
  8863. begin
  8864. {Get all the leaks for the small block pool}
  8865. InternalScanSmallBlockPool(LPMediumBlock, AFirstAllocationGroupToLog, ALastAllocationGroupToLog);
  8866. end
  8867. else
  8868. begin
  8869. if CheckBlockBeforeFreeOrRealloc(LPMediumBlock, boBlockCheck) then
  8870. begin
  8871. if (PFullDebugBlockHeader(LPMediumBlock).AllocationGroup >= AFirstAllocationGroupToLog)
  8872. and (PFullDebugBlockHeader(LPMediumBlock).AllocationGroup <= ALastAllocationGroupToLog) then
  8873. begin
  8874. LogMemoryLeakOrAllocatedBlock(LPMediumBlock, False);
  8875. end;
  8876. end
  8877. else
  8878. RaiseMemoryCorruptionError;
  8879. end;
  8880. end
  8881. else
  8882. begin
  8883. {Check that the block has not been modified since being freed}
  8884. if not CheckFreeBlockUnmodified(LPMediumBlock, LMediumBlockHeader and DropMediumAndLargeFlagsMask, boBlockCheck) then
  8885. RaiseMemoryCorruptionError;
  8886. end;
  8887. {Next medium block}
  8888. LPMediumBlock := NextMediumBlock(LPMediumBlock);
  8889. end;
  8890. {Get the next medium block pool}
  8891. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  8892. end;
  8893. {Scan large blocks}
  8894. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  8895. while LPLargeBlock <> @LargeBlocksCircularList do
  8896. begin
  8897. if CheckBlockBeforeFreeOrRealloc(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), boBlockCheck) then
  8898. begin
  8899. if (PFullDebugBlockHeader(PByte(LPLargeBlock) + LargeBlockHeaderSize).AllocationGroup >= AFirstAllocationGroupToLog)
  8900. and (PFullDebugBlockHeader(PByte(LPLargeBlock) + LargeBlockHeaderSize).AllocationGroup <= ALastAllocationGroupToLog) then
  8901. begin
  8902. LogMemoryLeakOrAllocatedBlock(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), False);
  8903. end;
  8904. end
  8905. else
  8906. RaiseMemoryCorruptionError;
  8907. {Get the next large block}
  8908. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  8909. end;
  8910. finally
  8911. {Unblock the FullDebugMode memory manager routines.}
  8912. UnblockFullDebugModeMMRoutines;
  8913. end;
  8914. end;
  8915. {Logs detail about currently allocated memory blocks for the specified range of
  8916. allocation groups. if ALastAllocationGroupToLog is less than
  8917. AFirstAllocationGroupToLog or it is zero, then all allocation groups are
  8918. logged. This routine also checks the memory pool for consistency at the same
  8919. time, raising an "Out of Memory" error if the check fails.}
  8920. procedure LogAllocatedBlocksToFile(AFirstAllocationGroupToLog, ALastAllocationGroupToLog: Cardinal);
  8921. begin
  8922. {Validate input}
  8923. if (ALastAllocationGroupToLog = 0) or (ALastAllocationGroupToLog < AFirstAllocationGroupToLog) then
  8924. begin
  8925. {Bad input: log all groups}
  8926. AFirstAllocationGroupToLog := 0;
  8927. ALastAllocationGroupToLog := $ffffffff;
  8928. end;
  8929. {Scan the memory pool, logging allocated blocks in the requested range.}
  8930. InternalScanMemoryPool(AFirstAllocationGroupToLog, ALastAllocationGroupToLog);
  8931. end;
  8932. {Scans the memory pool for any corruptions. If a corruption is encountered an "Out of Memory" exception is
  8933. raised.}
  8934. procedure ScanMemoryPoolForCorruptions;
  8935. begin
  8936. {Scan the memory pool for corruptions, but don't log any allocated blocks}
  8937. InternalScanMemoryPool($ffffffff, 0);
  8938. end;
  8939. {-----------------------Invalid Virtual Method Calls-------------------------}
  8940. { TFreedObject }
  8941. {Used to determine the index of the virtual method call on the freed object.
  8942. Do not change this without updating MaxFakeVMTEntries. Currently 200.}
  8943. procedure TFreedObject.GetVirtualMethodIndex;
  8944. asm
  8945. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8946. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8947. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8948. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8949. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8950. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8951. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8952. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8953. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8954. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8955. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8956. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8957. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8958. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8959. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8960. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8961. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8962. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8963. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8964. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8965. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8966. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8967. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8968. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8969. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8970. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8971. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8972. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8973. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8974. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8975. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8976. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8977. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8978. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8979. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8980. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8981. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8982. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8983. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8984. Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex); Inc(VMIndex);
  8985. jmp TFreedObject.VirtualMethodError
  8986. end;
  8987. procedure TFreedObject.VirtualMethodError;
  8988. var
  8989. LVMOffset: Integer;
  8990. LMsgPtr: PAnsiChar;
  8991. LErrorMessage: array[0..32767] of AnsiChar;
  8992. {$ifndef NoMessageBoxes}
  8993. LErrorMessageTitle: array[0..1023] of AnsiChar;
  8994. {$endif}
  8995. LClass: TClass;
  8996. LActualBlock: PFullDebugBlockHeader;
  8997. begin
  8998. {Get the offset of the virtual method}
  8999. LVMOffset := (MaxFakeVMTEntries - VMIndex) * SizeOf(Pointer) + vmtParent + SizeOf(Pointer);
  9000. {Reset the index for the next error}
  9001. VMIndex := 0;
  9002. {Get the address of the actual block}
  9003. LActualBlock := PFullDebugBlockHeader(PByte(Self) - SizeOf(TFullDebugBlockHeader));
  9004. {Display the error header}
  9005. LMsgPtr := AppendStringToBuffer(VirtualMethodErrorHeader, @LErrorMessage[0], Length(VirtualMethodErrorHeader));
  9006. {Is the debug info surrounding the block valid?}
  9007. if CalculateHeaderCheckSum(LActualBlock) = LActualBlock.HeaderCheckSum then
  9008. begin
  9009. {Get the class this block was used for previously}
  9010. LClass := DetectClassInstance(@LActualBlock.PreviouslyUsedByClass);
  9011. if (LClass <> nil) and (IntPtr(LClass) <> IntPtr(@FreedObjectVMT.VMTMethods[0])) then
  9012. begin
  9013. LMsgPtr := AppendStringToBuffer(FreedObjectClassMsg, LMsgPtr, Length(FreedObjectClassMsg));
  9014. LMsgPtr := AppendClassNameToBuffer(LClass, LMsgPtr);
  9015. end;
  9016. {Get the virtual method name}
  9017. LMsgPtr := AppendStringToBuffer(VirtualMethodName, LMsgPtr, Length(VirtualMethodName));
  9018. if LVMOffset < 0 then
  9019. begin
  9020. LMsgPtr := AppendStringToBuffer(StandardVirtualMethodNames[LVMOffset div SizeOf(Pointer)], LMsgPtr, Length(StandardVirtualMethodNames[LVMOffset div SizeOf(Pointer)]));
  9021. end
  9022. else
  9023. begin
  9024. LMsgPtr := AppendStringToBuffer(VirtualMethodOffset, LMsgPtr, Length(VirtualMethodOffset));
  9025. LMsgPtr := NativeUIntToStrBuf(LVMOffset, LMsgPtr);
  9026. end;
  9027. {Virtual method address}
  9028. if (LClass <> nil) and (IntPtr(LClass) <> IntPtr(@FreedObjectVMT.VMTMethods[0])) then
  9029. begin
  9030. LMsgPtr := AppendStringToBuffer(VirtualMethodAddress, LMsgPtr, Length(VirtualMethodAddress));
  9031. LMsgPtr := NativeUIntToHexBuf(PNativeUInt(PByte(LClass) + LVMOffset)^, LMsgPtr);
  9032. end;
  9033. {Log the allocation group}
  9034. if LActualBlock.AllocationGroup > 0 then
  9035. begin
  9036. LMsgPtr := AppendStringToBuffer(PreviousAllocationGroupMsg, LMsgPtr, Length(PreviousAllocationGroupMsg));
  9037. LMsgPtr := NativeUIntToStrBuf(LActualBlock.AllocationGroup, LMsgPtr);
  9038. end;
  9039. {Log the allocation number}
  9040. LMsgPtr := AppendStringToBuffer(PreviousAllocationNumberMsg, LMsgPtr, Length(PreviousAllocationNumberMsg));
  9041. LMsgPtr := NativeUIntToStrBuf(LActualBlock.AllocationNumber, LMsgPtr);
  9042. {The header is still intact - display info about the this/previous allocation}
  9043. if LActualBlock.AllocationStackTrace[0] <> 0 then
  9044. begin
  9045. LMsgPtr := AppendStringToBuffer(ThreadIDAtObjectAllocMsg, LMsgPtr, Length(ThreadIDAtObjectAllocMsg));
  9046. LMsgPtr := NativeUIntToHexBuf(LActualBlock.AllocatedByThread, LMsgPtr);
  9047. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  9048. LMsgPtr := LogStackTrace(@LActualBlock.AllocationStackTrace, StackTraceDepth, LMsgPtr);
  9049. end;
  9050. {Get the call stack for the previous free}
  9051. if LActualBlock.FreeStackTrace[0] <> 0 then
  9052. begin
  9053. LMsgPtr := AppendStringToBuffer(ThreadIDAtObjectFreeMsg, LMsgPtr, Length(ThreadIDAtObjectFreeMsg));
  9054. LMsgPtr := NativeUIntToHexBuf(LActualBlock.FreedByThread, LMsgPtr);
  9055. LMsgPtr := AppendStringToBuffer(StackTraceMsg, LMsgPtr, Length(StackTraceMsg));
  9056. LMsgPtr := LogStackTrace(@LActualBlock.FreeStackTrace, StackTraceDepth, LMsgPtr);
  9057. end;
  9058. end
  9059. else
  9060. begin
  9061. {Header has been corrupted}
  9062. LMsgPtr := AppendStringToBuffer(BlockHeaderCorruptedNoHistoryMsg, LMsgPtr, Length(BlockHeaderCorruptedNoHistoryMsg));
  9063. end;
  9064. {Add the current stack trace}
  9065. LMsgPtr := LogCurrentThreadAndStackTrace(2, LMsgPtr);
  9066. {$ifndef DisableLoggingOfMemoryDumps}
  9067. {Add the pointer address}
  9068. LMsgPtr := LogMemoryDump(LActualBlock, LMsgPtr);
  9069. {$endif}
  9070. {Trailing CRLF}
  9071. LMsgPtr^ := #13;
  9072. Inc(LMsgPtr);
  9073. LMsgPtr^ := #10;
  9074. Inc(LMsgPtr);
  9075. {Trailing #0}
  9076. LMsgPtr^ := #0;
  9077. {$ifdef LogErrorsToFile}
  9078. {Log the error}
  9079. AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
  9080. {$endif}
  9081. {$ifdef UseOutputDebugString}
  9082. OutputDebugStringA(LErrorMessage);
  9083. {$endif}
  9084. {$ifndef NoMessageBoxes}
  9085. {Show the message}
  9086. AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
  9087. ShowMessageBox(LErrorMessage, LErrorMessageTitle);
  9088. {$endif}
  9089. {Raise an access violation}
  9090. RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nil);
  9091. end;
  9092. {$ifdef CatchUseOfFreedInterfaces}
  9093. procedure TFreedObject.InterfaceError;
  9094. var
  9095. LMsgPtr: PAnsiChar;
  9096. {$ifndef NoMessageBoxes}
  9097. LErrorMessageTitle: array[0..1023] of AnsiChar;
  9098. {$endif}
  9099. LErrorMessage: array[0..4000] of AnsiChar;
  9100. begin
  9101. {Display the error header}
  9102. LMsgPtr := AppendStringToBuffer(InterfaceErrorHeader, @LErrorMessage[0], Length(InterfaceErrorHeader));
  9103. {Add the current stack trace}
  9104. LMsgPtr := LogCurrentThreadAndStackTrace(2, LMsgPtr);
  9105. {Trailing CRLF}
  9106. LMsgPtr^ := #13;
  9107. Inc(LMsgPtr);
  9108. LMsgPtr^ := #10;
  9109. Inc(LMsgPtr);
  9110. {Trailing #0}
  9111. LMsgPtr^ := #0;
  9112. {$ifdef LogErrorsToFile}
  9113. {Log the error}
  9114. AppendEventLog(@LErrorMessage[0], NativeUInt(LMsgPtr) - NativeUInt(@LErrorMessage[0]));
  9115. {$endif}
  9116. {$ifdef UseOutputDebugString}
  9117. OutputDebugStringA(LErrorMessage);
  9118. {$endif}
  9119. {$ifndef NoMessageBoxes}
  9120. {Show the message}
  9121. AppendStringToModuleName(BlockErrorMsgTitle, LErrorMessageTitle);
  9122. ShowMessageBox(LErrorMessage, LErrorMessageTitle);
  9123. {$endif}
  9124. {Raise an access violation}
  9125. RaiseException(EXCEPTION_ACCESS_VIOLATION, 0, 0, nil);
  9126. end;
  9127. {$endif}
  9128. {$endif}
  9129. {----------------------------Memory Leak Checking-----------------------------}
  9130. {$ifdef EnableMemoryLeakReporting}
  9131. {Adds a leak to the specified list}
  9132. function UpdateExpectedLeakList(APLeakList: PPExpectedMemoryLeak;
  9133. APNewEntry: PExpectedMemoryLeak; AExactSizeMatch: Boolean = True): Boolean;
  9134. var
  9135. LPInsertAfter, LPNewEntry: PExpectedMemoryLeak;
  9136. begin
  9137. {Default to error}
  9138. Result := False;
  9139. {Find the insertion spot}
  9140. LPInsertAfter := APLeakList^;
  9141. while LPInsertAfter <> nil do
  9142. begin
  9143. {Too big?}
  9144. if LPInsertAfter.LeakSize > APNewEntry.LeakSize then
  9145. begin
  9146. LPInsertAfter := LPInsertAfter.PreviousLeak;
  9147. Break;
  9148. end;
  9149. {Find a matching entry. If an exact size match is not required and the leak
  9150. is larger than the current entry, use it if the expected size of the next
  9151. entry is too large.}
  9152. if (IntPtr(LPInsertAfter.LeakAddress) = IntPtr(APNewEntry.LeakAddress))
  9153. and ((IntPtr(LPInsertAfter.LeakedClass) = IntPtr(APNewEntry.LeakedClass))
  9154. {$ifdef CheckCppObjectTypeEnabled}
  9155. or (LPInsertAfter.LeakedCppTypeIdPtr = APNewEntry.LeakedCppTypeIdPtr)
  9156. {$endif}
  9157. )
  9158. and ((LPInsertAfter.LeakSize = APNewEntry.LeakSize)
  9159. or ((not AExactSizeMatch)
  9160. and (LPInsertAfter.LeakSize < APNewEntry.LeakSize)
  9161. and ((LPInsertAfter.NextLeak = nil)
  9162. or (LPInsertAfter.NextLeak.LeakSize > APNewEntry.LeakSize))
  9163. )) then
  9164. begin
  9165. if (LPInsertAfter.LeakCount + APNewEntry.LeakCount) >= 0 then
  9166. begin
  9167. Inc(LPInsertAfter.LeakCount, APNewEntry.LeakCount);
  9168. {Is the count now 0?}
  9169. if LPInsertAfter.LeakCount = 0 then
  9170. begin
  9171. {Delete the entry}
  9172. if LPInsertAfter.NextLeak <> nil then
  9173. LPInsertAfter.NextLeak.PreviousLeak := LPInsertAfter.PreviousLeak;
  9174. if LPInsertAfter.PreviousLeak <> nil then
  9175. LPInsertAfter.PreviousLeak.NextLeak := LPInsertAfter.NextLeak
  9176. else
  9177. APLeakList^ := LPInsertAfter.NextLeak;
  9178. {Insert it as the first free slot}
  9179. LPInsertAfter.NextLeak := ExpectedMemoryLeaks.FirstFreeSlot;
  9180. ExpectedMemoryLeaks.FirstFreeSlot := LPInsertAfter;
  9181. end;
  9182. Result := True;
  9183. end;
  9184. Exit;
  9185. end;
  9186. {Next entry}
  9187. if LPInsertAfter.NextLeak <> nil then
  9188. LPInsertAfter := LPInsertAfter.NextLeak
  9189. else
  9190. Break;
  9191. end;
  9192. if APNewEntry.LeakCount > 0 then
  9193. begin
  9194. {Get a position for the entry}
  9195. LPNewEntry := ExpectedMemoryLeaks.FirstFreeSlot;
  9196. if LPNewEntry <> nil then
  9197. begin
  9198. ExpectedMemoryLeaks.FirstFreeSlot := LPNewEntry.NextLeak;
  9199. end
  9200. else
  9201. begin
  9202. if ExpectedMemoryLeaks.EntriesUsed < Length(ExpectedMemoryLeaks.ExpectedLeaks) then
  9203. begin
  9204. LPNewEntry := @ExpectedMemoryLeaks.ExpectedLeaks[ExpectedMemoryLeaks.EntriesUsed];
  9205. Inc(ExpectedMemoryLeaks.EntriesUsed);
  9206. end
  9207. else
  9208. begin
  9209. {No more space}
  9210. Exit;
  9211. end;
  9212. end;
  9213. {Set the entry}
  9214. LPNewEntry^ := APNewEntry^;
  9215. {Insert it into the list}
  9216. LPNewEntry.PreviousLeak := LPInsertAfter;
  9217. if LPInsertAfter <> nil then
  9218. begin
  9219. LPNewEntry.NextLeak := LPInsertAfter.NextLeak;
  9220. if LPNewEntry.NextLeak <> nil then
  9221. LPNewEntry.NextLeak.PreviousLeak := LPNewEntry;
  9222. LPInsertAfter.NextLeak := LPNewEntry;
  9223. end
  9224. else
  9225. begin
  9226. LPNewEntry.NextLeak := APLeakList^;
  9227. if LPNewEntry.NextLeak <> nil then
  9228. LPNewEntry.NextLeak.PreviousLeak := LPNewEntry;
  9229. APLeakList^ := LPNewEntry;
  9230. end;
  9231. Result := True;
  9232. end;
  9233. end;
  9234. {Locks the expected leaks. Returns false if the list could not be allocated.}
  9235. function LockExpectedMemoryLeaksList: Boolean;
  9236. begin
  9237. {Lock the expected leaks list}
  9238. {$ifndef AssumeMultiThreaded}
  9239. if IsMultiThread then
  9240. {$endif}
  9241. begin
  9242. while LockCmpxchg(0, 1, @ExpectedMemoryLeaksListLocked) <> 0 do
  9243. begin
  9244. {$ifdef NeverSleepOnThreadContention}
  9245. {$ifdef UseSwitchToThread}
  9246. SwitchToThread;
  9247. {$endif}
  9248. {$else}
  9249. Sleep(InitialSleepTime);
  9250. if LockCmpxchg(0, 1, @ExpectedMemoryLeaksListLocked) = 0 then
  9251. Break;
  9252. Sleep(AdditionalSleepTime);
  9253. {$endif}
  9254. end;
  9255. end;
  9256. {Allocate the list if it does not exist}
  9257. if ExpectedMemoryLeaks = nil then
  9258. ExpectedMemoryLeaks := VirtualAlloc(nil, ExpectedMemoryLeaksListSize, MEM_COMMIT, PAGE_READWRITE);
  9259. {Done}
  9260. Result := ExpectedMemoryLeaks <> nil;
  9261. end;
  9262. {Registers expected memory leaks. Returns true on success. The list of leaked
  9263. blocks is limited, so failure is possible if the list is full.}
  9264. function RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
  9265. var
  9266. LNewEntry: TExpectedMemoryLeak;
  9267. begin
  9268. {Fill out the structure}
  9269. {$ifndef FullDebugMode}
  9270. LNewEntry.LeakAddress := ALeakedPointer;
  9271. {$else}
  9272. LNewEntry.LeakAddress := Pointer(PByte(ALeakedPointer) - SizeOf(TFullDebugBlockHeader));
  9273. {$endif}
  9274. LNewEntry.LeakedClass := nil;
  9275. {$ifdef CheckCppObjectTypeEnabled}
  9276. LNewEntry.LeakedCppTypeIdPtr := nil;
  9277. {$endif}
  9278. LNewEntry.LeakSize := 0;
  9279. LNewEntry.LeakCount := 1;
  9280. {Add it to the correct list}
  9281. Result := LockExpectedMemoryLeaksList
  9282. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LNewEntry);
  9283. ExpectedMemoryLeaksListLocked := False;
  9284. end;
  9285. function RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
  9286. var
  9287. LNewEntry: TExpectedMemoryLeak;
  9288. begin
  9289. {Fill out the structure}
  9290. LNewEntry.LeakAddress := nil;
  9291. LNewEntry.LeakedClass := ALeakedObjectClass;
  9292. {$ifdef CheckCppObjectTypeEnabled}
  9293. LNewEntry.LeakedCppTypeIdPtr := nil;
  9294. {$endif}
  9295. LNewEntry.LeakSize := ALeakedObjectClass.InstanceSize;
  9296. LNewEntry.LeakCount := ACount;
  9297. {Add it to the correct list}
  9298. Result := LockExpectedMemoryLeaksList
  9299. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LNewEntry);
  9300. ExpectedMemoryLeaksListLocked := False;
  9301. end;
  9302. {$ifdef CheckCppObjectTypeEnabled}
  9303. function RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): Boolean; overload;
  9304. var
  9305. LNewEntry: TExpectedMemoryLeak;
  9306. begin
  9307. {Fill out the structure}
  9308. if Assigned(GetCppVirtObjSizeByTypeIdPtrFunc) then
  9309. begin
  9310. //Return 0 if not a proper type
  9311. LNewEntry.LeakSize := GetCppVirtObjSizeByTypeIdPtrFunc(ALeakedCppVirtObjTypeIdPtr);
  9312. if LNewEntry.LeakSize > 0 then
  9313. begin
  9314. LNewEntry.LeakAddress := nil;
  9315. LNewEntry.LeakedClass := nil;
  9316. LNewEntry.LeakedCppTypeIdPtr := ALeakedCppVirtObjTypeIdPtr;
  9317. LNewEntry.LeakCount := ACount;
  9318. {Add it to the correct list}
  9319. Result := LockExpectedMemoryLeaksList
  9320. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LNewEntry);
  9321. ExpectedMemoryLeaksListLocked := False;
  9322. end
  9323. else
  9324. begin
  9325. Result := False;
  9326. end;
  9327. end
  9328. else
  9329. begin
  9330. Result := False;
  9331. end;
  9332. end;
  9333. {$endif}
  9334. function RegisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
  9335. var
  9336. LNewEntry: TExpectedMemoryLeak;
  9337. begin
  9338. {Fill out the structure}
  9339. LNewEntry.LeakAddress := nil;
  9340. LNewEntry.LeakedClass := nil;
  9341. {$ifdef CheckCppObjectTypeEnabled}
  9342. LNewEntry.LeakedCppTypeIdPtr := nil;
  9343. {$endif}
  9344. LNewEntry.LeakSize := ALeakedBlockSize;
  9345. LNewEntry.LeakCount := ACount;
  9346. {Add it to the correct list}
  9347. Result := LockExpectedMemoryLeaksList
  9348. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryBySizeOnly, @LNewEntry);
  9349. ExpectedMemoryLeaksListLocked := False;
  9350. end;
  9351. function UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
  9352. var
  9353. LNewEntry: TExpectedMemoryLeak;
  9354. begin
  9355. {Fill out the structure}
  9356. {$ifndef FullDebugMode}
  9357. LNewEntry.LeakAddress := ALeakedPointer;
  9358. {$else}
  9359. LNewEntry.LeakAddress := Pointer(PByte(ALeakedPointer) - SizeOf(TFullDebugBlockHeader));
  9360. {$endif}
  9361. LNewEntry.LeakedClass := nil;
  9362. {$ifdef CheckCppObjectTypeEnabled}
  9363. LNewEntry.LeakedCppTypeIdPtr := nil;
  9364. {$endif}
  9365. LNewEntry.LeakSize := 0;
  9366. LNewEntry.LeakCount := -1;
  9367. {Remove it from the list}
  9368. Result := LockExpectedMemoryLeaksList
  9369. and UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LNewEntry);
  9370. ExpectedMemoryLeaksListLocked := False;
  9371. end;
  9372. function UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
  9373. begin
  9374. Result := RegisterExpectedMemoryLeak(ALeakedObjectClass, - ACount);
  9375. end;
  9376. {$ifdef CheckCppObjectTypeEnabled}
  9377. function UnregisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr: Pointer; ACount: Integer): Boolean; overload;
  9378. begin
  9379. Result := RegisterExpectedMemoryLeak(ALeakedCppVirtObjTypeIdPtr, - ACount);
  9380. end;
  9381. {$endif}
  9382. function UnregisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
  9383. begin
  9384. Result := RegisterExpectedMemoryLeak(ALeakedBlockSize, - ACount);
  9385. end;
  9386. {Returns a list of all expected memory leaks}
  9387. function GetRegisteredMemoryLeaks: TRegisteredMemoryLeaks;
  9388. procedure AddEntries(AEntry: PExpectedMemoryLeak);
  9389. var
  9390. LInd: Integer;
  9391. begin
  9392. while AEntry <> nil do
  9393. begin
  9394. LInd := Length(Result);
  9395. SetLength(Result, LInd + 1);
  9396. {Add the entry}
  9397. {$ifndef FullDebugMode}
  9398. Result[LInd].LeakAddress := AEntry.LeakAddress;
  9399. {$else}
  9400. Result[LInd].LeakAddress := Pointer(PByte(AEntry.LeakAddress) + SizeOf(TFullDebugBlockHeader));
  9401. {$endif}
  9402. Result[LInd].LeakedClass := AEntry.LeakedClass;
  9403. {$ifdef CheckCppObjectTypeEnabled}
  9404. Result[LInd].LeakedCppTypeIdPtr := AEntry.LeakedCppTypeIdPtr;
  9405. {$endif}
  9406. Result[LInd].LeakSize := AEntry.LeakSize;
  9407. Result[LInd].LeakCount := AEntry.LeakCount;
  9408. {Next entry}
  9409. AEntry := AEntry.NextLeak;
  9410. end;
  9411. end;
  9412. begin
  9413. SetLength(Result, 0);
  9414. if (ExpectedMemoryLeaks <> nil) and LockExpectedMemoryLeaksList then
  9415. begin
  9416. {Add all entries}
  9417. AddEntries(ExpectedMemoryLeaks.FirstEntryByAddress);
  9418. AddEntries(ExpectedMemoryLeaks.FirstEntryByClass);
  9419. AddEntries(ExpectedMemoryLeaks.FirstEntryBySizeOnly);
  9420. {Unlock the list}
  9421. ExpectedMemoryLeaksListLocked := False;
  9422. end;
  9423. end;
  9424. {$else}
  9425. {$ifdef BDS2006AndUp}
  9426. function NoOpRegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean;
  9427. begin
  9428. {Do nothing. Used when memory leak reporting is disabled under Delphi 2006 and later.}
  9429. Result := False;
  9430. end;
  9431. function NoOpUnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean;
  9432. begin
  9433. {Do nothing. Used when memory leak reporting is disabled under Delphi 2006 and later.}
  9434. Result := False;
  9435. end;
  9436. {$endif}
  9437. {$endif}
  9438. {Detects the probable string data type for a memory block.}
  9439. function DetectStringData(APMemoryBlock: Pointer;
  9440. AAvailableSpaceInBlock: NativeInt): TStringDataType;
  9441. const
  9442. {If the string reference count field contains a value greater than this,
  9443. then it is assumed that the block is not a string.}
  9444. MaxRefCount = 255;
  9445. {The lowest ASCII character code considered valid string data. If there are
  9446. any characters below this code point then the data is assumed not to be a
  9447. string. #9 = Tab.}
  9448. MinCharCode = #9;
  9449. var
  9450. LStringLength, LElemSize, LCharInd: Integer;
  9451. LPAnsiStr: PAnsiChar;
  9452. LPUniStr: PWideChar;
  9453. begin
  9454. {Check that the reference count is within a reasonable range}
  9455. if PStrRec(APMemoryBlock).refCnt > MaxRefCount then
  9456. begin
  9457. Result := stUnknown;
  9458. Exit;
  9459. end;
  9460. {$ifdef BCB6OrDelphi6AndUp}
  9461. {$if RTLVersion >= 20}
  9462. LElemSize := PStrRec(APMemoryBlock).elemSize;
  9463. {Element size must be either 1 (Ansi) or 2 (Unicode)}
  9464. if (LElemSize <> 1) and (LElemSize <> 2) then
  9465. begin
  9466. Result := stUnknown;
  9467. Exit;
  9468. end;
  9469. {$ifend}
  9470. {$if RTLVersion < 20}
  9471. LElemSize := 1;
  9472. {$ifend}
  9473. {$else}
  9474. LElemSize := 1;
  9475. {$endif}
  9476. {Get the string length}
  9477. LStringLength := PStrRec(APMemoryBlock).length;
  9478. {Does the string fit?}
  9479. if (LStringLength <= 0)
  9480. or (LStringLength >= (AAvailableSpaceInBlock - SizeOf(StrRec)) div LElemSize) then
  9481. begin
  9482. Result := stUnknown;
  9483. Exit;
  9484. end;
  9485. {Check for no characters outside the expected range. If there are,
  9486. then it is probably not a string.}
  9487. if LElemSize = 1 then
  9488. begin
  9489. {Check that all characters are in the range considered valid.}
  9490. LPAnsiStr := PAnsiChar(PByte(APMemoryBlock) + SizeOf(StrRec));
  9491. for LCharInd := 1 to LStringLength do
  9492. begin
  9493. if LPAnsiStr^ < MinCharCode then
  9494. begin
  9495. Result := stUnknown;
  9496. Exit;
  9497. end;
  9498. Inc(LPAnsiStr);
  9499. end;
  9500. {Must have a trailing #0}
  9501. if LPAnsiStr^ = #0 then
  9502. Result := stAnsiString
  9503. else
  9504. Result := stUnknown;
  9505. end
  9506. else
  9507. begin
  9508. {Check that all characters are in the range considered valid.}
  9509. LPUniStr := PWideChar(PByte(APMemoryBlock) + SizeOf(StrRec));
  9510. for LCharInd := 1 to LStringLength do
  9511. begin
  9512. if LPUniStr^ < MinCharCode then
  9513. begin
  9514. Result := stUnknown;
  9515. Exit;
  9516. end;
  9517. Inc(LPUniStr);
  9518. end;
  9519. {Must have a trailing #0}
  9520. if LPUniStr^ = #0 then
  9521. Result := stUnicodeString
  9522. else
  9523. Result := stUnknown;
  9524. end;
  9525. end;
  9526. {Walks all allocated blocks, calling ACallBack for each. Passes the user block size and AUserData to the callback.
  9527. Important note: All block types will be locked during the callback, so the memory manager cannot be used inside it.}
  9528. procedure WalkAllocatedBlocks(ACallBack: TWalkAllocatedBlocksCallback; AUserData: Pointer);
  9529. const
  9530. DebugHeaderSize = {$ifdef FullDebugMode}SizeOf(TFullDebugBlockHeader){$else}0{$endif};
  9531. TotalDebugOverhead = {$ifdef FullDebugMode}FullDebugBlockOverhead{$else}0{$endif};
  9532. var
  9533. LPMediumBlock: Pointer;
  9534. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  9535. LMediumBlockHeader: NativeUInt;
  9536. LPLargeBlock: PLargeBlockHeader;
  9537. LBlockSize: NativeInt;
  9538. LPSmallBlockPool: PSmallBlockPoolHeader;
  9539. LCurPtr, LEndPtr: Pointer;
  9540. LInd: Integer;
  9541. begin
  9542. {Lock all small block types}
  9543. LockAllSmallBlockTypes;
  9544. {Lock the medium blocks}
  9545. LockMediumBlocks;
  9546. try
  9547. {Step through all the medium block pools}
  9548. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  9549. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  9550. begin
  9551. LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
  9552. while LPMediumBlock <> nil do
  9553. begin
  9554. LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
  9555. {Is the block in use?}
  9556. if LMediumBlockHeader and IsFreeBlockFlag = 0 then
  9557. begin
  9558. if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
  9559. begin
  9560. {Step through all the blocks in the small block pool}
  9561. LPSmallBlockPool := LPMediumBlock;
  9562. {Get the useable size inside a block}
  9563. LBlockSize := LPSmallBlockPool.BlockType.BlockSize - BlockHeaderSize - TotalDebugOverhead;
  9564. {Get the first and last pointer for the pool}
  9565. GetFirstAndLastSmallBlockInPool(LPSmallBlockPool, LCurPtr, LEndPtr);
  9566. {Step through all blocks}
  9567. while UIntPtr(LCurPtr) <= UIntPtr(LEndPtr) do
  9568. begin
  9569. {Is this block in use?}
  9570. if (PNativeUInt(PByte(LCurPtr) - BlockHeaderSize)^ and IsFreeBlockFlag) = 0 then
  9571. begin
  9572. ACallBack(PByte(LCurPtr) + DebugHeaderSize, LBlockSize, AUserData);
  9573. end;
  9574. {Next block}
  9575. Inc(PByte(LCurPtr), LPSmallBlockPool.BlockType.BlockSize);
  9576. end;
  9577. end
  9578. else
  9579. begin
  9580. LBlockSize := (LMediumBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize - TotalDebugOverhead;
  9581. ACallBack(PByte(LPMediumBlock) + DebugHeaderSize, LBlockSize, AUserData);
  9582. end;
  9583. end;
  9584. {Next medium block}
  9585. LPMediumBlock := NextMediumBlock(LPMediumBlock);
  9586. end;
  9587. {Get the next medium block pool}
  9588. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  9589. end;
  9590. finally
  9591. {Unlock medium blocks}
  9592. MediumBlocksLocked := False;
  9593. {Unlock all the small block types}
  9594. for LInd := 0 to NumSmallBlockTypes - 1 do
  9595. SmallBlockTypes[LInd].BlockTypeLocked := False;
  9596. end;
  9597. {Step through all the large blocks}
  9598. LockLargeBlocks;
  9599. try
  9600. {Get all leaked large blocks}
  9601. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  9602. while LPLargeBlock <> @LargeBlocksCircularList do
  9603. begin
  9604. LBlockSize := (LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask) - BlockHeaderSize - LargeBlockHeaderSize - TotalDebugOverhead;
  9605. ACallBack(PByte(LPLargeBlock) + LargeBlockHeaderSize + DebugHeaderSize, LBlockSize, AUserData);
  9606. {Get the next large block}
  9607. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  9608. end;
  9609. finally
  9610. LargeBlocksLocked := False;
  9611. end;
  9612. end;
  9613. {-----------LogMemoryManagerStateToFile implementation------------}
  9614. const
  9615. MaxMemoryLogNodes = 100000;
  9616. QuickSortMinimumItemsInPartition = 4;
  9617. type
  9618. {While scanning the memory pool the list of classes is built up in a binary search tree.}
  9619. PMemoryLogNode = ^TMemoryLogNode;
  9620. TMemoryLogNode = record
  9621. {The left and right child nodes}
  9622. LeftAndRightNodePointers: array[Boolean] of PMemoryLogNode;
  9623. {The class this node belongs to}
  9624. ClassPtr: Pointer;
  9625. {The number of instances of the class}
  9626. InstanceCount: NativeInt;
  9627. {The total memory usage for this class}
  9628. TotalMemoryUsage: NativeInt;
  9629. end;
  9630. TMemoryLogNodes = array[0..MaxMemoryLogNodes - 1] of TMemoryLogNode;
  9631. PMemoryLogNodes = ^TMemoryLogNodes;
  9632. TMemoryLogInfo = record
  9633. {The number of nodes in "Nodes" that are used.}
  9634. NodeCount: Integer;
  9635. {The root node of the binary search tree. The content of this node is not actually used, it just simplifies the
  9636. binary search code.}
  9637. RootNode: TMemoryLogNode;
  9638. Nodes: TMemoryLogNodes;
  9639. end;
  9640. PMemoryLogInfo = ^TMemoryLogInfo;
  9641. {LogMemoryManagerStateToFile callback subroutine}
  9642. procedure LogMemoryManagerStateCallBack(APBlock: Pointer; ABlockSize: NativeInt; AUserData: Pointer);
  9643. var
  9644. LClass, LClassHashBits: NativeUInt;
  9645. LPLogInfo: PMemoryLogInfo;
  9646. LPParentNode, LPClassNode: PMemoryLogNode;
  9647. LChildNodeDirection: Boolean;
  9648. begin
  9649. LPLogInfo := AUserData;
  9650. {Detecting an object is very expensive (due to the VirtualQuery call), so we do some basic checks and try to find
  9651. the "class" in the tree first.}
  9652. LClass := PNativeUInt(APBlock)^;
  9653. {Do some basic pointer checks: The "class" must be dword aligned and beyond 64K}
  9654. if (LClass > 65535)
  9655. and (LClass and 3 = 0) then
  9656. begin
  9657. LPParentNode := @LPLogInfo.RootNode;
  9658. LClassHashBits := LClass;
  9659. repeat
  9660. LChildNodeDirection := Boolean(LClassHashBits and 1);
  9661. {Split off the next bit of the class pointer and traverse in the appropriate direction.}
  9662. LPClassNode := LPParentNode.LeftAndRightNodePointers[LChildNodeDirection];
  9663. {Is this child node the node the class we're looking for?}
  9664. if (LPClassNode = nil) or (NativeUInt(LPClassNode.ClassPtr) = LClass) then
  9665. Break;
  9666. {The node was not found: Keep on traversing the tree.}
  9667. LClassHashBits := LClassHashBits shr 1;
  9668. LPParentNode := LPClassNode;
  9669. until False;
  9670. end
  9671. else
  9672. LPClassNode := nil;
  9673. {Was the "class" found?}
  9674. if LPClassNode = nil then
  9675. begin
  9676. {The "class" is not yet in the tree: Determine if it is actually a class.}
  9677. LClass := NativeUInt(DetectClassInstance(APBlock));
  9678. {If it is not a class, try to detect the string type.}
  9679. if LClass = 0 then
  9680. LClass := Ord(DetectStringData(APBlock, ABlockSize));
  9681. {Is this class already in the tree?}
  9682. LPParentNode := @LPLogInfo.RootNode;
  9683. LClassHashBits := LClass;
  9684. repeat
  9685. LChildNodeDirection := Boolean(LClassHashBits and 1);
  9686. {Split off the next bit of the class pointer and traverse in the appropriate direction.}
  9687. LPClassNode := LPParentNode.LeftAndRightNodePointers[LChildNodeDirection];
  9688. {Is this child node the node the class we're looking for?}
  9689. if LPClassNode = nil then
  9690. begin
  9691. {The end of the tree was reached: Add a new child node.}
  9692. LPClassNode := @LPLogInfo.Nodes[LPLogInfo.NodeCount];
  9693. Inc(LPLogInfo.NodeCount);
  9694. LPParentNode.LeftAndRightNodePointers[LChildNodeDirection] := LPClassNode;
  9695. LPClassNode.ClassPtr := Pointer(LClass);
  9696. Break;
  9697. end
  9698. else
  9699. begin
  9700. if NativeUInt(LPClassNode.ClassPtr) = LClass then
  9701. Break;
  9702. end;
  9703. {The node was not found: Keep on traversing the tree.}
  9704. LClassHashBits := LClassHashBits shr 1;
  9705. LPParentNode := LPClassNode;
  9706. until False;
  9707. end;
  9708. {Update the statistics for the class}
  9709. Inc(LPClassNode.InstanceCount);
  9710. Inc(LPClassNode.TotalMemoryUsage, ABlockSize);
  9711. end;
  9712. {LogMemoryManagerStateToFile subroutine: A median-of-3 quicksort routine for sorting a TMemoryLogNodes array.}
  9713. procedure QuickSortLogNodes(APLeftItem: PMemoryLogNodes; ARightIndex: Integer);
  9714. var
  9715. M, I, J: Integer;
  9716. LPivot, LTempItem: TMemoryLogNode;
  9717. begin
  9718. while True do
  9719. begin
  9720. {Order the left, middle and right items in ascending order}
  9721. M := ARightIndex shr 1;
  9722. {Is the middle item larger than the left item?}
  9723. if APLeftItem[0].TotalMemoryUsage > APLeftItem[M].TotalMemoryUsage then
  9724. begin
  9725. {Swap items 0 and M}
  9726. LTempItem := APLeftItem[0];
  9727. APLeftItem[0] := APLeftItem[M];
  9728. APLeftItem[M] := LTempItem;
  9729. end;
  9730. {Is the middle item larger than the right?}
  9731. if APLeftItem[M].TotalMemoryUsage > APLeftItem[ARightIndex].TotalMemoryUsage then
  9732. begin
  9733. {The right-hand item is not larger - swap it with the middle}
  9734. LTempItem := APLeftItem[ARightIndex];
  9735. APLeftItem[ARightIndex] := APLeftItem[M];
  9736. APLeftItem[M] := LTempItem;
  9737. {Is the left larger than the new middle?}
  9738. if APLeftItem[0].TotalMemoryUsage > APLeftItem[M].TotalMemoryUsage then
  9739. begin
  9740. {Swap items 0 and M}
  9741. LTempItem := APLeftItem[0];
  9742. APLeftItem[0] := APLeftItem[M];
  9743. APLeftItem[M] := LTempItem;
  9744. end;
  9745. end;
  9746. {Move the pivot item out of the way by swapping M with R - 1}
  9747. LPivot := APLeftItem[M];
  9748. APLeftItem[M] := APLeftItem[ARightIndex - 1];
  9749. APLeftItem[ARightIndex - 1] := LPivot;
  9750. {Set up the loop counters}
  9751. I := 0;
  9752. J := ARightIndex - 1;
  9753. while true do
  9754. begin
  9755. {Find the first item from the left that is not smaller than the pivot}
  9756. repeat
  9757. Inc(I);
  9758. until APLeftItem[I].TotalMemoryUsage >= LPivot.TotalMemoryUsage;
  9759. {Find the first item from the right that is not larger than the pivot}
  9760. repeat
  9761. Dec(J);
  9762. until APLeftItem[J].TotalMemoryUsage <= LPivot.TotalMemoryUsage;
  9763. {Stop the loop when the two indexes cross}
  9764. if J < I then
  9765. Break;
  9766. {Swap item I and J}
  9767. LTempItem := APLeftItem[I];
  9768. APLeftItem[I] := APLeftItem[J];
  9769. APLeftItem[J] := LTempItem;
  9770. end;
  9771. {Put the pivot item back in the correct position by swapping I with R - 1}
  9772. APLeftItem[ARightIndex - 1] := APLeftItem[I];
  9773. APLeftItem[I] := LPivot;
  9774. {Sort the left-hand partition}
  9775. if J >= (QuickSortMinimumItemsInPartition - 1) then
  9776. QuickSortLogNodes(APLeftItem, J);
  9777. {Sort the right-hand partition}
  9778. APLeftItem := @APLeftItem[I + 1];
  9779. ARightIndex := ARightIndex - I - 1;
  9780. if ARightIndex < (QuickSortMinimumItemsInPartition - 1) then
  9781. Break;
  9782. end;
  9783. end;
  9784. {LogMemoryManagerStateToFile subroutine: An InsertionSort routine for sorting a TMemoryLogNodes array.}
  9785. procedure InsertionSortLogNodes(APLeftItem: PMemoryLogNodes; ARightIndex: Integer);
  9786. var
  9787. I, J: Integer;
  9788. LCurNode: TMemoryLogNode;
  9789. begin
  9790. for I := 1 to ARightIndex do
  9791. begin
  9792. LCurNode := APLeftItem[I];
  9793. {Scan backwards to find the best insertion spot}
  9794. J := I;
  9795. while (J > 0) and (APLeftItem[J - 1].TotalMemoryUsage > LCurNode.TotalMemoryUsage) do
  9796. begin
  9797. APLeftItem[J] := APLeftItem[J - 1];
  9798. Dec(J);
  9799. end;
  9800. APLeftItem[J] := LCurNode;
  9801. end;
  9802. end;
  9803. {Writes a log file containing a summary of the memory mananger state and a summary of allocated blocks grouped by
  9804. class. The file will be saved in UTF-8 encoding (in supported Delphi versions). Returns True on success. }
  9805. function LogMemoryManagerStateToFile(const AFileName: string; const AAdditionalDetails: string): Boolean;
  9806. const
  9807. MsgBufferSize = 65536;
  9808. MaxLineLength = 512;
  9809. {Write the UTF-8 BOM in Delphi versions that support UTF-8 conversion.}
  9810. LogStateHeaderMsg = {$ifdef BCB6OrDelphi7AndUp}#$EF#$BB#$BF + {$endif}
  9811. 'FastMM State Capture:'#13#10'---------------------'#13#10#13#10;
  9812. LogStateAllocatedMsg = 'K Allocated'#13#10;
  9813. LogStateOverheadMsg = 'K Overhead'#13#10;
  9814. LogStateEfficiencyMsg = '% Efficiency'#13#10#13#10'Usage Detail:'#13#10;
  9815. LogStateAdditionalInfoMsg = #13#10'Additional Information:'#13#10'-----------------------'#13#10;
  9816. var
  9817. LPLogInfo: PMemoryLogInfo;
  9818. LInd: Integer;
  9819. LPNode: PMemoryLogNode;
  9820. LMsgBuffer: array[0..MsgBufferSize - 1] of AnsiChar;
  9821. LPMsg: PAnsiChar;
  9822. LBufferSpaceUsed, LBytesWritten: Cardinal;
  9823. LFileHandle: NativeUInt;
  9824. LMemoryManagerUsageSummary: TMemoryManagerUsageSummary;
  9825. LUTF8Str: AnsiString;
  9826. begin
  9827. {Get the current memory manager usage summary.}
  9828. GetMemoryManagerUsageSummary(LMemoryManagerUsageSummary);
  9829. {Allocate the memory required to capture detailed allocation information.}
  9830. LPLogInfo := VirtualAlloc(nil, SizeOf(TMemoryLogInfo), MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE);
  9831. if LPLogInfo <> nil then
  9832. begin
  9833. try
  9834. {Log all allocated blocks by class.}
  9835. WalkAllocatedBlocks(LogMemoryManagerStateCallBack, LPLogInfo);
  9836. {Sort the classes by total memory usage: Do the initial QuickSort pass over the list to sort the list in groups
  9837. of QuickSortMinimumItemsInPartition size.}
  9838. if LPLogInfo.NodeCount >= QuickSortMinimumItemsInPartition then
  9839. QuickSortLogNodes(@LPLogInfo.Nodes[0], LPLogInfo.NodeCount - 1);
  9840. {Do the final InsertionSort pass.}
  9841. InsertionSortLogNodes(@LPLogInfo.Nodes[0], LPLogInfo.NodeCount - 1);
  9842. {Create the output file}
  9843. {$ifdef POSIX}
  9844. lFileHandle := FileCreate(AFilename);
  9845. {$else}
  9846. LFileHandle := CreateFile(PChar(AFilename), GENERIC_READ or GENERIC_WRITE, 0,
  9847. nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  9848. {$endif}
  9849. if LFileHandle <> INVALID_HANDLE_VALUE then
  9850. begin
  9851. try
  9852. {Log the usage summary}
  9853. LPMsg := @LMsgBuffer;
  9854. LPMsg := AppendStringToBuffer(LogStateHeaderMsg, LPMsg, Length(LogStateHeaderMsg));
  9855. LPMsg := NativeUIntToStrBuf(LMemoryManagerUsageSummary.AllocatedBytes shr 10, LPMsg);
  9856. LPMsg := AppendStringToBuffer(LogStateAllocatedMsg, LPMsg, Length(LogStateAllocatedMsg));
  9857. LPMsg := NativeUIntToStrBuf(LMemoryManagerUsageSummary.OverheadBytes shr 10, LPMsg);
  9858. LPMsg := AppendStringToBuffer(LogStateOverheadMsg, LPMsg, Length(LogStateOverheadMsg));
  9859. LPMsg := NativeUIntToStrBuf(Round(LMemoryManagerUsageSummary.EfficiencyPercentage), LPMsg);
  9860. LPMsg := AppendStringToBuffer(LogStateEfficiencyMsg, LPMsg, Length(LogStateEfficiencyMsg));
  9861. {Log the allocation detail}
  9862. for LInd := LPLogInfo.NodeCount - 1 downto 0 do
  9863. begin
  9864. LPNode := @LPLogInfo.Nodes[LInd];
  9865. {Add the allocated size}
  9866. LPMsg^ := ' ';
  9867. Inc(LPMsg);
  9868. LPMsg := NativeUIntToStrBuf(LPNode.TotalMemoryUsage, LPMsg);
  9869. LPMsg := AppendStringToBuffer(BytesMessage, LPMsg, Length(BytesMessage));
  9870. {Add the class type}
  9871. case NativeInt(LPNode.ClassPtr) of
  9872. {Unknown}
  9873. 0:
  9874. begin
  9875. LPMsg := AppendStringToBuffer(UnknownClassNameMsg, LPMsg, Length(UnknownClassNameMsg));
  9876. end;
  9877. {AnsiString}
  9878. 1:
  9879. begin
  9880. LPMsg := AppendStringToBuffer(AnsiStringBlockMessage, LPMsg, Length(AnsiStringBlockMessage));
  9881. end;
  9882. {UnicodeString}
  9883. 2:
  9884. begin
  9885. LPMsg := AppendStringToBuffer(UnicodeStringBlockMessage, LPMsg, Length(UnicodeStringBlockMessage));
  9886. end;
  9887. {Classes}
  9888. else
  9889. begin
  9890. LPMsg := AppendClassNameToBuffer(LPNode.ClassPtr, LPMsg);
  9891. end;
  9892. end;
  9893. {Add the count}
  9894. LPMsg^ := ' ';
  9895. Inc(LPMsg);
  9896. LPMsg^ := 'x';
  9897. Inc(LPMsg);
  9898. LPMsg^ := ' ';
  9899. Inc(LPMsg);
  9900. LPMsg := NativeUIntToStrBuf(LPNode.InstanceCount, LPMsg);
  9901. LPMsg^ := #13;
  9902. Inc(LPMsg);
  9903. LPMsg^ := #10;
  9904. Inc(LPMsg);
  9905. {Flush the buffer?}
  9906. LBufferSpaceUsed := NativeInt(LPMsg) - NativeInt(@LMsgBuffer);
  9907. if LBufferSpaceUsed > (MsgBufferSize - MaxLineLength) then
  9908. begin
  9909. WriteFile(LFileHandle, LMsgBuffer, LBufferSpaceUsed, LBytesWritten, nil);
  9910. LPMsg := @LMsgBuffer;
  9911. end;
  9912. end;
  9913. if AAdditionalDetails <> '' then
  9914. LPMsg := AppendStringToBuffer(LogStateAdditionalInfoMsg, LPMsg, Length(LogStateAdditionalInfoMsg));
  9915. {Flush any remaining bytes}
  9916. LBufferSpaceUsed := NativeInt(LPMsg) - NativeInt(@LMsgBuffer);
  9917. if LBufferSpaceUsed > 0 then
  9918. WriteFile(LFileHandle, LMsgBuffer, LBufferSpaceUsed, LBytesWritten, nil);
  9919. {Write the additional info}
  9920. if AAdditionalDetails <> '' then
  9921. begin
  9922. {$ifdef BCB6OrDelphi7AndUp}
  9923. LUTF8Str := UTF8Encode(AAdditionalDetails);
  9924. {$else}
  9925. LUTF8Str := AAdditionalDetails;
  9926. {$endif}
  9927. WriteFile(LFileHandle, LUTF8Str[1], Length(LUTF8Str), LBytesWritten, nil);
  9928. end;
  9929. {Success}
  9930. Result := True;
  9931. finally
  9932. {Close the file}
  9933. {$ifdef POSIX}
  9934. __close(LFileHandle)
  9935. {$else}
  9936. CloseHandle(LFileHandle);
  9937. {$endif}
  9938. end;
  9939. end
  9940. else
  9941. Result := False;
  9942. finally
  9943. VirtualFree(LPLogInfo, 0, MEM_RELEASE);
  9944. end;
  9945. end
  9946. else
  9947. Result := False;
  9948. end;
  9949. {-----------CheckBlocksOnShutdown implementation------------}
  9950. {Checks blocks for modification after free and also for memory leaks}
  9951. procedure CheckBlocksOnShutdown(ACheckForLeakedBlocks: Boolean);
  9952. {$ifdef EnableMemoryLeakReporting}
  9953. type
  9954. {Leaked class type}
  9955. TLeakedClass = record
  9956. ClassPointer: TClass;
  9957. {$ifdef CheckCppObjectTypeEnabled}
  9958. CppTypeIdPtr: Pointer;
  9959. {$endif}
  9960. NumLeaks: Cardinal;
  9961. end;
  9962. TLeakedClasses = array[0..255] of TLeakedClass;
  9963. PLeakedClasses = ^TLeakedClasses;
  9964. {Leak statistics for a small block type}
  9965. TSmallBlockLeaks = array[0..NumSmallBlockTypes - 1] of TLeakedClasses;
  9966. {A leaked medium or large block}
  9967. TMediumAndLargeBlockLeaks = array[0..4095] of NativeUInt;
  9968. {$endif}
  9969. var
  9970. {$ifdef EnableMemoryLeakReporting}
  9971. {The leaked classes for small blocks}
  9972. LSmallBlockLeaks: TSmallBlockLeaks;
  9973. LLeakType: TMemoryLeakType;
  9974. {$ifdef CheckCppObjectTypeEnabled}
  9975. LLeakedCppTypeIdPtr: Pointer;
  9976. LCppTypeName: PAnsiChar;
  9977. {$endif}
  9978. LMediumAndLargeBlockLeaks: TMediumAndLargeBlockLeaks;
  9979. LNumMediumAndLargeLeaks: Integer;
  9980. LPLargeBlock: PLargeBlockHeader;
  9981. LLeakMessage: array[0..32767] of AnsiChar;
  9982. {$ifndef NoMessageBoxes}
  9983. LMessageTitleBuffer: array[0..1023] of AnsiChar;
  9984. {$endif}
  9985. LMsgPtr: PAnsiChar;
  9986. LExpectedLeaksOnly, LSmallLeakHeaderAdded, LBlockSizeHeaderAdded: Boolean;
  9987. LBlockTypeInd, LClassInd, LBlockInd: Cardinal;
  9988. LMediumBlockSize, LPreviousBlockSize, LLargeBlockSize, LThisBlockSize: NativeUInt;
  9989. {$endif}
  9990. LPMediumBlock: Pointer;
  9991. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  9992. LMediumBlockHeader: NativeUInt;
  9993. {$ifdef EnableMemoryLeakReporting}
  9994. {Tries to account for a memory leak. Returns true if the leak is expected and
  9995. removes the leak from the list}
  9996. function GetMemoryLeakType(AAddress: Pointer; ASpaceInsideBlock: NativeUInt): TMemoryLeakType;
  9997. var
  9998. LLeak: TExpectedMemoryLeak;
  9999. begin
  10000. {Default to not found}
  10001. Result := mltUnexpectedLeak;
  10002. if ExpectedMemoryLeaks <> nil then
  10003. begin
  10004. {Check by pointer address}
  10005. LLeak.LeakAddress := AAddress;
  10006. LLeak.LeakedClass := nil;
  10007. {$ifdef CheckCppObjectTypeEnabled}
  10008. LLeak.LeakedCppTypeIdPtr := nil;
  10009. {$endif}
  10010. LLeak.LeakSize := 0;
  10011. LLeak.LeakCount := -1;
  10012. if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByAddress, @LLeak, False) then
  10013. begin
  10014. Result := mltExpectedLeakRegisteredByPointer;
  10015. Exit;
  10016. end;
  10017. {Check by class}
  10018. LLeak.LeakAddress := nil;
  10019. {$ifdef FullDebugMode}
  10020. LLeak.LeakedClass := TClass(PNativeUInt(PByte(AAddress)+ SizeOf(TFullDebugBlockHeader))^);
  10021. {$else}
  10022. LLeak.LeakedClass := TClass(PNativeUInt(AAddress)^);
  10023. {$endif}
  10024. {$ifdef CheckCppObjectTypeEnabled}
  10025. if Assigned(GetCppVirtObjTypeIdPtrFunc) then
  10026. begin
  10027. {$ifdef FullDebugMode}
  10028. LLeak.LeakedCppTypeIdPtr := GetCppVirtObjTypeIdPtrFunc(Pointer(PByte(AAddress)
  10029. + SizeOf(TFullDebugBlockHeader)), ASpaceInsideBlock);
  10030. {$else}
  10031. LLeak.LeakedCppTypeIdPtr := GetCppVirtObjTypeIdPtrFunc(AAddress, ASpaceInsideBlock);
  10032. {$endif}
  10033. end;
  10034. LLeakedCppTypeIdPtr := LLeak.LeakedCppTypeIdPtr;
  10035. {$endif}
  10036. LLeak.LeakSize := ASpaceInsideBlock;
  10037. if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryByClass, @LLeak, False) then
  10038. begin
  10039. Result := mltExpectedLeakRegisteredByClass;
  10040. Exit;
  10041. end;
  10042. {Check by size: the block must be large enough to hold the leak}
  10043. LLeak.LeakedClass := nil;
  10044. if UpdateExpectedLeakList(@ExpectedMemoryLeaks.FirstEntryBySizeOnly, @LLeak, False) then
  10045. Result := mltExpectedLeakRegisteredBySize;
  10046. end;
  10047. end;
  10048. {Checks the small block pool for leaks.}
  10049. procedure CheckSmallBlockPoolForLeaks(APSmallBlockPool: PSmallBlockPoolHeader);
  10050. var
  10051. LLeakedClass: TClass;
  10052. {$ifdef CheckCppObjectTypeEnabled}
  10053. LLeakedCppObjectTypeId: Pointer;
  10054. {$endif}
  10055. LSmallBlockLeakType: TMemoryLeakType;
  10056. LClassIndex: Integer;
  10057. LCurPtr, LEndPtr, LDataPtr: Pointer;
  10058. LBlockTypeIndex: Cardinal;
  10059. LPLeakedClasses: PLeakedClasses;
  10060. LSmallBlockSize: Cardinal;
  10061. begin
  10062. {Get the useable size inside a block}
  10063. LSmallBlockSize := APSmallBlockPool.BlockType.BlockSize - BlockHeaderSize;
  10064. {$ifdef FullDebugMode}
  10065. Dec(LSmallBlockSize, FullDebugBlockOverhead);
  10066. {$endif}
  10067. {Get the block type index}
  10068. LBlockTypeIndex := (UIntPtr(APSmallBlockPool.BlockType) - UIntPtr(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
  10069. LPLeakedClasses := @LSmallBlockLeaks[LBlockTypeIndex];
  10070. {Get the first and last pointer for the pool}
  10071. GetFirstAndLastSmallBlockInPool(APSmallBlockPool, LCurPtr, LEndPtr);
  10072. {Step through all blocks}
  10073. while UIntPtr(LCurPtr) <= UIntPtr(LEndPtr) do
  10074. begin
  10075. {Is this block in use? If so, is the debug info intact?}
  10076. if ((PNativeUInt(PByte(LCurPtr) - BlockHeaderSize)^ and IsFreeBlockFlag) = 0) then
  10077. begin
  10078. {$ifdef FullDebugMode}
  10079. if CheckBlockBeforeFreeOrRealloc(LCurPtr, boBlockCheck) then
  10080. {$endif}
  10081. begin
  10082. {$ifdef CheckCppObjectTypeEnabled}
  10083. LLeakedCppTypeIdPtr := nil;
  10084. {$endif}
  10085. {Get the leak type}
  10086. LSmallBlockLeakType := GetMemoryLeakType(LCurPtr, LSmallBlockSize);
  10087. {$ifdef LogMemoryLeakDetailToFile}
  10088. {$ifdef HideExpectedLeaksRegisteredByPointer}
  10089. if LSmallBlockLeakType <> mltExpectedLeakRegisteredByPointer then
  10090. {$endif}
  10091. LogMemoryLeakOrAllocatedBlock(LCurPtr, True);
  10092. {$endif}
  10093. {Only expected leaks?}
  10094. LExpectedLeaksOnly := LExpectedLeaksOnly and (LSmallBlockLeakType <> mltUnexpectedLeak);
  10095. {$ifdef HideExpectedLeaksRegisteredByPointer}
  10096. if LSmallBlockLeakType <> mltExpectedLeakRegisteredByPointer then
  10097. {$endif}
  10098. begin
  10099. {Get a pointer to the user data}
  10100. {$ifndef FullDebugMode}
  10101. LDataPtr := LCurPtr;
  10102. {$else}
  10103. LDataPtr := Pointer(PByte(LCurPtr) + SizeOf(TFullDebugBlockHeader));
  10104. {$endif}
  10105. {Default to an unknown block}
  10106. LClassIndex := 0;
  10107. {Get the class contained by the block}
  10108. LLeakedClass := DetectClassInstance(LDataPtr);
  10109. {Not a Delphi class? -> is it perhaps a string or C++ object type?}
  10110. if LLeakedClass = nil then
  10111. begin
  10112. {$ifdef CheckCppObjectTypeEnabled}
  10113. LLeakedCppObjectTypeId := LLeakedCppTypeIdPtr;
  10114. if (LLeakedCppObjectTypeId = nil) and (ExpectedMemoryLeaks = nil) then
  10115. begin
  10116. if Assigned(GetCppVirtObjTypeIdPtrFunc) then
  10117. begin
  10118. LLeakedCppObjectTypeId := GetCppVirtObjTypeIdPtrFunc(LDataPtr, LSmallBlockSize);
  10119. end;
  10120. end;
  10121. if Assigned(LLeakedCppObjectTypeId) then
  10122. begin
  10123. LClassIndex := 3;
  10124. while LClassIndex <= High(TLeakedClasses) do
  10125. begin
  10126. if (Pointer(LPLeakedClasses[LClassIndex].CppTypeIdPtr) = LLeakedCppObjectTypeId)
  10127. or ((LPLeakedClasses[LClassIndex].CppTypeIdPtr = nil)
  10128. and (LPLeakedClasses[LClassIndex].ClassPointer = nil)) then
  10129. begin
  10130. Break;
  10131. end;
  10132. Inc(LClassIndex);
  10133. end;
  10134. if LClassIndex <= High(TLeakedClasses) then
  10135. Pointer(LPLeakedClasses[LClassIndex].CppTypeIdPtr) := LLeakedCppObjectTypeId
  10136. else
  10137. LClassIndex := 0;
  10138. end
  10139. else
  10140. begin
  10141. {$endif}
  10142. {Not a known class: Is it perhaps string data?}
  10143. case DetectStringData(LDataPtr, APSmallBlockPool.BlockType.BlockSize - (BlockHeaderSize {$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif})) of
  10144. stAnsiString: LClassIndex := 1;
  10145. stUnicodeString: LClassIndex := 2;
  10146. end;
  10147. {$ifdef CheckCppObjectTypeEnabled}
  10148. end;
  10149. {$endif}
  10150. end
  10151. else
  10152. begin
  10153. LClassIndex := 3;
  10154. while LClassIndex <= High(TLeakedClasses) do
  10155. begin
  10156. if (LPLeakedClasses[LClassIndex].ClassPointer = LLeakedClass)
  10157. or ((LPLeakedClasses[LClassIndex].ClassPointer = nil)
  10158. {$ifdef CheckCppObjectTypeEnabled}
  10159. and (LPLeakedClasses[LClassIndex].CppTypeIdPtr = nil)
  10160. {$endif}
  10161. ) then
  10162. begin
  10163. Break;
  10164. end;
  10165. Inc(LClassIndex);
  10166. end;
  10167. if LClassIndex <= High(TLeakedClasses) then
  10168. LPLeakedClasses[LClassIndex].ClassPointer := LLeakedClass
  10169. else
  10170. LClassIndex := 0;
  10171. end;
  10172. {Add to the number of leaks for the class}
  10173. Inc(LPLeakedClasses[LClassIndex].NumLeaks);
  10174. end;
  10175. end;
  10176. end
  10177. else
  10178. begin
  10179. {$ifdef CheckUseOfFreedBlocksOnShutdown}
  10180. {Check that the block has not been modified since being freed}
  10181. CheckFreeBlockUnmodified(LCurPtr, APSmallBlockPool.BlockType.BlockSize, boBlockCheck);
  10182. {$endif}
  10183. end;
  10184. {Next block}
  10185. Inc(PByte(LCurPtr), APSmallBlockPool.BlockType.BlockSize);
  10186. end;
  10187. end;
  10188. {$endif}
  10189. begin
  10190. {$ifdef EnableMemoryLeakReporting}
  10191. {Clear the leak arrays}
  10192. FillChar(LSmallBlockLeaks, SizeOf(LSmallBlockLeaks), 0);
  10193. FillChar(LMediumAndLargeBlockLeaks, SizeOf(LMediumAndLargeBlockLeaks), 0);
  10194. {Step through all the medium block pools}
  10195. LNumMediumAndLargeLeaks := 0;
  10196. {No unexpected leaks so far}
  10197. LExpectedLeaksOnly := True;
  10198. {$endif}
  10199. {Step through all the medium block pools}
  10200. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  10201. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  10202. begin
  10203. LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
  10204. while LPMediumBlock <> nil do
  10205. begin
  10206. LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
  10207. {Is the block in use?}
  10208. if LMediumBlockHeader and IsFreeBlockFlag = 0 then
  10209. begin
  10210. {$ifdef EnableMemoryLeakReporting}
  10211. if ACheckForLeakedBlocks then
  10212. begin
  10213. if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
  10214. begin
  10215. {Get all the leaks for the small block pool}
  10216. CheckSmallBlockPoolForLeaks(LPMediumBlock);
  10217. end
  10218. else
  10219. begin
  10220. if (LNumMediumAndLargeLeaks < Length(LMediumAndLargeBlockLeaks))
  10221. {$ifdef FullDebugMode}
  10222. and CheckBlockBeforeFreeOrRealloc(LPMediumBlock, boBlockCheck)
  10223. {$endif}
  10224. then
  10225. begin
  10226. LMediumBlockSize := (LMediumBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
  10227. {$ifdef FullDebugMode}
  10228. Dec(LMediumBlockSize, FullDebugBlockOverhead);
  10229. {$endif}
  10230. {Get the leak type}
  10231. LLeakType := GetMemoryLeakType(LPMediumBlock, LMediumBlockSize);
  10232. {Is it an expected leak?}
  10233. LExpectedLeaksOnly := LExpectedLeaksOnly and (LLeakType <> mltUnexpectedLeak);
  10234. {$ifdef LogMemoryLeakDetailToFile}
  10235. {$ifdef HideExpectedLeaksRegisteredByPointer}
  10236. if LLeakType <> mltExpectedLeakRegisteredByPointer then
  10237. {$endif}
  10238. LogMemoryLeakOrAllocatedBlock(LPMediumBlock, True);
  10239. {$endif}
  10240. {$ifdef HideExpectedLeaksRegisteredByPointer}
  10241. if LLeakType <> mltExpectedLeakRegisteredByPointer then
  10242. {$endif}
  10243. begin
  10244. {Add the leak to the list}
  10245. LMediumAndLargeBlockLeaks[LNumMediumAndLargeLeaks] := LMediumBlockSize;
  10246. Inc(LNumMediumAndLargeLeaks);
  10247. end;
  10248. end;
  10249. end;
  10250. end;
  10251. {$endif}
  10252. end
  10253. else
  10254. begin
  10255. {$ifdef CheckUseOfFreedBlocksOnShutdown}
  10256. {Check that the block has not been modified since being freed}
  10257. CheckFreeBlockUnmodified(LPMediumBlock, LMediumBlockHeader and DropMediumAndLargeFlagsMask, boBlockCheck);
  10258. {$endif}
  10259. end;
  10260. {Next medium block}
  10261. LPMediumBlock := NextMediumBlock(LPMediumBlock);
  10262. end;
  10263. {Get the next medium block pool}
  10264. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  10265. end;
  10266. {$ifdef EnableMemoryLeakReporting}
  10267. if ACheckForLeakedBlocks then
  10268. begin
  10269. {Get all leaked large blocks}
  10270. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  10271. while LPLargeBlock <> @LargeBlocksCircularList do
  10272. begin
  10273. if (LNumMediumAndLargeLeaks < length(LMediumAndLargeBlockLeaks))
  10274. {$ifdef FullDebugMode}
  10275. and CheckBlockBeforeFreeOrRealloc(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), boBlockCheck)
  10276. {$endif}
  10277. then
  10278. begin
  10279. LLargeBlockSize := (LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask) - BlockHeaderSize - LargeBlockHeaderSize;
  10280. {$ifdef FullDebugMode}
  10281. Dec(LLargeBlockSize, FullDebugBlockOverhead);
  10282. {$endif}
  10283. {Get the leak type}
  10284. LLeakType := GetMemoryLeakType(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), LLargeBlockSize);
  10285. {Is it an expected leak?}
  10286. LExpectedLeaksOnly := LExpectedLeaksOnly and (LLeakType <> mltUnexpectedLeak);
  10287. {$ifdef LogMemoryLeakDetailToFile}
  10288. {$ifdef HideExpectedLeaksRegisteredByPointer}
  10289. if LLeakType <> mltExpectedLeakRegisteredByPointer then
  10290. {$endif}
  10291. LogMemoryLeakOrAllocatedBlock(Pointer(PByte(LPLargeBlock) + LargeBlockHeaderSize), True);
  10292. {$endif}
  10293. {$ifdef HideExpectedLeaksRegisteredByPointer}
  10294. if LLeakType <> mltExpectedLeakRegisteredByPointer then
  10295. {$endif}
  10296. begin
  10297. {Add the leak}
  10298. LMediumAndLargeBlockLeaks[LNumMediumAndLargeLeaks] := LLargeBlockSize;
  10299. Inc(LNumMediumAndLargeLeaks);
  10300. end;
  10301. end;
  10302. {Get the next large block}
  10303. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  10304. end;
  10305. {Display the leak message if required}
  10306. if not LExpectedLeaksOnly then
  10307. begin
  10308. {Small leak header has not been added}
  10309. LSmallLeakHeaderAdded := False;
  10310. LPreviousBlockSize := 0;
  10311. {Set up the leak message header so long}
  10312. LMsgPtr := AppendStringToBuffer(LeakMessageHeader, @LLeakMessage[0], length(LeakMessageHeader));
  10313. {Step through all the small block types}
  10314. for LBlockTypeInd := 0 to NumSmallBlockTypes - 1 do
  10315. begin
  10316. LThisBlockSize := SmallBlockTypes[LBlockTypeInd].BlockSize - BlockHeaderSize;
  10317. {$ifdef FullDebugMode}
  10318. Dec(LThisBlockSize, FullDebugBlockOverhead);
  10319. if NativeInt(LThisBlockSize) < 0 then
  10320. LThisBlockSize := 0;
  10321. {$endif}
  10322. LBlockSizeHeaderAdded := False;
  10323. {Any leaks?}
  10324. for LClassInd := High(LSmallBlockLeaks[LBlockTypeInd]) downto 0 do
  10325. begin
  10326. {Is there still space in the message buffer? Reserve space for the message
  10327. footer.}
  10328. if LMsgPtr > @LLeakMessage[High(LLeakMessage) - 2048] then
  10329. Break;
  10330. {Check the count}
  10331. if LSmallBlockLeaks[LBlockTypeInd][LClassInd].NumLeaks > 0 then
  10332. begin
  10333. {Need to add the header?}
  10334. if not LSmallLeakHeaderAdded then
  10335. begin
  10336. LMsgPtr := AppendStringToBuffer(SmallLeakDetail, LMsgPtr, Length(SmallLeakDetail));
  10337. LSmallLeakHeaderAdded := True;
  10338. end;
  10339. {Need to add the size header?}
  10340. if not LBlockSizeHeaderAdded then
  10341. begin
  10342. LMsgPtr^ := #13;
  10343. Inc(LMsgPtr);
  10344. LMsgPtr^ := #10;
  10345. Inc(LMsgPtr);
  10346. LMsgPtr := NativeUIntToStrBuf(LPreviousBlockSize + 1, LMsgPtr);
  10347. LMsgPtr^ := ' ';
  10348. Inc(LMsgPtr);
  10349. LMsgPtr^ := '-';
  10350. Inc(LMsgPtr);
  10351. LMsgPtr^ := ' ';
  10352. Inc(LMsgPtr);
  10353. LMsgPtr := NativeUIntToStrBuf(LThisBlockSize, LMsgPtr);
  10354. LMsgPtr := AppendStringToBuffer(BytesMessage, LMsgPtr, Length(BytesMessage));
  10355. LBlockSizeHeaderAdded := True;
  10356. end
  10357. else
  10358. begin
  10359. LMsgPtr^ := ',';
  10360. Inc(LMsgPtr);
  10361. LMsgPtr^ := ' ';
  10362. Inc(LMsgPtr);
  10363. end;
  10364. {Show the count}
  10365. case LClassInd of
  10366. {Unknown}
  10367. 0:
  10368. begin
  10369. LMsgPtr := AppendStringToBuffer(UnknownClassNameMsg, LMsgPtr, Length(UnknownClassNameMsg));
  10370. end;
  10371. {AnsiString}
  10372. 1:
  10373. begin
  10374. LMsgPtr := AppendStringToBuffer(AnsiStringBlockMessage, LMsgPtr, Length(AnsiStringBlockMessage));
  10375. end;
  10376. {UnicodeString}
  10377. 2:
  10378. begin
  10379. LMsgPtr := AppendStringToBuffer(UnicodeStringBlockMessage, LMsgPtr, Length(UnicodeStringBlockMessage));
  10380. end;
  10381. {Classes}
  10382. else
  10383. begin
  10384. {$ifdef CheckCppObjectTypeEnabled}
  10385. if LSmallBlockLeaks[LBlockTypeInd][LClassInd].CppTypeIdPtr <> nil then
  10386. begin
  10387. if Assigned(GetCppVirtObjTypeNameByTypeIdPtrFunc) then
  10388. begin
  10389. LCppTypeName := GetCppVirtObjTypeNameByTypeIdPtrFunc(LSmallBlockLeaks[LBlockTypeInd][LClassInd].CppTypeIdPtr);
  10390. LMsgPtr := AppendStringToBuffer(LCppTypeName, LMsgPtr, StrLen(LCppTypeName));
  10391. end
  10392. else
  10393. LMsgPtr := AppendClassNameToBuffer(nil, LMsgPtr);
  10394. end
  10395. else
  10396. begin
  10397. {$endif}
  10398. LMsgPtr := AppendClassNameToBuffer(LSmallBlockLeaks[LBlockTypeInd][LClassInd].ClassPointer, LMsgPtr);
  10399. {$ifdef CheckCppObjectTypeEnabled}
  10400. end;
  10401. {$endif}
  10402. end;
  10403. end;
  10404. {Add the count}
  10405. LMsgPtr^ := ' ';
  10406. Inc(LMsgPtr);
  10407. LMsgPtr^ := 'x';
  10408. Inc(LMsgPtr);
  10409. LMsgPtr^ := ' ';
  10410. Inc(LMsgPtr);
  10411. LMsgPtr := NativeUIntToStrBuf(LSmallBlockLeaks[LBlockTypeInd][LClassInd].NumLeaks, LMsgPtr);
  10412. end;
  10413. end;
  10414. LPreviousBlockSize := LThisBlockSize;
  10415. end;
  10416. {Add the medium/large block leak message}
  10417. if LNumMediumAndLargeLeaks > 0 then
  10418. begin
  10419. {Any non-small leaks?}
  10420. if LSmallLeakHeaderAdded then
  10421. begin
  10422. LMsgPtr^ := #13;
  10423. Inc(LMsgPtr);
  10424. LMsgPtr^ := #10;
  10425. Inc(LMsgPtr);
  10426. LMsgPtr^ := #13;
  10427. Inc(LMsgPtr);
  10428. LMsgPtr^ := #10;
  10429. Inc(LMsgPtr);
  10430. end;
  10431. {Add the medium/large block leak message}
  10432. LMsgPtr := AppendStringToBuffer(LargeLeakDetail, LMsgPtr, Length(LargeLeakDetail));
  10433. {List all the blocks}
  10434. for LBlockInd := 0 to LNumMediumAndLargeLeaks - 1 do
  10435. begin
  10436. if LBlockInd <> 0 then
  10437. begin
  10438. LMsgPtr^ := ',';
  10439. Inc(LMsgPtr);
  10440. LMsgPtr^ := ' ';
  10441. Inc(LMsgPtr);
  10442. end;
  10443. LMsgPtr := NativeUIntToStrBuf(LMediumAndLargeBlockLeaks[LBlockInd], LMsgPtr);
  10444. {Is there still space in the message buffer? Reserve space for the
  10445. message footer.}
  10446. if LMsgPtr > @LLeakMessage[High(LLeakMessage) - 2048] then
  10447. Break;
  10448. end;
  10449. end;
  10450. {$ifdef LogErrorsToFile}
  10451. {Set the message footer}
  10452. LMsgPtr := AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
  10453. {Append the message to the memory errors file}
  10454. AppendEventLog(@LLeakMessage[0], UIntPtr(LMsgPtr) - UIntPtr(@LLeakMessage[1]));
  10455. {$else}
  10456. {Set the message footer}
  10457. AppendStringToBuffer(LeakMessageFooter, LMsgPtr, Length(LeakMessageFooter));
  10458. {$endif}
  10459. {$ifdef UseOutputDebugString}
  10460. OutputDebugStringA(LLeakMessage);
  10461. {$endif}
  10462. {$ifndef NoMessageBoxes}
  10463. {Show the message}
  10464. AppendStringToModuleName(LeakMessageTitle, LMessageTitleBuffer);
  10465. ShowMessageBox(LLeakMessage, LMessageTitleBuffer);
  10466. {$endif}
  10467. end;
  10468. end;
  10469. {$endif}
  10470. end;
  10471. {Returns statistics about the current state of the memory manager}
  10472. procedure GetMemoryManagerState(var AMemoryManagerState: TMemoryManagerState);
  10473. var
  10474. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  10475. LPMediumBlock: Pointer;
  10476. LInd: Integer;
  10477. LBlockTypeIndex, LMediumBlockSize: Cardinal;
  10478. LMediumBlockHeader, LLargeBlockSize: NativeUInt;
  10479. LPLargeBlock: PLargeBlockHeader;
  10480. begin
  10481. {Clear the structure}
  10482. FillChar(AMemoryManagerState, SizeOf(AMemoryManagerState), 0);
  10483. {Set the small block size stats}
  10484. for LInd := 0 to NumSmallBlockTypes - 1 do
  10485. begin
  10486. AMemoryManagerState.SmallBlockTypeStates[LInd].InternalBlockSize :=
  10487. SmallBlockTypes[LInd].BlockSize;
  10488. AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize :=
  10489. SmallBlockTypes[LInd].BlockSize - BlockHeaderSize{$ifdef FullDebugMode} - FullDebugBlockOverhead{$endif};
  10490. if NativeInt(AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize) < 0 then
  10491. AMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize := 0;
  10492. end;
  10493. {Lock all small block types}
  10494. LockAllSmallBlockTypes;
  10495. {Lock the medium blocks}
  10496. LockMediumBlocks;
  10497. {Step through all the medium block pools}
  10498. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  10499. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  10500. begin
  10501. {Add to the medium block used space}
  10502. Inc(AMemoryManagerState.ReservedMediumBlockAddressSpace, MediumBlockPoolSize);
  10503. LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
  10504. while LPMediumBlock <> nil do
  10505. begin
  10506. LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
  10507. {Is the block in use?}
  10508. if LMediumBlockHeader and IsFreeBlockFlag = 0 then
  10509. begin
  10510. {Get the block size}
  10511. LMediumBlockSize := LMediumBlockHeader and DropMediumAndLargeFlagsMask;
  10512. if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
  10513. begin
  10514. {Get the block type index}
  10515. LBlockTypeIndex := (UIntPtr(PSmallBlockPoolHeader(LPMediumBlock).BlockType) - UIntPtr(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
  10516. {Subtract from medium block usage}
  10517. Dec(AMemoryManagerState.ReservedMediumBlockAddressSpace, LMediumBlockSize);
  10518. {Add it to the reserved space for the block size}
  10519. Inc(AMemoryManagerState.SmallBlockTypeStates[LBlockTypeIndex].ReservedAddressSpace, LMediumBlockSize);
  10520. {Add the usage for the pool}
  10521. Inc(AMemoryManagerState.SmallBlockTypeStates[LBlockTypeIndex].AllocatedBlockCount,
  10522. PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse);
  10523. end
  10524. else
  10525. begin
  10526. {$ifdef FullDebugMode}
  10527. Dec(LMediumBlockSize, FullDebugBlockOverhead);
  10528. {$endif}
  10529. Inc(AMemoryManagerState.AllocatedMediumBlockCount);
  10530. Inc(AMemoryManagerState.TotalAllocatedMediumBlockSize, LMediumBlockSize - BlockHeaderSize);
  10531. end;
  10532. end;
  10533. {Next medium block}
  10534. LPMediumBlock := NextMediumBlock(LPMediumBlock);
  10535. end;
  10536. {Get the next medium block pool}
  10537. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  10538. end;
  10539. {Unlock medium blocks}
  10540. MediumBlocksLocked := False;
  10541. {Unlock all the small block types}
  10542. for LInd := 0 to NumSmallBlockTypes - 1 do
  10543. SmallBlockTypes[LInd].BlockTypeLocked := False;
  10544. {Step through all the large blocks}
  10545. LockLargeBlocks;
  10546. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  10547. while LPLargeBlock <> @LargeBlocksCircularList do
  10548. begin
  10549. LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  10550. Inc(AMemoryManagerState.AllocatedLargeBlockCount);
  10551. Inc(AMemoryManagerState.ReservedLargeBlockAddressSpace, LLargeBlockSize);
  10552. Inc(AMemoryManagerState.TotalAllocatedLargeBlockSize, LPLargeBlock.UserAllocatedSize);
  10553. {Get the next large block}
  10554. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  10555. end;
  10556. LargeBlocksLocked := False;
  10557. end;
  10558. {Returns a summary of the information returned by GetMemoryManagerState}
  10559. procedure GetMemoryManagerUsageSummary(
  10560. var AMemoryManagerUsageSummary: TMemoryManagerUsageSummary);
  10561. var
  10562. LMMS: TMemoryManagerState;
  10563. LAllocatedBytes, LReservedBytes: NativeUInt;
  10564. LSBTIndex: Integer;
  10565. begin
  10566. {Get the memory manager state}
  10567. GetMemoryManagerState(LMMS);
  10568. {Add up the totals}
  10569. LAllocatedBytes := LMMS.TotalAllocatedMediumBlockSize
  10570. + LMMS.TotalAllocatedLargeBlockSize;
  10571. LReservedBytes := LMMS.ReservedMediumBlockAddressSpace
  10572. + LMMS.ReservedLargeBlockAddressSpace;
  10573. for LSBTIndex := 0 to NumSmallBlockTypes - 1 do
  10574. begin
  10575. Inc(LAllocatedBytes, LMMS.SmallBlockTypeStates[LSBTIndex].UseableBlockSize
  10576. * LMMS.SmallBlockTypeStates[LSBTIndex].AllocatedBlockCount);
  10577. Inc(LReservedBytes, LMMS.SmallBlockTypeStates[LSBTIndex].ReservedAddressSpace);
  10578. end;
  10579. {Set the structure values}
  10580. AMemoryManagerUsageSummary.AllocatedBytes := LAllocatedBytes;
  10581. AMemoryManagerUsageSummary.OverheadBytes := LReservedBytes - LAllocatedBytes;
  10582. if LReservedBytes > 0 then
  10583. begin
  10584. AMemoryManagerUsageSummary.EfficiencyPercentage :=
  10585. LAllocatedBytes / LReservedBytes * 100;
  10586. end
  10587. else
  10588. AMemoryManagerUsageSummary.EfficiencyPercentage := 100;
  10589. end;
  10590. {$ifndef POSIX}
  10591. {Gets the state of every 64K block in the 4GB address space. Under 64-bit this
  10592. returns only the state for the low 4GB.}
  10593. procedure GetMemoryMap(var AMemoryMap: TMemoryMap);
  10594. var
  10595. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  10596. LPLargeBlock: PLargeBlockHeader;
  10597. LInd, LChunkIndex, LNextChunk, LLargeBlockSize: NativeUInt;
  10598. LMBI: TMemoryBasicInformation;
  10599. begin
  10600. {Clear the map}
  10601. FillChar(AMemoryMap, SizeOf(AMemoryMap), Ord(csUnallocated));
  10602. {Step through all the medium block pools}
  10603. LockMediumBlocks;
  10604. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  10605. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  10606. begin
  10607. {Add to the medium block used space}
  10608. LChunkIndex := NativeUInt(LPMediumBlockPoolHeader) shr 16;
  10609. for LInd := 0 to (MediumBlockPoolSize - 1) shr 16 do
  10610. begin
  10611. if (LChunkIndex + LInd) > High(AMemoryMap) then
  10612. Break;
  10613. AMemoryMap[LChunkIndex + LInd] := csAllocated;
  10614. end;
  10615. {Get the next medium block pool}
  10616. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  10617. end;
  10618. MediumBlocksLocked := False;
  10619. {Step through all the large blocks}
  10620. LockLargeBlocks;
  10621. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  10622. while LPLargeBlock <> @LargeBlocksCircularList do
  10623. begin
  10624. LChunkIndex := UIntPtr(LPLargeBlock) shr 16;
  10625. LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  10626. for LInd := 0 to (LLargeBlockSize - 1) shr 16 do
  10627. begin
  10628. if (LChunkIndex + LInd) > High(AMemoryMap) then
  10629. Break;
  10630. AMemoryMap[LChunkIndex + LInd] := csAllocated;
  10631. end;
  10632. {Get the next large block}
  10633. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  10634. end;
  10635. LargeBlocksLocked := False;
  10636. {Fill in the rest of the map}
  10637. LInd := 0;
  10638. while LInd <= 65535 do
  10639. begin
  10640. {If the chunk is not allocated by this MM, what is its status?}
  10641. if AMemoryMap[LInd] = csUnallocated then
  10642. begin
  10643. {Query the address space starting at the chunk boundary}
  10644. if VirtualQuery(Pointer(LInd * 65536), LMBI, SizeOf(LMBI)) = 0 then
  10645. begin
  10646. {VirtualQuery may fail for addresses >2GB if a large address space is
  10647. not enabled.}
  10648. FillChar(AMemoryMap[LInd], 65536 - LInd, csSysReserved);
  10649. Break;
  10650. end;
  10651. {Get the chunk number after the region}
  10652. LNextChunk := (LMBI.RegionSize - 1) shr 16 + LInd + 1;
  10653. {Validate}
  10654. if LNextChunk > 65536 then
  10655. LNextChunk := 65536;
  10656. {Set the status of all the chunks in the region}
  10657. if LMBI.State = MEM_COMMIT then
  10658. begin
  10659. FillChar(AMemoryMap[LInd], LNextChunk - LInd, csSysAllocated);
  10660. end
  10661. else
  10662. begin
  10663. if LMBI.State = MEM_RESERVE then
  10664. FillChar(AMemoryMap[LInd], LNextChunk - LInd, csSysReserved);
  10665. end;
  10666. {Point to the start of the next chunk}
  10667. LInd := LNextChunk;
  10668. end
  10669. else
  10670. begin
  10671. {Next chunk}
  10672. Inc(LInd);
  10673. end;
  10674. end;
  10675. end;
  10676. {$endif}
  10677. {Returns summarised information about the state of the memory manager. (For
  10678. backward compatibility.)}
  10679. function FastGetHeapStatus: THeapStatus;
  10680. var
  10681. LPMediumBlockPoolHeader: PMediumBlockPoolHeader;
  10682. LPMediumBlock: Pointer;
  10683. LBlockTypeIndex, LMediumBlockSize: Cardinal;
  10684. LSmallBlockUsage, LSmallBlockOverhead, LMediumBlockHeader, LLargeBlockSize: NativeUInt;
  10685. LInd: Integer;
  10686. LPLargeBlock: PLargeBlockHeader;
  10687. begin
  10688. {Clear the structure}
  10689. FillChar(Result, SizeOf(Result), 0);
  10690. {Lock all small block types}
  10691. LockAllSmallBlockTypes;
  10692. {Lock the medium blocks}
  10693. LockMediumBlocks;
  10694. {Step through all the medium block pools}
  10695. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  10696. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  10697. begin
  10698. {Add to the total and committed address space}
  10699. Inc(Result.TotalAddrSpace, ((MediumBlockPoolSize + $ffff) and $ffff0000));
  10700. Inc(Result.TotalCommitted, ((MediumBlockPoolSize + $ffff) and $ffff0000));
  10701. {Add the medium block pool overhead}
  10702. Inc(Result.Overhead, (((MediumBlockPoolSize + $ffff) and $ffff0000)
  10703. - MediumBlockPoolSize + MediumBlockPoolHeaderSize));
  10704. {Get the first medium block in the pool}
  10705. LPMediumBlock := GetFirstMediumBlockInPool(LPMediumBlockPoolHeader);
  10706. while LPMediumBlock <> nil do
  10707. begin
  10708. {Get the block header}
  10709. LMediumBlockHeader := PNativeUInt(PByte(LPMediumBlock) - BlockHeaderSize)^;
  10710. {Get the block size}
  10711. LMediumBlockSize := LMediumBlockHeader and DropMediumAndLargeFlagsMask;
  10712. {Is the block in use?}
  10713. if LMediumBlockHeader and IsFreeBlockFlag = 0 then
  10714. begin
  10715. if (LMediumBlockHeader and IsSmallBlockPoolInUseFlag) <> 0 then
  10716. begin
  10717. {Get the block type index}
  10718. LBlockTypeIndex := (UIntPtr(PSmallBlockPoolHeader(LPMediumBlock).BlockType) - UIntPtr(@SmallBlockTypes[0])) div SizeOf(TSmallBlockType);
  10719. {Get the usage in the block}
  10720. LSmallBlockUsage := PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse
  10721. * SmallBlockTypes[LBlockTypeIndex].BlockSize;
  10722. {Get the total overhead for all the small blocks}
  10723. LSmallBlockOverhead := PSmallBlockPoolHeader(LPMediumBlock).BlocksInUse
  10724. * (BlockHeaderSize{$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif});
  10725. {Add to the totals}
  10726. Inc(Result.FreeSmall, LMediumBlockSize - LSmallBlockUsage - BlockHeaderSize);
  10727. Inc(Result.Overhead, LSmallBlockOverhead + BlockHeaderSize);
  10728. Inc(Result.TotalAllocated, LSmallBlockUsage - LSmallBlockOverhead);
  10729. end
  10730. else
  10731. begin
  10732. {$ifdef FullDebugMode}
  10733. Dec(LMediumBlockSize, FullDebugBlockOverhead);
  10734. Inc(Result.Overhead, FullDebugBlockOverhead);
  10735. {$endif}
  10736. {Add to the result}
  10737. Inc(Result.TotalAllocated, LMediumBlockSize - BlockHeaderSize);
  10738. Inc(Result.Overhead, BlockHeaderSize);
  10739. end;
  10740. end
  10741. else
  10742. begin
  10743. {The medium block is free}
  10744. Inc(Result.FreeBig, LMediumBlockSize);
  10745. end;
  10746. {Next medium block}
  10747. LPMediumBlock := NextMediumBlock(LPMediumBlock);
  10748. end;
  10749. {Get the next medium block pool}
  10750. LPMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  10751. end;
  10752. {Add the sequential feed unused space}
  10753. Inc(Result.Unused, MediumSequentialFeedBytesLeft);
  10754. {Unlock the medium blocks}
  10755. MediumBlocksLocked := False;
  10756. {Unlock all the small block types}
  10757. for LInd := 0 to NumSmallBlockTypes - 1 do
  10758. SmallBlockTypes[LInd].BlockTypeLocked := False;
  10759. {Step through all the large blocks}
  10760. LockLargeBlocks;
  10761. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  10762. while LPLargeBlock <> @LargeBlocksCircularList do
  10763. begin
  10764. LLargeBlockSize := LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask;
  10765. Inc(Result.TotalAddrSpace, LLargeBlockSize);
  10766. Inc(Result.TotalCommitted, LLargeBlockSize);
  10767. Inc(Result.TotalAllocated, LPLargeBlock.UserAllocatedSize
  10768. {$ifdef FullDebugMode} - FullDebugBlockOverhead{$endif});
  10769. Inc(Result.Overhead, LLargeBlockSize - LPLargeBlock.UserAllocatedSize
  10770. {$ifdef FullDebugMode} + FullDebugBlockOverhead{$endif});
  10771. {Get the next large block}
  10772. LPLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  10773. end;
  10774. LargeBlocksLocked := False;
  10775. {Set the total number of free bytes}
  10776. Result.TotalFree := Result.FreeSmall + Result.FreeBig + Result.Unused;
  10777. end;
  10778. {Frees all allocated memory. Does not support segmented large blocks (yet).}
  10779. procedure FreeAllMemory;
  10780. var
  10781. LPMediumBlockPoolHeader, LPNextMediumBlockPoolHeader: PMediumBlockPoolHeader;
  10782. LPMediumFreeBlock: PMediumFreeBlock;
  10783. LPLargeBlock, LPNextLargeBlock: PLargeBlockHeader;
  10784. LInd: Integer;
  10785. begin
  10786. {Free all block pools}
  10787. LPMediumBlockPoolHeader := MediumBlockPoolsCircularList.NextMediumBlockPoolHeader;
  10788. while LPMediumBlockPoolHeader <> @MediumBlockPoolsCircularList do
  10789. begin
  10790. {Get the next medium block pool so long}
  10791. LPNextMediumBlockPoolHeader := LPMediumBlockPoolHeader.NextMediumBlockPoolHeader;
  10792. {$ifdef ClearMediumBlockPoolsBeforeReturningToOS}
  10793. FillChar(LPMediumBlockPoolHeader^, MediumBlockPoolSize, 0);
  10794. {$else}
  10795. {$ifdef ClearSmallAndMediumBlocksInFreeMem}
  10796. FillChar(LPMediumBlockPoolHeader^, MediumBlockPoolSize, 0);
  10797. {$endif}
  10798. {$endif}
  10799. {Free this pool}
  10800. VirtualFree(LPMediumBlockPoolHeader, 0, MEM_RELEASE);
  10801. {Next pool}
  10802. LPMediumBlockPoolHeader := LPNextMediumBlockPoolHeader;
  10803. end;
  10804. {Clear all small block types}
  10805. for LInd := 0 to High(SmallBlockTypes) do
  10806. begin
  10807. SmallBlockTypes[Lind].PreviousPartiallyFreePool := @SmallBlockTypes[Lind];
  10808. SmallBlockTypes[Lind].NextPartiallyFreePool := @SmallBlockTypes[Lind];
  10809. SmallBlockTypes[Lind].NextSequentialFeedBlockAddress := Pointer(1);
  10810. SmallBlockTypes[Lind].MaxSequentialFeedBlockAddress := nil;
  10811. end;
  10812. {Clear all medium block pools}
  10813. MediumBlockPoolsCircularList.PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  10814. MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  10815. {All medium bins are empty}
  10816. for LInd := 0 to High(MediumBlockBins) do
  10817. begin
  10818. LPMediumFreeBlock := @MediumBlockBins[LInd];
  10819. LPMediumFreeBlock.PreviousFreeBlock := LPMediumFreeBlock;
  10820. LPMediumFreeBlock.NextFreeBlock := LPMediumFreeBlock;
  10821. end;
  10822. MediumBlockBinGroupBitmap := 0;
  10823. FillChar(MediumBlockBinBitmaps, SizeOf(MediumBlockBinBitmaps), 0);
  10824. MediumSequentialFeedBytesLeft := 0;
  10825. {Free all large blocks}
  10826. LPLargeBlock := LargeBlocksCircularList.NextLargeBlockHeader;
  10827. while LPLargeBlock <> @LargeBlocksCircularList do
  10828. begin
  10829. {Get the next large block}
  10830. LPNextLargeBlock := LPLargeBlock.NextLargeBlockHeader;
  10831. {$ifdef ClearLargeBlocksBeforeReturningToOS}
  10832. FillChar(LPLargeBlock^,
  10833. LPLargeBlock.BlockSizeAndFlags and DropMediumAndLargeFlagsMask, 0);
  10834. {$endif}
  10835. {Free this large block}
  10836. VirtualFree(LPLargeBlock, 0, MEM_RELEASE);
  10837. {Next large block}
  10838. LPLargeBlock := LPNextLargeBlock;
  10839. end;
  10840. {There are no large blocks allocated}
  10841. LargeBlocksCircularList.PreviousLargeBlockHeader := @LargeBlocksCircularList;
  10842. LargeBlocksCircularList.NextLargeBlockHeader := @LargeBlocksCircularList;
  10843. end;
  10844. {----------------------------Memory Manager Setup-----------------------------}
  10845. {Checks that no other memory manager has been installed after the RTL MM and
  10846. that there are currently no live pointers allocated through the RTL MM.}
  10847. function CheckCanInstallMemoryManager: Boolean;
  10848. {$ifndef NoMessageBoxes}
  10849. var
  10850. LErrorMessageTitle: array[0..1023] of AnsiChar;
  10851. {$endif}
  10852. begin
  10853. {Default to error}
  10854. Result := False;
  10855. {$ifdef FullDebugMode}
  10856. {$ifdef LoadDebugDLLDynamically}
  10857. {$ifdef DoNotInstallIfDLLMissing}
  10858. {Should FastMM be installed only if the FastMM_FullDebugMode.dll file is
  10859. available?}
  10860. if FullDebugModeDLL = 0 then
  10861. Exit;
  10862. {$endif}
  10863. {$endif}
  10864. {$endif}
  10865. {Is FastMM already installed?}
  10866. if FastMMIsInstalled then
  10867. begin
  10868. {$ifdef UseOutputDebugString}
  10869. OutputDebugStringA(AlreadyInstalledMsg);
  10870. {$endif}
  10871. {$ifndef NoMessageBoxes}
  10872. AppendStringToModuleName(AlreadyInstalledTitle, LErrorMessageTitle);
  10873. ShowMessageBox(AlreadyInstalledMsg, LErrorMessageTitle);
  10874. {$endif}
  10875. Exit;
  10876. end;
  10877. {Has another MM been set, or has the Embarcadero MM been used? If so, this
  10878. file is not the first unit in the uses clause of the project's .dpr file.}
  10879. if IsMemoryManagerSet then
  10880. begin
  10881. {When using runtime packages, another library may already have installed
  10882. FastMM: Silently ignore the installation request.}
  10883. {$ifndef UseRuntimePackages}
  10884. {Another memory manager has been set.}
  10885. {$ifdef UseOutputDebugString}
  10886. OutputDebugStringA(OtherMMInstalledMsg);
  10887. {$endif}
  10888. {$ifndef NoMessageBoxes}
  10889. AppendStringToModuleName(OtherMMInstalledTitle, LErrorMessageTitle);
  10890. ShowMessageBox(OtherMMInstalledMsg, LErrorMessageTitle);
  10891. {$endif}
  10892. {$endif}
  10893. Exit;
  10894. end;
  10895. {$ifndef POSIX}
  10896. if GetHeapStatus.TotalAllocated <> 0 then
  10897. begin
  10898. {Memory has been already been allocated with the RTL MM}
  10899. {$ifdef UseOutputDebugString}
  10900. OutputDebugStringA(MemoryAllocatedMsg);
  10901. {$endif}
  10902. {$ifndef NoMessageBoxes}
  10903. AppendStringToModuleName(MemoryAllocatedTitle, LErrorMessageTitle);
  10904. ShowMessageBox(MemoryAllocatedMsg, LErrorMessageTitle);
  10905. {$endif}
  10906. Exit;
  10907. end;
  10908. {$endif}
  10909. {All OK}
  10910. Result := True;
  10911. end;
  10912. {Initializes the lookup tables for the memory manager}
  10913. procedure InitializeMemoryManager;
  10914. const
  10915. {The size of the Inc(VMTIndex) code in TFreedObject.GetVirtualMethodIndex}
  10916. VMTIndexIncCodeSize = 6;
  10917. var
  10918. LInd, LSizeInd, LMinimumPoolSize, LOptimalPoolSize, LGroupNumber,
  10919. LBlocksPerPool, LPreviousBlockSize: Cardinal;
  10920. LPMediumFreeBlock: PMediumFreeBlock;
  10921. begin
  10922. {$ifdef FullDebugMode}
  10923. {$ifdef LoadDebugDLLDynamically}
  10924. {Attempt to load the FullDebugMode DLL dynamically.}
  10925. FullDebugModeDLL := LoadLibrary(FullDebugModeLibraryName);
  10926. if FullDebugModeDLL <> 0 then
  10927. begin
  10928. GetStackTrace := GetProcAddress(FullDebugModeDLL,
  10929. {$ifdef RawStackTraces}'GetRawStackTrace'{$else}'GetFrameBasedStackTrace'{$endif});
  10930. LogStackTrace := GetProcAddress(FullDebugModeDLL, 'LogStackTrace');
  10931. end;
  10932. {$endif}
  10933. {$endif}
  10934. {$ifdef EnableMMX}
  10935. {$ifndef ForceMMX}
  10936. UseMMX := MMX_Supported;
  10937. {$endif}
  10938. {$endif}
  10939. {Initialize the memory manager}
  10940. {-------------Set up the small block types-------------}
  10941. LPreviousBlockSize := 0;
  10942. for LInd := 0 to High(SmallBlockTypes) do
  10943. begin
  10944. {Set the move procedure}
  10945. {$ifdef UseCustomFixedSizeMoveRoutines}
  10946. {The upsize move procedure may move chunks in 16 bytes even with 8-byte
  10947. alignment, since the new size will always be at least 8 bytes bigger than
  10948. the old size.}
  10949. if not Assigned(SmallBlockTypes[LInd].UpsizeMoveProcedure) then
  10950. {$ifdef UseCustomVariableSizeMoveRoutines}
  10951. SmallBlockTypes[LInd].UpsizeMoveProcedure := MoveX16LP;
  10952. {$else}
  10953. SmallBlockTypes[LInd].UpsizeMoveProcedure := @System.Move;
  10954. {$endif}
  10955. {$endif}
  10956. {Set the first "available pool" to the block type itself, so that the
  10957. allocation routines know that there are currently no pools with free
  10958. blocks of this size.}
  10959. SmallBlockTypes[LInd].PreviousPartiallyFreePool := @SmallBlockTypes[LInd];
  10960. SmallBlockTypes[LInd].NextPartiallyFreePool := @SmallBlockTypes[LInd];
  10961. {Set the block size to block type index translation table}
  10962. for LSizeInd := (LPreviousBlockSize div SmallBlockGranularity) to ((SmallBlockTypes[LInd].BlockSize - 1) div SmallBlockGranularity) do
  10963. AllocSize2SmallBlockTypeIndX4[LSizeInd] := LInd * 4;
  10964. {Cannot sequential feed yet: Ensure that the next address is greater than
  10965. the maximum address}
  10966. SmallBlockTypes[LInd].MaxSequentialFeedBlockAddress := Pointer(0);
  10967. SmallBlockTypes[LInd].NextSequentialFeedBlockAddress := Pointer(1);
  10968. {Get the mask to use for finding a medium block suitable for a block pool}
  10969. LMinimumPoolSize :=
  10970. ((SmallBlockTypes[LInd].BlockSize * MinimumSmallBlocksPerPool
  10971. + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)
  10972. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  10973. if LMinimumPoolSize < MinimumMediumBlockSize then
  10974. LMinimumPoolSize := MinimumMediumBlockSize;
  10975. {Get the closest group number for the minimum pool size}
  10976. LGroupNumber := (LMinimumPoolSize - MinimumMediumBlockSize + MediumBlockBinsPerGroup * MediumBlockGranularity div 2)
  10977. div (MediumBlockBinsPerGroup * MediumBlockGranularity);
  10978. {Too large?}
  10979. if LGroupNumber > 7 then
  10980. LGroupNumber := 7;
  10981. {Set the bitmap}
  10982. SmallBlockTypes[LInd].AllowedGroupsForBlockPoolBitmap := Byte(-(1 shl LGroupNumber));
  10983. {Set the minimum pool size}
  10984. SmallBlockTypes[LInd].MinimumBlockPoolSize := MinimumMediumBlockSize + LGroupNumber * (MediumBlockBinsPerGroup * MediumBlockGranularity);
  10985. {Get the optimal block pool size}
  10986. LOptimalPoolSize := ((SmallBlockTypes[LInd].BlockSize * TargetSmallBlocksPerPool
  10987. + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset)
  10988. and -MediumBlockGranularity) + MediumBlockSizeOffset;
  10989. {Limit the optimal pool size to within range}
  10990. if LOptimalPoolSize < OptimalSmallBlockPoolSizeLowerLimit then
  10991. LOptimalPoolSize := OptimalSmallBlockPoolSizeLowerLimit;
  10992. if LOptimalPoolSize > OptimalSmallBlockPoolSizeUpperLimit then
  10993. LOptimalPoolSize := OptimalSmallBlockPoolSizeUpperLimit;
  10994. {How many blocks will fit in the adjusted optimal size?}
  10995. LBlocksPerPool := (LOptimalPoolSize - SmallBlockPoolHeaderSize) div SmallBlockTypes[LInd].BlockSize;
  10996. {Recalculate the optimal pool size to minimize wastage due to a partial
  10997. last block.}
  10998. SmallBlockTypes[LInd].OptimalBlockPoolSize :=
  10999. ((LBlocksPerPool * SmallBlockTypes[LInd].BlockSize + SmallBlockPoolHeaderSize + MediumBlockGranularity - 1 - MediumBlockSizeOffset) and -MediumBlockGranularity) + MediumBlockSizeOffset;
  11000. {$ifdef CheckHeapForCorruption}
  11001. {Debug checks}
  11002. if (SmallBlockTypes[LInd].OptimalBlockPoolSize < MinimumMediumBlockSize)
  11003. or (SmallBlockTypes[LInd].BlockSize div SmallBlockGranularity * SmallBlockGranularity <> SmallBlockTypes[LInd].BlockSize) then
  11004. begin
  11005. {$ifdef BCB6OrDelphi7AndUp}
  11006. System.Error(reInvalidPtr);
  11007. {$else}
  11008. System.RunError(reInvalidPtr);
  11009. {$endif}
  11010. end;
  11011. {$endif}
  11012. {Set the previous small block size}
  11013. LPreviousBlockSize := SmallBlockTypes[LInd].BlockSize;
  11014. end;
  11015. {-------------------Set up the medium blocks-------------------}
  11016. {$ifdef CheckHeapForCorruption}
  11017. {Check that there are no gaps between where the small blocks end and the
  11018. medium blocks start}
  11019. if (((MaximumSmallBlockSize - 3) + (MediumBlockGranularity - 1 + BlockHeaderSize - MediumBlockSizeOffset))
  11020. and -MediumBlockGranularity) + MediumBlockSizeOffset < MinimumMediumBlockSize then
  11021. begin
  11022. {$ifdef BCB6OrDelphi7AndUp}
  11023. System.Error(reInvalidPtr);
  11024. {$else}
  11025. System.RunError(reInvalidPtr);
  11026. {$endif}
  11027. end;
  11028. {$endif}
  11029. {There are currently no medium block pools}
  11030. MediumBlockPoolsCircularList.PreviousMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  11031. MediumBlockPoolsCircularList.NextMediumBlockPoolHeader := @MediumBlockPoolsCircularList;
  11032. {All medium bins are empty}
  11033. for LInd := 0 to High(MediumBlockBins) do
  11034. begin
  11035. LPMediumFreeBlock := @MediumBlockBins[LInd];
  11036. LPMediumFreeBlock.PreviousFreeBlock := LPMediumFreeBlock;
  11037. LPMediumFreeBlock.NextFreeBlock := LPMediumFreeBlock;
  11038. end;
  11039. {------------------Set up the large blocks---------------------}
  11040. LargeBlocksCircularList.PreviousLargeBlockHeader := @LargeBlocksCircularList;
  11041. LargeBlocksCircularList.NextLargeBlockHeader := @LargeBlocksCircularList;
  11042. {------------------Set up the debugging structures---------------------}
  11043. {$ifdef FullDebugMode}
  11044. {Set up the fake VMT}
  11045. {Copy the basic info from the TFreedObject class}
  11046. System.Move(Pointer(PByte(TFreedObject) + vmtSelfPtr + SizeOf(Pointer))^,
  11047. FreedObjectVMT.VMTData[vmtSelfPtr + SizeOf(Pointer)], vmtParent - vmtSelfPtr);
  11048. PNativeUInt(@FreedObjectVMT.VMTData[vmtSelfPtr])^ := NativeUInt(@FreedObjectVMT.VMTMethods[0]);
  11049. {Set up the virtual method table}
  11050. for LInd := 0 to MaxFakeVMTEntries - 1 do
  11051. begin
  11052. PNativeUInt(@FreedObjectVMT.VMTMethods[Low(FreedObjectVMT.VMTMethods) + Integer(LInd * SizeOf(Pointer))])^ :=
  11053. NativeUInt(@TFreedObject.GetVirtualMethodIndex) + LInd * VMTIndexIncCodeSize;
  11054. {$ifdef CatchUseOfFreedInterfaces}
  11055. VMTBadInterface[LInd] := @TFreedObject.InterfaceError;
  11056. {$endif}
  11057. end;
  11058. {Set up the default log file name}
  11059. SetDefaultMMLogFileName;
  11060. {$endif}
  11061. end;
  11062. {Installs the memory manager (InitializeMemoryManager should be called first)}
  11063. procedure InstallMemoryManager;
  11064. {$ifdef MMSharingEnabled}
  11065. var
  11066. i, LCurrentProcessID: Cardinal;
  11067. LPMapAddress: PPointer;
  11068. LChar: AnsiChar;
  11069. {$endif}
  11070. begin
  11071. if not FastMMIsInstalled then
  11072. begin
  11073. {$ifdef FullDebugMode}
  11074. {$ifdef 32Bit}
  11075. {Try to reserve the 64K block covering address $80808080}
  11076. ReservedBlock := VirtualAlloc(Pointer(DebugReservedAddress), 65536, MEM_RESERVE, PAGE_NOACCESS);
  11077. {$endif}
  11078. {$endif}
  11079. {$ifdef MMSharingEnabled}
  11080. {Build a string identifying the current process}
  11081. LCurrentProcessID := GetCurrentProcessId;
  11082. for i := 0 to 7 do
  11083. begin
  11084. LChar := HexTable[((LCurrentProcessID shr (i * 4)) and $F)];
  11085. MappingObjectName[(High(MappingObjectName) - 1) - i] := LChar;
  11086. {$ifdef EnableBackwardCompatibleMMSharing}
  11087. UniqueProcessIDString[8 - i] := LChar;
  11088. UniqueProcessIDStringBE[8 - i] := LChar;
  11089. {$endif}
  11090. end;
  11091. {$endif}
  11092. {$ifdef AttemptToUseSharedMM}
  11093. {Is the replacement memory manager already installed for this process?}
  11094. {$ifdef EnableBackwardCompatibleMMSharing}
  11095. MMWindow := FindWindowA('STATIC', PAnsiChar(@UniqueProcessIDString[1]));
  11096. MMWindowBE := FindWindowA('STATIC', PAnsiChar(@UniqueProcessIDStringBE[1]));
  11097. {$endif}
  11098. MappingObjectHandle := OpenFileMappingA(FILE_MAP_READ, False, MappingObjectName);
  11099. {Is no MM being shared?}
  11100. {$ifdef EnableBackwardCompatibleMMSharing}
  11101. if (MMWindow or MMWindowBE or MappingObjectHandle) = 0 then
  11102. {$else}
  11103. if MappingObjectHandle = 0 then
  11104. {$endif}
  11105. begin
  11106. {$endif}
  11107. {$ifdef ShareMM}
  11108. {Share the MM with other DLLs? - if this DLL is unloaded, then
  11109. dependent DLLs will cause a crash.}
  11110. {$ifndef ShareMMIfLibrary}
  11111. if not IsLibrary then
  11112. {$endif}
  11113. begin
  11114. {$ifdef EnableBackwardCompatibleMMSharing}
  11115. {No memory manager installed yet - create the invisible window}
  11116. MMWindow := CreateWindowA('STATIC', PAnsiChar(@UniqueProcessIDString[1]),
  11117. WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
  11118. MMWindowBE := CreateWindowA('STATIC', PAnsiChar(@UniqueProcessIDStringBE[1]),
  11119. WS_POPUP, 0, 0, 0, 0, 0, 0, hInstance, nil);
  11120. {The window data is a pointer to this memory manager}
  11121. if MMWindow <> 0 then
  11122. SetWindowLongA(MMWindow, GWL_USERDATA, NativeInt(@NewMemoryManager));
  11123. if MMWindowBE <> 0 then
  11124. SetWindowLongA(MMWindowBE, GWL_USERDATA, NativeInt(@NewMemoryManager));
  11125. {$endif}
  11126. {Create the memory mapped file}
  11127. MappingObjectHandle := CreateFileMappingA(INVALID_HANDLE_VALUE, nil,
  11128. PAGE_READWRITE, 0, SizeOf(Pointer), MappingObjectName);
  11129. {Map a view of the memory}
  11130. LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_WRITE, 0, 0, 0);
  11131. {Set a pointer to the new memory manager}
  11132. LPMapAddress^ := @NewMemoryManager;
  11133. {Unmap the file}
  11134. UnmapViewOfFile(LPMapAddress);
  11135. end;
  11136. {$endif}
  11137. {We will be using this memory manager}
  11138. {$ifndef FullDebugMode}
  11139. NewMemoryManager.GetMem := FastGetMem;
  11140. NewMemoryManager.FreeMem := FastFreeMem;
  11141. NewMemoryManager.ReallocMem := FastReallocMem;
  11142. {$else}
  11143. NewMemoryManager.GetMem := DebugGetMem;
  11144. NewMemoryManager.FreeMem := DebugFreeMem;
  11145. NewMemoryManager.ReallocMem := DebugReallocMem;
  11146. {$endif}
  11147. {$ifdef BDS2006AndUp}
  11148. {$ifndef FullDebugMode}
  11149. NewMemoryManager.AllocMem := FastAllocMem;
  11150. {$else}
  11151. NewMemoryManager.AllocMem := DebugAllocMem;
  11152. {$endif}
  11153. {$ifdef EnableMemoryLeakReporting}
  11154. NewMemoryManager.RegisterExpectedMemoryLeak := RegisterExpectedMemoryLeak;
  11155. NewMemoryManager.UnRegisterExpectedMemoryLeak := UnRegisterExpectedMemoryLeak;
  11156. {$else}
  11157. NewMemoryManager.RegisterExpectedMemoryLeak := NoOpRegisterExpectedMemoryLeak;
  11158. NewMemoryManager.UnRegisterExpectedMemoryLeak := NoOpUnRegisterExpectedMemoryLeak;
  11159. {$endif}
  11160. {$endif}
  11161. {Owns the memory manager}
  11162. IsMemoryManagerOwner := True;
  11163. {$ifdef AttemptToUseSharedMM}
  11164. end
  11165. else
  11166. begin
  11167. {Get the address of the shared memory manager}
  11168. {$ifndef BDS2006AndUp}
  11169. {$ifdef EnableBackwardCompatibleMMSharing}
  11170. if MappingObjectHandle <> 0 then
  11171. begin
  11172. {$endif}
  11173. {Map a view of the memory}
  11174. LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_READ, 0, 0, 0);
  11175. {Set the new memory manager}
  11176. NewMemoryManager := PMemoryManager(LPMapAddress^)^;
  11177. {Unmap the file}
  11178. UnmapViewOfFile(LPMapAddress);
  11179. {$ifdef EnableBackwardCompatibleMMSharing}
  11180. end
  11181. else
  11182. begin
  11183. if MMWindow <> 0 then
  11184. begin
  11185. NewMemoryManager := PMemoryManager(GetWindowLong(MMWindow, GWL_USERDATA))^;
  11186. end
  11187. else
  11188. begin
  11189. NewMemoryManager := PMemoryManager(GetWindowLong(MMWindowBE, GWL_USERDATA))^;
  11190. end;
  11191. end;
  11192. {$endif}
  11193. {$else}
  11194. {$ifdef EnableBackwardCompatibleMMSharing}
  11195. if MappingObjectHandle <> 0 then
  11196. begin
  11197. {$endif}
  11198. {Map a view of the memory}
  11199. LPMapAddress := MapViewOfFile(MappingObjectHandle, FILE_MAP_READ, 0, 0, 0);
  11200. {Set the new memory manager}
  11201. NewMemoryManager := PMemoryManagerEx(LPMapAddress^)^;
  11202. {Unmap the file}
  11203. UnmapViewOfFile(LPMapAddress);
  11204. {$ifdef EnableBackwardCompatibleMMSharing}
  11205. end
  11206. else
  11207. begin
  11208. if MMWindow <> 0 then
  11209. begin
  11210. NewMemoryManager := PMemoryManagerEx(GetWindowLong(MMWindow, GWL_USERDATA))^;
  11211. end
  11212. else
  11213. begin
  11214. NewMemoryManager := PMemoryManagerEx(GetWindowLong(MMWindowBE, GWL_USERDATA))^;
  11215. end;
  11216. end;
  11217. {$endif}
  11218. {$endif}
  11219. {Close the file mapping handle}
  11220. CloseHandle(MappingObjectHandle);
  11221. MappingObjectHandle := 0;
  11222. {The memory manager is not owned by this module}
  11223. IsMemoryManagerOwner := False;
  11224. end;
  11225. {$endif}
  11226. {Save the old memory manager}
  11227. GetMemoryManager(OldMemoryManager);
  11228. {Replace the memory manager with either this one or the shared one.}
  11229. SetMemoryManager(NewMemoryManager);
  11230. {FastMM is now installed}
  11231. FastMMIsInstalled := True;
  11232. {$ifdef UseOutputDebugString}
  11233. if IsMemoryManagerOwner then
  11234. OutputDebugStringA(FastMMInstallMsg)
  11235. else
  11236. OutputDebugStringA(FastMMInstallSharedMsg);
  11237. {$endif}
  11238. end;
  11239. end;
  11240. procedure UninstallMemoryManager;
  11241. begin
  11242. {Is this the owner of the shared MM window?}
  11243. if IsMemoryManagerOwner then
  11244. begin
  11245. {$ifdef ShareMM}
  11246. {$ifdef EnableBackwardCompatibleMMSharing}
  11247. {Destroy the window}
  11248. if MMWindow <> 0 then
  11249. begin
  11250. DestroyWindow(MMWindow);
  11251. MMWindow := 0;
  11252. end;
  11253. if MMWindowBE <> 0 then
  11254. begin
  11255. DestroyWindow(MMWindowBE);
  11256. MMWindowBE := 0;
  11257. end;
  11258. {$endif}
  11259. {Destroy the memory mapped file handle}
  11260. if MappingObjectHandle <> 0 then
  11261. begin
  11262. CloseHandle(MappingObjectHandle);
  11263. MappingObjectHandle := 0;
  11264. end;
  11265. {$endif}
  11266. {$ifdef FullDebugMode}
  11267. {Release the reserved block}
  11268. if ReservedBlock <> nil then
  11269. begin
  11270. VirtualFree(ReservedBlock, 0, MEM_RELEASE);
  11271. ReservedBlock := nil;
  11272. end;
  11273. {$endif}
  11274. end;
  11275. {$ifndef DetectMMOperationsAfterUninstall}
  11276. {Restore the old memory manager}
  11277. SetMemoryManager(OldMemoryManager);
  11278. {$else}
  11279. {Set the invalid memory manager: no more MM operations allowed}
  11280. SetMemoryManager(InvalidMemoryManager);
  11281. {$endif}
  11282. {Memory manager has been uninstalled}
  11283. FastMMIsInstalled := False;
  11284. {$ifdef UseOutputDebugString}
  11285. if IsMemoryManagerOwner then
  11286. OutputDebugStringA(FastMMUninstallMsg)
  11287. else
  11288. OutputDebugStringA(FastMMUninstallSharedMsg);
  11289. {$endif}
  11290. end;
  11291. procedure FinalizeMemoryManager;
  11292. begin
  11293. {Restore the old memory manager if FastMM has been installed}
  11294. if FastMMIsInstalled then
  11295. begin
  11296. {$ifndef NeverUninstall}
  11297. {Uninstall FastMM}
  11298. UninstallMemoryManager;
  11299. {$endif}
  11300. {Do we own the memory manager, or are we just sharing it?}
  11301. if IsMemoryManagerOwner then
  11302. begin
  11303. {$ifdef CheckUseOfFreedBlocksOnShutdown}
  11304. CheckBlocksOnShutdown(
  11305. {$ifdef EnableMemoryLeakReporting}
  11306. True
  11307. {$ifdef RequireIDEPresenceForLeakReporting}
  11308. and DelphiIsRunning
  11309. {$endif}
  11310. {$ifdef RequireDebuggerPresenceForLeakReporting}
  11311. and ((DebugHook <> 0)
  11312. {$ifdef PatchBCBTerminate}
  11313. or (Assigned(pCppDebugHook) and (pCppDebugHook^ <> 0))
  11314. {$endif PatchBCBTerminate}
  11315. )
  11316. {$endif}
  11317. {$ifdef ManualLeakReportingControl}
  11318. and ReportMemoryLeaksOnShutdown
  11319. {$endif}
  11320. {$else}
  11321. False
  11322. {$endif}
  11323. );
  11324. {$else}
  11325. {$ifdef EnableMemoryLeakReporting}
  11326. if True
  11327. {$ifdef RequireIDEPresenceForLeakReporting}
  11328. and DelphiIsRunning
  11329. {$endif}
  11330. {$ifdef RequireDebuggerPresenceForLeakReporting}
  11331. and ((DebugHook <> 0)
  11332. {$ifdef PatchBCBTerminate}
  11333. or (Assigned(pCppDebugHook) and (pCppDebugHook^ <> 0))
  11334. {$endif PatchBCBTerminate}
  11335. )
  11336. {$endif}
  11337. {$ifdef ManualLeakReportingControl}
  11338. and ReportMemoryLeaksOnShutdown
  11339. {$endif}
  11340. then
  11341. CheckBlocksOnShutdown(True);
  11342. {$endif}
  11343. {$endif}
  11344. {$ifdef EnableMemoryLeakReporting}
  11345. {Free the expected memory leaks list}
  11346. if ExpectedMemoryLeaks <> nil then
  11347. begin
  11348. VirtualFree(ExpectedMemoryLeaks, 0, MEM_RELEASE);
  11349. ExpectedMemoryLeaks := nil;
  11350. end;
  11351. {$endif}
  11352. {$ifndef NeverUninstall}
  11353. {Clean up: Free all memory. If this is a .DLL that owns its own MM, then
  11354. it is necessary to prevent the main application from running out of
  11355. address space.}
  11356. FreeAllMemory;
  11357. {$endif}
  11358. end;
  11359. end;
  11360. end;
  11361. procedure RunInitializationCode;
  11362. begin
  11363. {Only run this code once during startup.}
  11364. if InitializationCodeHasRun then
  11365. Exit;
  11366. InitializationCodeHasRun := True;
  11367. {$ifndef BCB}
  11368. {$ifdef InstallOnlyIfRunningInIDE}
  11369. if (DebugHook <> 0) and DelphiIsRunning then
  11370. {$endif}
  11371. begin
  11372. {Initialize all the lookup tables, etc. for the memory manager}
  11373. InitializeMemoryManager;
  11374. {Has another MM been set, or has the Embarcadero MM been used? If so, this
  11375. file is not the first unit in the uses clause of the project's .dpr
  11376. file.}
  11377. if CheckCanInstallMemoryManager then
  11378. begin
  11379. {$ifdef ClearLogFileOnStartup}
  11380. DeleteEventLog;
  11381. {$endif}
  11382. InstallMemoryManager;
  11383. end;
  11384. end;
  11385. {$endif}
  11386. end;
  11387. initialization
  11388. RunInitializationCode;
  11389. finalization
  11390. {$ifndef PatchBCBTerminate}
  11391. FinalizeMemoryManager;
  11392. {$endif}
  11393. end.