forked from pleriche/FastMM5
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFastMM5.pas
More file actions
9704 lines (8339 loc) · 369 KB
/
FastMM5.pas
File metadata and controls
9704 lines (8339 loc) · 369 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{
FastMM 5.01
Description:
A fast replacement memory manager for Embarcadero Delphi applications that scales well across multiple threads and CPU
cores, is not prone to memory fragmentation, and supports shared memory without the use of external .DLL files.
Developed by:
Pierre le Riche
Sponsored by:
gs-soft AG
Homepage:
https://github.com/pleriche/FastMM5
Licence:
FastMM 5 is dual-licensed. You may choose to use it under the restrictions of the GPL v3 licence at no cost to you,
or you may purchase a commercial licence. A commercial licence includes all future updates. The commercial licence
pricing is as follows:
1 developer = $99
2 developers = $189
3 developers = $269
4 developers = $339
5 developers = $399
>5 developers = $399 + $50 per developer from the 6th onwards
site licence (unlimited developers) = $999
Please send an e-mail to fastmm@leriche.org to request an invoice before or after payment is made at
https://www.paypal.me/fastmm (paypal@leriche.org). Support is available for users with a commercial licence via the
same e-mail address.
Usage Instructions:
Add FastMM5.pas as the first unit in your project's DPR file. It will install itself automatically during startup,
replacing the default memory manager.
In order to share the memory manager between the main application and libraries call
FastMM_AttemptToUseSharedMemoryManager (in order to use the memory manager of another module in the process) or
FastMM_ShareMemoryManager (to share the memory manager instance of the current module with other modules). It is
important to share the memory manager between modules where memory allocated in the one module may be freed by the
other.
If the application requires memory alignment greater than the default, call FastMM_EnterMinimumAddressAlignment and
once the greater alignment is no longer required call FastMM_ExitMinimumAddressAlignment. Calls may be nested. The
coarsest memory alignment requested takes precedence.
At the cost of performance and increased memory usage FastMM can log additional metadata together with every block.
In order to enable this mode call FastMM_EnterDebugMode and to exit debug mode call FastMM_ExitDebugMode. Calls may
be nested in which case debug mode will be active as long as the number of FastMM_EnterDebugMode calls exceed the
number of FastMM_ExitDebugMode calls. In debug mode freed memory blocks will be filled with the byte pattern
$808080... so that usage of a freed memory block or object, as well as corruption of the block header and/or footer
will likely be detected. If the debug support library, FastMM_FullDebugMode.dll, is available and the application has
not specified its own handlers for FastMM_GetStackTrace and FastMM_ConvertStackTraceToText then the support library
will be loaded during the first call to FastMM_EnterDebugMode.
Events (memory leaks, errors, etc.) may be logged to file, displayed on-screen, passed to the debugger or any
combination of the three. Specify how each event should be handled via the FastMM_LogToFileEvents,
FastMM_MessageBoxEvents and FastMM_OutputDebugStringEvents variables. The default event log filename will be built
from the application filepath, but may be overridden via FastMM_SetEventLogFilename. Messages are built from
templates that may be changed/translated by the application.
The optimization strategy of the memory manager may be tuned via FastMM_SetOptimizationStrategy. It can be set to
favour performance, low memory usage, or a blend of both. The default strategy is to blend the performance and low
memory usage goals.
Supported Compilers:
Delphi XE3 and later
Supported Platforms:
Windows, 32-bit and 64-bit
}
unit FastMM5;
interface
uses
Winapi.Windows;
{$RangeChecks Off}
{$BoolEval Off}
{$OverflowChecks Off}
{$Optimization On}
{$StackFrames Off}
{$TypedAddress Off}
{$LongStrings On}
{$Align 8}
{Calling the deprecated GetHeapStatus is unavoidable, so suppress the warning.}
{$warn Symbol_Deprecated Off}
{$warn Symbol_Platform Off}
{$if SizeOf(Pointer) = 8}
{$define 64Bit}
{$else}
{$define 32Bit}
{$endif}
{$ifdef CPUX86}
{$ifndef PurePascal}
{$define X86ASM}
{$endif}
{$else}
{$ifdef CPUX64}
{$ifndef PurePascal}
{$define X64ASM}
{$endif}
{$else}
{x86/x64 CPUs do not reorder writes, but ARM CPUs do.}
{$define WeakMemoryOrdering}
{$define PurePascal}
{$endif}
{$endif}
const
{The current version of FastMM. The first digit is the major version, followed by a two digit minor version number.}
CFastMM_Version = 501;
{The number of arenas for small, medium and large blocks. Increasing the number of arenas decreases the likelihood
of thread contention happening (when the number of threads inside a GetMem call is greater than the number of arenas),
at a slightly higher fixed cost per GetMem call. Usually two threads can be served simultaneously from the same arena
(a new block can be split off for one thread while a freed block can be recycled for the other), so the optimal number
of arenas is usually somewhere between 0.5x and 1x the number of threads. If you suspect that thread contention may
be dragging down performance, inspect the FastMM_...BlockThreadContentionCount variables - if their numbers are high
then an increase in the number of arenas will reduce thread contention.}
CFastMM_SmallBlockArenaCount = 4;
CFastMM_MediumBlockArenaCount = 4;
CFastMM_LargeBlockArenaCount = 8;
{The number of entries per stack trace differs between 32-bit and 64-bit in order to ensure that the debug header is
always a multiple of 64 bytes.}
{$ifdef 32Bit}
CFastMM_StackTraceEntryCount = 19; //8 stack trace entries per 64 bytes
{$else}
CFastMM_StackTraceEntryCount = 20; //4 stack trace entries per 64 bytes
{$endif}
type
{The optimization strategy for the memory manager.}
TFastMM_MemoryManagerOptimizationStrategy = (mmosOptimizeForSpeed, mmosBalanced, mmosOptimizeForLowMemoryUsage);
TFastMM_MemoryManagerEventType = (
{Another third party memory manager has already been installed.}
mmetAnotherThirdPartyMemoryManagerAlreadyInstalled,
{FastMM cannot be installed, because memory has already been allocated through the default memory manager.}
mmetCannotInstallAfterDefaultMemoryManagerHasBeenUsed,
{When an attempt is made to install or use a shared memory manager, but the memory manager has already been used to
allocate memory.}
mmetCannotSwitchToSharedMemoryManagerWithLivePointers,
{Details about an individual memory leak.}
mmetUnexpectedMemoryLeakDetail,
{Summary of memory leaks}
mmetUnexpectedMemoryLeakSummary,
{When an attempt to free or reallocate a debug block that has already been freed is detected.}
mmetDebugBlockDoubleFree,
mmetDebugBlockReallocOfFreedBlock,
{When a corruption of the memory pool is detected.}
mmetDebugBlockHeaderCorruption,
mmetDebugBlockFooterCorruption,
mmetDebugBlockModifiedAfterFree,
{When a virtual method is called on a freed object.}
mmetVirtualMethodCallOnFreedObject);
TFastMM_MemoryManagerEventTypeSet = set of TFastMM_MemoryManagerEventType;
TFastMM_MemoryManagerInstallationState = (
{The default memory manager is currently in use.}
mmisDefaultMemoryManagerInUse,
{Another third party memory manager has been installed.}
mmisOtherThirdPartyMemoryManagerInstalled,
{A shared memory manager is being used.}
mmisUsingSharedMemoryManager,
{This memory manager has been installed.}
mmisInstalled);
TFastMM_StackTrace = array[0..CFastMM_StackTraceEntryCount - 1] of NativeUInt;
{The debug block header. Must be a multiple of 64 in order to guarantee that minimum block alignment restrictions
are honoured.}
{$PointerMath On}
PFastMM_DebugBlockHeader = ^TFastMM_DebugBlockHeader;
{$PointerMath Off}
TFastMM_DebugBlockHeader = packed record
{The first two pointer sized slots cannot be used by the debug block header. The medium block manager uses the
first two pointers in a free block for the free block linked list, and the small block manager uses the first
pointer for the free block linked list. This space is thus reserved.}
Reserved1: Pointer;
Reserved2: Pointer;
{The user requested size for the block.}
UserSize: NativeInt;
{The object class this block was used for the previous time it was allocated. When a block is freed, the pointer
that would normally be in the space of the class pointer is copied here, so if it is detected that the block was
used after being freed we have an idea what class it is.}
PreviouslyUsedByClass: Pointer;
{The call stack when the block was allocated}
AllocationStackTrace: TFastMM_StackTrace;
{The call stack when the block was freed}
FreeStackTrace: TFastMM_StackTrace;
{The value of the FastMM_CurrentAllocationGroup when the block was allocated. Can be used in the debugging process
to group related memory leaks together.}
AllocationGroup: Cardinal;
{The allocation number: All debug mode allocations are numbered sequentially. This number may be useful in memory
leak analysis. If it reaches 4G it wraps back to 0.}
AllocationNumber: Cardinal;
{The ID of the thread that allocated the block}
AllocatedByThread: Cardinal;
{The ID of the thread that freed the block}
FreedByThread: Cardinal;
{The sum of the dwords(32-bit)/qwords(64-bit) in this structure starting after the initial two reserved fields up
to just before this field.}
HeaderCheckSum: NativeUInt;
{$ifdef 64Bit}
Padding1: Cardinal;
{$endif}
Padding2: SmallInt;
{The debug block signature. This will always be CIsDebugBlockFlag.}
DebugBlockFlags: SmallInt;
end;
TFastMM_WalkAllocatedBlocksBlockType = (
btLargeBlock,
btMediumBlockSpan,
btMediumBlock,
btSmallBlockSpan,
btSmallBlock);
TFastMM_WalkBlocksBlockTypes = set of TFastMM_WalkAllocatedBlocksBlockType;
TFastMM_WalkAllocatedBlocks_BlockInfo = record
BlockAddress: Pointer;
{If there is additional debug information for the block, this will be a pointer to it. (Will be nil if there is no
additional debug information for the block.}
DebugInformation: PFastMM_DebugBlockHeader;
{The size of the block or span. This includes the size of the block header, padding and internal fragmentation.}
BlockSize: NativeInt;
{The usable size of the block. This is BlockSize less any headers, footers, other management structures and
internal fragmentation.}
UsableSize: NativeInt;
{An arbitrary pointer value passed in to the WalkAllocatedBlocks routine, which is passed through to the callback.}
UserData: Pointer;
{The arena number for the block}
ArenaIndex: Byte;
{The type of block}
BlockType: TFastMM_WalkAllocatedBlocksBlockType;
{True if the block is free, False if it is in use}
BlockIsFree: Boolean;
{--------Medium block spans only-------}
{If True this is the current sequential feed medium block span for ArenaIndex}
IsSequentialFeedMediumBlockSpan: Boolean;
{If this is the sequential feed span for the medium block arena then this will contain the number of bytes
currently unused.}
MediumBlockSequentialFeedSpanUnusedBytes: Integer;
{----Small block spans only-----}
{If True this is the current sequential feed small block span for ArenaIndex and the block size}
IsSequentialFeedSmallBlockSpan: Boolean;
{If IsSmallBlockSpan = True then this will contain the size of the small block.}
SmallBlockSpanBlockSize: Word;
{If this is a sequential feed small block span then this will contain the number of bytes currently unused.}
SmallBlockSequentialFeedSpanUnusedBytes: Integer;
end;
TFastMM_WalkBlocksCallback = procedure(const ABlockInfo: TFastMM_WalkAllocatedBlocks_BlockInfo);
TFastMM_MinimumAddressAlignment = (maa8Bytes, maa16Bytes, maa32Bytes, maa64Bytes);
TFastMM_MinimumAddressAlignmentSet = set of TFastMM_MinimumAddressAlignment;
{The formats in which text files (e.g. the event log) may be written. Controlled via the FastMM_TextFileEncoding
variable.}
TFastMM_TextFileEncoding = (
{UTF-8 with no byte-order mark}
teUTF8,
{UTF-8 with a byte-order mark}
teUTF8_BOM,
{UTF-16 little endian, with no byte-order mark}
teUTF16LE,
{UTF-16 little endian, with a byte-order mark}
teUTF16LE_BOM);
{A routine used to obtain the current stack trace up to AMaxDepth levels deep. The first ASkipFrames frames in the
stack trace are skipped. Unused entries will be set to 0.}
TFastMM_GetStackTrace = procedure(APReturnAddresses: PNativeUInt; AMaxDepth, ASkipFrames: Cardinal);
{A routine used to convert a stack trace to a textual representation (typically unit and line information).
APReturnAddresses points to a buffer with up to AMaxDepth return addresses (zero return addresses are ignored). The
textual representation is stored to APBufferPosition. The routine will update both APBufferPosition and
ARemainingBufferSpaceInWideChars.}
TFastMM_ConvertStackTraceToText = function(APReturnAddresses: PNativeUInt; AMaxDepth: Cardinal;
APBuffer, APBufferEnd: PWideChar): PWideChar;
{List of registered leaks}
TFastMM_RegisteredMemoryLeak = record
LeakAddress: Pointer;
LeakedClass: TClass;
LeakSize: NativeInt;
LeakCount: Integer;
end;
TFastMM_RegisteredMemoryLeaks = array of TFastMM_RegisteredMemoryLeak;
TFastMM_UsageSummary = record
{The total number of bytes allocated by the application.}
AllocatedBytes: NativeUInt;
{The committed virtual address space less AllocatedBytes: The total number of address space bytes used by control
structures, or lost due to fragmentation and other overhead. Blocks that have been freed by the application but
not yet released back to the operating system are included in this total.}
OverheadBytes: NativeUInt;
{The efficiency of the memory manager expressed as a percentage. This is:
100 * AllocatedBytes / (AllocatedBytes + OverheadBytes).}
EfficiencyPercentage: Double;
end;
{------------------------Core memory manager interface------------------------}
function FastMM_GetMem(ASize: NativeInt): Pointer;
function FastMM_FreeMem(APointer: Pointer): Integer;
function FastMM_ReallocMem(APointer: Pointer; ANewSize: NativeInt): Pointer;
function FastMM_AllocMem(ASize: NativeInt): Pointer;
{------------------------Expected memory leak management------------------------}
{Registers expected memory leaks. Returns True on success. The list of leaked blocks is limited, so failure is
possible if the list is full.}
function FastMM_RegisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
function FastMM_RegisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
function FastMM_RegisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
{Removes expected memory leaks. Returns True on success.}
function FastMM_UnregisterExpectedMemoryLeak(ALeakedPointer: Pointer): Boolean; overload;
function FastMM_UnregisterExpectedMemoryLeak(ALeakedObjectClass: TClass; ACount: Integer = 1): Boolean; overload;
function FastMM_UnregisterExpectedMemoryLeak(ALeakedBlockSize: NativeInt; ACount: Integer = 1): Boolean; overload;
{Returns a list of all expected memory leaks}
function FastMM_GetRegisteredMemoryLeaks: TFastMM_RegisteredMemoryLeaks;
{------------------------Diagnostics------------------------}
{Returns the user size of the block, normally the number of bytes requested in the original GetMem or ReallocMem call.
Exception: Outside of debug mode the requested size for small and medium blocks is not tracked, and in these instances
the value returned will be the same as the value returned by the FastMM_BlockMaximumUserBytes call.}
function FastMM_BlockCurrentUserBytes(APointer: Pointer): NativeInt;
{Returns the maximum number of bytes that may safely be used by the application for the block starting at APointer.
This will be greater or equal to the size requested in the original GetMem or ReallocMem call. Note that using more
than the value returned by FastMM_BlockCurrentUserBytes is not recommended, since a reallocation request will only move
up to FastMM_BlockCurrentUserBytes bytes.}
function FastMM_BlockMaximumUserBytes(APointer: Pointer): NativeInt;
{Attempts to release all pending free blocks. Returns True if there were no pending frees, or all pending frees could
be released. Returns False if there were locked (currently in use) managers with pending frees.}
function FastMM_ProcessAllPendingFrees: Boolean;
{Walks the block types indicated by the AWalkBlockTypes set, calling ACallBack for each allocated block. If
AWalkBlockTypes = [] then all block types is assumed. Note that pending free blocks are treated as used blocks for the
purpose of the AWalkUsedBlocksOnly parameter. Call FastMM_ProcessAllPendingFrees first in order to process all pending
frees if this is a concern. ALockTimeoutMilliseconds is the maximum number of millseconds that FastMM_WalkBlocks will
wait to acquire a lock on an arena, skipping the arena if it is unable to do so. Returns True if all blocks were walked
successfully, False if one or more arenas were skipped due to a lock timeout.}
function FastMM_WalkBlocks(ACallBack: TFastMM_WalkBlocksCallback; AWalkBlockTypes: TFastMM_WalkBlocksBlockTypes = [];
AWalkUsedBlocksOnly: Boolean = True; AUserData: Pointer = nil; ALockTimeoutMilliseconds: Cardinal = 1000): Boolean;
{Walks all debug mode blocks (blocks that were allocated between a FastMM_EnterDebugMode and FastMM_ExitDebugMode call),
checking for corruption of the debug header, footer, and in the case of freed blocks whether the block content was
modified after the block was freed. If a corruption is encountered an error message will be logged and/or displayed
(as per the error logging configuration) and an invalid pointer exception will be raised.}
procedure FastMM_ScanDebugBlocksForCorruption;
{Returns a THeapStatus structure with information about the current memory usage.}
function FastMM_GetHeapStatus: THeapStatus;
{Returns the number of allocated bytes, the number of overhead bytes (wastage due to management structures and internal
fragmentation), as well as the efficiency percentage. The efficiency percentage is the total allocated bytes divided
by the total address space committed (whether in use or reserved for future use) multiplied by 100. Note that freed
blocks not yet released to the operating system are included in the overhead, which differs from FastMM_GetHeapStatus
that exposes freed blocks in separate fields.}
function FastMM_GetUsageSummary: TFastMM_UsageSummary;
{Writes a log file containing a summary of the memory manager state and a list of allocated blocks grouped by class.
The file will be saved in the encoding specified by FastMM_TextFileEncoding. ALockTimeoutMilliseconds is the maximum
amount of time to wait for a lock on a manager to be released, before it is skipped. Returns True on success.}
function FastMM_LogStateToFile(const AFilename: string; const AAdditionalDetails: string = '';
ALockTimeoutMilliseconds: Cardinal = 1000): Boolean;
{------------------------Memory Manager Sharing------------------------}
{Searches the current process for a shared memory manager. If no memory has been allocated using this memory manager
it will switch to using the shared memory manager instead. Returns True if another memory manager was found and it
could be shared. If this memory manager instance *is* the shared memory manager, it will do nothing and return True.}
function FastMM_AttemptToUseSharedMemoryManager: Boolean;
{Starts sharing this memory manager with other modules in the current process. Only one memory manager may be shared
per process, so this function may fail.}
function FastMM_ShareMemoryManager: Boolean;
{------------------------Configuration------------------------}
{Returns the current installation state of the memory manager.}
function FastMM_GetInstallationState: TFastMM_MemoryManagerInstallationState;
{Gets/sets the optimization strategy for the memory manager. FastMM can be optimized for maximum performance, low
memory usage or a blend of the two.}
procedure FastMM_SetOptimizationStrategy(AStrategy: TFastMM_MemoryManagerOptimizationStrategy);
function FastMM_GetCurrentOptimizationStrategy: TFastMM_MemoryManagerOptimizationStrategy;
{Call FastMM_EnterMinimumAddressAlignment to request that all subsequent allocated blocks are aligned to the specified
minimum. Call FastMM_ExitMinimumAddressAlignment to rescind a prior request. Requests for coarser alignments have
precedence over requests for lesser alignments. These calls are thread safe. In the current implementation the
following minimum alignments are always in effect, regardless of any alignment requests:
32-Bit applications: >= maa8Bytes
64-bit applications: >= maa16Bytes
Allocations greater than 150 bytes: >= maa16Bytes
Allocations greater than 302 bytes: >= maa32Bytes
Allocations greater than 606 bytes: maa64Bytes}
procedure FastMM_EnterMinimumAddressAlignment(AMinimumAddressAlignment: TFastMM_MinimumAddressAlignment);
procedure FastMM_ExitMinimumAddressAlignment(AMinimumAddressAlignment: TFastMM_MinimumAddressAlignment);
{Returns the current minimum address alignment in effect.}
function FastMM_GetCurrentMinimumAddressAlignment: TFastMM_MinimumAddressAlignment;
{Attempts to load the debug support library specified by FastMM_DebugSupportLibraryName. On success it will set the
FastMM_GetStackTrace and FastMM_ConvertStackTraceToText handlers to point to the routines in the debug library, provided
alternate handlers have not yet been assigned by the application. Returns True if the library was loaded successfully,
or was already loaded successfully prior to this call. FastMM_EnterDebugMode will call FastMM_LoadDebugSupportLibrary
the first time it is called, unless the debug support library has already been loaded or handlers for both
FastMM_GetStackTrace and FastMM_ConvertStackTraceToText have been set by the application.}
function FastMM_LoadDebugSupportLibrary: Boolean;
{Frees the debug support library, pointing the stack trace handlers currently using the debug support library back to
the default no-op handlers.}
function FastMM_FreeDebugSupportLibrary: Boolean;
{Enters/exits debug mode. Calls may be nested, in which case debug mode is only exited when the number of
FastMM_ExitDebugMode calls equal the number of FastMM_EnterDebugMode calls. In debug mode extra metadata is logged
before and after the user data in the block, and extra checks are performed in order to catch common programming
errors. Returns True on success, False if this memory manager instance is not currently installed or the installed
memory manager has changed. Note that debug mode comes with a severe performance penalty, and due to the extra
metadata all blocks that are allocated while debug mode is active will use significantly more address space.}
function FastMM_EnterDebugMode: Boolean;
function FastMM_ExitDebugMode: Boolean;
{Returns True if debug mode is currently active, i.e. FastMM_EnterDebugMode has been called more times than
FastMM_ExitDebugMode.}
function FastMM_DebugModeActive: Boolean;
{No-op call stack routines.}
procedure FastMM_NoOpGetStackTrace(APReturnAddresses: PNativeUInt; AMaxDepth, ASkipFrames: Cardinal);
function FastMM_NoOpConvertStackTraceToText(APReturnAddresses: PNativeUInt; AMaxDepth: Cardinal;
APBufferPosition, APBufferEnd: PWideChar): PWideChar;
{Sets the default event log path and filename. If the FastMMLogFilePath environment variable is set then that will be
used as the path, otherwise the path to the application will be used. The filename is built from the name of the
application.}
procedure FastMM_SetDefaultEventLogFilename;
{Sets the full path and filename for the event log. if APEventLogFilename = nil then the default event log filename
will be set.}
procedure FastMM_SetEventLogFilename(APEventLogFilename: PWideChar);
{Returns the current full path and filename for the event log.}
function FastMM_GetEventLogFilename: PWideChar;
{Deletes the event log file.}
function FastMM_DeleteEventLogFile: Boolean;
var
{-----------Stack trace support routines----------}
{The active routines used to get a call stack and to convert it to a textual representation. These will be set to
the no-op routines during startup. If either of these have not been assigned a different value when
FastMM_EnterDebugMode is called for the first time then an attempt will be made to load the debug support DLL and
any of these still set to the no-op routines will be rerouted to the handlers in the debug support DLL.}
FastMM_GetStackTrace: TFastMM_GetStackTrace;
FastMM_ConvertStackTraceToText: TFastMM_ConvertStackTraceToText;
{---------Debug options---------}
{The name of the library that contains the functionality used to obtain the current call stack, and also to convert a
call stack to unit and line number information. The first time EnterDebugMode is called an attempt will be made to
load this library, unless handlers for both FastMM_GetStackTrace and FastMM_ConvertStackTraceToText have already been
set.}
FastMM_DebugSupportLibraryName: PWideChar = {$ifndef 64Bit}'FastMM_FullDebugMode.dll'{$else}'FastMM_FullDebugMode64.dll'{$endif};
{The events that are passed to OutputDebugString.}
FastMM_OutputDebugStringEvents: TFastMM_MemoryManagerEventTypeSet = [mmetDebugBlockDoubleFree,
mmetDebugBlockReallocOfFreedBlock, mmetDebugBlockHeaderCorruption, mmetDebugBlockFooterCorruption,
mmetDebugBlockModifiedAfterFree, mmetVirtualMethodCallOnFreedObject, mmetAnotherThirdPartyMemoryManagerAlreadyInstalled,
mmetCannotInstallAfterDefaultMemoryManagerHasBeenUsed, mmetCannotSwitchToSharedMemoryManagerWithLivePointers];
{The events that are logged to file.}
FastMM_LogToFileEvents: TFastMM_MemoryManagerEventTypeSet = [mmetDebugBlockDoubleFree,
mmetDebugBlockReallocOfFreedBlock, mmetDebugBlockHeaderCorruption, mmetDebugBlockFooterCorruption,
mmetDebugBlockModifiedAfterFree, mmetVirtualMethodCallOnFreedObject, mmetAnotherThirdPartyMemoryManagerAlreadyInstalled,
mmetCannotInstallAfterDefaultMemoryManagerHasBeenUsed, mmetCannotSwitchToSharedMemoryManagerWithLivePointers];
{The events that are displayed in a message box.}
FastMM_MessageBoxEvents: TFastMM_MemoryManagerEventTypeSet = [mmetDebugBlockDoubleFree,
mmetDebugBlockReallocOfFreedBlock, mmetDebugBlockHeaderCorruption, mmetDebugBlockFooterCorruption,
mmetDebugBlockModifiedAfterFree, mmetVirtualMethodCallOnFreedObject, mmetAnotherThirdPartyMemoryManagerAlreadyInstalled,
mmetCannotInstallAfterDefaultMemoryManagerHasBeenUsed, mmetCannotSwitchToSharedMemoryManagerWithLivePointers];
{All debug blocks are tagged with the current value of this variable when the block is allocated. This may be used
by the application to track memory issues.}
FastMM_CurrentAllocationGroup: Cardinal;
{This variable is incremented during every debug getmem call (wrapping to 0 once it hits 4G) and stored in the debug
header. It may be useful for debugging purposes.}
FastMM_LastAllocationNumber: Cardinal;
{These variables are incremented every time all the arenas for the block size are locked simultaneously and FastMM had
to relinquish the thread's timeslice during a GetMem or ReallocMem call. (FreeMem frees can always be deferred, so
will never cause a thread contention). If these numbers are excessively high then it is an indication that the number
of small, medium and/or large block arenas are insufficient for the number of application threads and should be
increased. (The CFastMM_SmallBlockArenaCount, CFastMM_MediumBlockArenaCount and CFastMM_LargeBlockArenaCount constants.)}
FastMM_SmallBlockThreadContentionCount: Cardinal;
FastMM_MediumBlockThreadContentionCount: Cardinal;
FastMM_LargeBlockThreadContentionCount: Cardinal;
{---------Message and log file text configuration--------}
{The text encoding to use for the event log and other text file output.}
FastMM_TextFileEncoding: TFastMM_TextFileEncoding;
{Messages contain numeric tokens that will be substituted. The available tokens are:
0: A blank string (invalid token IDs will also translate to this)
1: The current date in yyyy-mm-dd format.
2: The current time in HH:nn:ss format.
3: Block size in bytes
4: The ID of the allocating thread (in hexadecimal).
5: The ID of the freeing thread (in hexadecimal).
6: The stack trace when the block was allocated.
7: The stack trace when the block was freed.
8: The object class for the block. For freed blocks this will be the prior object class, otherwise it will be the
current object class.
9: The allocation number for the block (in decimal).
10: Hex and ASCII dump size in bytes
11: Block address (in hexadecimal).
12: Hex dump of block (each line is followed by #13#10)
13: ASCII dump of block (each line is followed by #13#10)
14: Leak summary entries
15: The size and offsets for modifications to a block after it was freed.
16: The full path and filename of the event log.
17: The virtual method name for a virtual method calls on a freed object
18: The total kilobytes allocated (FastMM_LogStateToFile)
19: The total kilobytes overhead (FastMM_LogStateToFile)
20: The efficiency percentage (FastMM_LogStateToFile)
21: The total number of bytes used by the class (FastMM_LogStateToFile)
22: The number of instances of the class (FastMM_LogStateToFile)
23: The average number of bytes per instance for the class (FastMM_LogStateToFile)
24: The stack trace for a virtual method call on a freed object
}
{This entry precedes every entry in the event log.}
FastMM_LogFileEntryHeader: PWideChar = '--------------------------------{1} {2}--------------------------------'#13#10;
{Memory manager installation errors}
FastMM_CannotInstallAfterDefaultMemoryManagerHasBeenUsedMessage: PWideChar = 'FastMM cannot be installed, because the '
+ 'default memory manager has already been used to allocate memory.';
FastMM_CannotSwitchToSharedMemoryManagerWithLivePointersMessage: PWideChar = 'Cannot switch to the shared memory '
+ 'manager, because the local memory manager instance has already been used to allocate memory.';
FastMM_AnotherMemoryManagerAlreadyInstalledMessage: PWideChar = 'FastMM cannot be installed, because another third '
+ 'party memory manager has already been installed.';
FastMM_CannotSwitchMemoryManagerMessageBoxCaption: PWideChar = 'Cannot Switch Memory Managers';
{Memory leak messages.}
FastMM_MemoryLeakDetailMessage_NormalBlock: PWideChar = 'A memory block has been leaked. The size is: {3}'#13#10#13#10
+ 'The block is currently used for an object of class: {8}'#13#10#13#10
+ 'Current memory dump of {10} bytes starting at pointer address {11}:'#13#10
+ '{12}'#13#10'{13}'#13#10;
FastMM_MemoryLeakDetailMessage_DebugBlock: PWideChar = 'A memory block has been leaked. The size is: {3}'#13#10#13#10
+ 'This block was allocated by thread 0x{4}, and the stack trace (return addresses) at the time was:'
+ '{6}'#13#10#13#10'The block is currently used for an object of class: {8}'#13#10#13#10
+ 'The allocation number is: {9}'#13#10#13#10
+ 'Current memory dump of {10} bytes starting at pointer address {11}:'#13#10
+ '{12}'#13#10'{13}'#13#10;
FastMM_MemoryLeakSummaryMessage_LeakDetailNotLogged: PWideChar = 'This application has leaked memory. '
+ 'The leaks ordered by size are:'#13#10'{14}'#13#10;
FastMM_MemoryLeakSummaryMessage_LeakDetailLoggedToEventLog: PWideChar = 'This application has leaked memory. '
+ 'The leaks ordered by size are:'#13#10'{14}'#13#10#13#10
+ 'Memory leak detail was logged to {16}'#13#10;
FastMM_MemoryLeakMessageBoxCaption: PWideChar = 'Unexpected Memory Leak';
{Attempts to free or reallocate a debug block that has alredy been freed.}
FastMM_DebugBlockDoubleFree: PWideChar = 'An attempt was made to free a block that has already been freed.'#13#10#13#10
+ 'The block size is {3}.'#13#10#13#10
+ 'The block was allocated by thread 0x{4}, and the stack trace (return addresses) at the time was:'
+ '{6}'#13#10#13#10'This block was freed by thread 0x{5}, and the stack trace (return addresses) at the time was:'
+ '{7}'#13#10#13#10
+ 'The allocation number is: {9}'#13#10;
FastMM_DebugBlockReallocOfFreedBlock: PWideChar = 'An attempt was made to resize a block that has already been freed.'#13#10#13#10
+ 'The block size is {3}.'#13#10#13#10
+ 'The block was allocated by thread 0x{4}, and the stack trace (return addresses) at the time was:'
+ '{6}'#13#10#13#10'This block was freed by thread 0x{5}, and the stack trace (return addresses) at the time was:'
+ '{7}'#13#10#13#10
+ 'The allocation number is: {9}'#13#10;
{Memory pool corruption messages.}
FastMM_BlockModifiedAfterFreeMessage: PWideChar = 'A memory block was modified after it was freed.'#13#10#13#10
+ 'The block size is {3}.'#13#10#13#10
+ 'Modifications were detected at offsets (with lengths in brackets): {15}.'#13#10#13#10
+ 'The block was allocated by thread 0x{4}, and the stack trace (return addresses) at the time was:'
+ '{6}'#13#10#13#10'This block was freed by thread 0x{5}, and the stack trace (return addresses) at the time was:'
+ '{7}'#13#10#13#10
+ 'The allocation number is: {9}'#13#10#13#10
+ 'Current memory dump of {10} bytes starting at pointer address {11}:'#13#10
+ '{12}'#13#10'{13}'#13#10;
FastMM_BlockHeaderCorruptedMessage: PWideChar = 'A memory block header has been corrupted.'#13#10#13#10
+ 'Current memory dump of {10} bytes starting at pointer address {11}:'#13#10
+ '{12}'#13#10'{13}'#13#10;
FastMM_BlockFooterCorruptedMessage_AllocatedBlock: PWideChar = 'A memory block footer has been corrupted.'#13#10#13#10
+ 'The block size is {3}.'#13#10#13#10
+ 'The block was allocated by thread 0x{4}, and the stack trace (return addresses) at the time was:'
+ '{6}'#13#10#13#10
+ 'The allocation number is: {9}'#13#10#13#10
+ 'Current memory dump of {10} bytes starting at pointer address {11}:'#13#10
+ '{12}'#13#10'{13}'#13#10;
FastMM_BlockFooterCorruptedMessage_FreedBlock: PWideChar = 'A memory block footer has been corrupted.'#13#10#13#10
+ 'The block size is {3}.'#13#10#13#10
+ 'The block was allocated by thread 0x{4}, and the stack trace (return addresses) at the time was:'
+ '{6}'#13#10#13#10'This block was freed by thread 0x{5}, and the stack trace (return addresses) at the time was:'
+ '{7}'#13#10#13#10
+ 'The allocation number is: {9}'#13#10#13#10
+ 'Current memory dump of {10} bytes starting at pointer address {11}:'#13#10
+ '{12}'#13#10'{13}'#13#10;
FastMM_MemoryCorruptionMessageBoxCaption: PWideChar = 'Memory Corruption Detected';
{Virtual method call on a freed object.}
FastMM_VirtualMethodCallOnFreedObjectMessage: PWideChar = 'A virtual method was called on a freed object.'#13#10#13#10
+ 'Freed object class: {8}'#13#10#13#10
+ 'Virtual method: {17}'#13#10#13#10
+ 'The block size is {3}.'#13#10#13#10
+ 'The block was allocated by thread 0x{4}, and the stack trace (return addresses) at the time was:'
+ '{6}'#13#10#13#10'This block was freed by thread 0x{5}, and the stack trace (return addresses) at the time was:'
+ '{7}'#13#10#13#10'The stack trace for the virtual call that lead to this error is:'
+ '{24}'#13#10#13#10
+ 'The allocation number is: {9}'#13#10#13#10
+ 'Current memory dump of {10} bytes starting at pointer address {11}:'#13#10
+ '{12}'#13#10'{13}'#13#10;
FastMM_VirtualMethodCallOnFreedObjectMessageBoxCaption: PWideChar = 'Virtual Method Call On Freed Object';
{Memory state logging messages}
FastMM_LogStateToFileTemplate: PWideChar = 'FastMM State Capture:'#13#10
+ '---------------------'#13#10
+ '{18}K Allocated'#13#10
+ '{19}K Overhead'#13#10
+ '{20}% Efficiency'#13#10#13#10
+ 'Usage Detail:'#13#10;
FastMM_LogStateToFileTemplate_UsageDetail: PWideChar = '{21} bytes: {8} x {22} ({23} bytes avg.)'#13#10;
{Initialization error messages.}
FastMM_DebugSupportLibraryNotAvailableError: PWideChar = 'The FastMM debug support library could not be loaded.';
FastMM_DebugSupportLibraryNotAvailableError_Caption: PWideChar = 'Fatal Error';
implementation
{All blocks are preceded by a block header. The block header varies in size according to the block type. The block
type and state may be determined from the bits of the word preceding the block address, as follows:
All block types:
----------------
Bit 0: Block is free flag
0 = Block is in use
1 = Block is free
Bit 1: Debug info flag
0 = the block contains no additional debug information
1 = the block contains a debug mode sub-block
Bit 2: Block type 1
0 = Is not a small block
1 = Is a small block
Small blocks only (bit 2 = 1):
------------------------------
Bits 3..15: Offset to small block span header
The offset of the block from the start of the small block span header, divided by 64.
Medium, Large and Debug Blocks (bit 2 = 0):
-------------------------------------------
Bit 3: Block type 2
0 = Is not a medium block
1 = Is a medium block
Bit 4: Block type 3
0 = Is not a large block
1 = Is a large block
Bit 5: Block type 4
0 = Is not a debug sub-block
1 = Is a debug sub-block
Bits 6..15: Reserved (always 0)
}
const
{$ifdef 32Bit}
CPointerSizeBitShift = 2; //1 shl 2 = 4
CTObjectInstanceSize = 8;
{$else}
CPointerSizeBitShift = 3; //1 shl 3 = 8
CTObjectInstanceSize = 16;
{$endif}
{Block status flags}
CBlockIsFreeFlag = 1;
CHasDebugInfoFlag = 2;
CIsSmallBlockFlag = 4;
CIsMediumBlockFlag = 8;
CIsLargeBlockFlag = 16;
CIsDebugBlockFlag = 32;
{-----Small block constants-----}
{$ifdef 32Bit}
CSmallBlockTypeCount = 61;
CSmallBlockGranularityBits = 3;
{$else}
CSmallBlockTypeCount = 51;
CSmallBlockGranularityBits = 4;
{$endif}
CSmallBlockGranularity = 1 shl CSmallBlockGranularityBits;
CMaximumSmallBlockSize = 2624; //Must be a multiple of 64 for the 64-byte alignment option to work
CSmallBlockFlagCount = 3;
CDropSmallBlockFlagsMask = - (1 shl CSmallBlockFlagCount);
CSmallBlockSpanOffsetBitShift = 6 - CSmallBlockFlagCount;
{-----Medium block constants-----}
{Medium blocks are always aligned to at least 64 bytes (which is the typical cache line size). Spans must be a
multiple of 64K (to make optimal use of the virtual address space), and offsets divided by the granularity must fit
inside a 16-bit word.}
CMediumBlockAlignmentBits = 6;
CMediumBlockAlignment = 1 shl CMediumBlockAlignmentBits;
CMaximumMediumBlockSpanSize = 64 * 1024 * CMediumBlockAlignment; // = 4MB
{Medium blocks are binned in linked lists - one linked list for each size.}
CMediumBlockBinsPerGroup = 32;
CMediumBlockBinGroupCount = 32;
CMediumBlockBinCount = CMediumBlockBinGroupCount * CMediumBlockBinsPerGroup;
{The smallest medium block should be <= 10% greater than the largest small block. It is an odd multiple
of the typical cache line size in order to facilitate better cache line utilization.}
CMinimumMediumBlockSize = CMaximumSmallBlockSize + 256; // = 2880
{The spacing between medium block bins is not constant. There are three groups: initial, middle and final.}
CInitialBinCount = 384;
CInitialBinSpacingBits = 8;
CInitialBinSpacing = 1 shl CInitialBinSpacingBits; // = 256
CMediumBlockMiddleBinsStart = CMinimumMediumBlockSize + CInitialBinSpacing * CInitialBinCount;
CMiddleBinCount = 384;
CMiddleBinSpacingBits = 9;
CMiddleBinSpacing = 1 shl CMiddleBinSpacingBits; // = 512
CMediumBlockFinalBinsStart = CMediumBlockMiddleBinsStart + CMiddleBinSpacing * CMiddleBinCount;
CFinalBinCount = CMediumBlockBinCount - CMiddleBinCount - CInitialBinCount;
CFinalBinSpacingBits = 10;
CFinalBinSpacing = 1 shl CFinalBinSpacingBits; // = 1024
{The maximum size allocatable through medium blocks. Blocks larger than this are allocated via the OS from the
virtual memory pool ( = large blocks).}
CMaximumMediumBlockSize = CMediumBlockFinalBinsStart + (CFinalBinCount - 1) * CFinalBinSpacing;
{-----Large block constants-----}
CLargeBlockGranularity = 64 * 1024; //Address space obtained from VirtualAlloc is always aligned to a 64K boundary
{-----Small block span constants-----}
{Allocating and deallocating small block spans are expensive, so it is not something that should be done frequently.}
CMinimumSmallBlocksPerSpan = 16;
COptimalSmallBlocksPerSpan = 64;
COptimalSmallBlockSpanSizeLowerLimit = CMinimumMediumBlockSize + 16 * 1024;
COptimalSmallBlockSpanSizeUpperLimit = CMinimumMediumBlockSize + 96 * 1024;
{The maximum amount by which a small block span may exceed the optimal size before the block will be split instead of
using it as-is.}
CSmallBlockSpanMaximumAmountWithWhichOptimalSizeMayBeExceeded = 4 * 1024;
{-------------Block resizing constants---------------}
CSmallBlockDownsizeCheckAdder = 64;
CSmallBlockUpsizeAdder = 32;
{When a medium block is reallocated to a size smaller than this, then it must be reallocated to a small block and the
data moved. If not, then it is shrunk in place.}
CMediumInPlaceDownsizeLimit = CMinimumMediumBlockSize div 4;
{------Debug constants-------}
{$ifdef 32Bit}
{The number of bytes of address space that is reserved and only released once the first OS allocation request fails.
This allows some subsequent memory allocation requests to succeed in order to allow the application to allocate some
memory for error handling, etc. in response to the first EOutOfMemory exception. This only applies to 32-bit
applications.}
CEmergencyReserveAddressSpace = CMaximumMediumBlockSpanSize;
{$endif}
{Event and state log tokens}
CEventLogTokenBlankString = 0;
CEventLogTokenCurrentDate = 1;
CEventLogTokenCurrentTime = 2;
CEventLogTokenBlockSize = 3;
CEventLogTokenAllocatedByThread = 4;
CEventLogTokenFreedByThread = 5;
CEventLogTokenAllocationStackTrace = 6;
CEventLogTokenFreeStackTrace = 7;
CEventLogTokenObjectClass = 8;
CEventLogTokenAllocationNumber = 9;
CEventLogTokenMemoryDumpSize = 10;
CEventLogTokenBlockAddress = 11;
CEventLogTokenHexDump = 12;
CEventLogTokenASCIIDump = 13;
CEventLogTokenLeakSummaryEntries = 14;
CEventLogTokenModifyAfterFreeDetail = 15;
CEventLogTokenEventLogFilename = 16;
CEventLogTokenVirtualMethodName = 17;
CEventLogTokenVirtualMethodCallOnFreedObject = 24;
CStateLogTokenAllocatedKB = 18;
CStateLogTokenOverheadKB = 19;
CStateLogTokenEfficiencyPercentage = 20;
CStateLogTokenClassTotalBytesUsed = 21;
CStateLogTokenClassInstanceCount = 22;
CStateLogTokenClassAverageBytesPerInstance = 23;
{The highest ID of an event log token.}
CEventLogMaxTokenID = 30;
{The maximum size of an event message, in wide characters.}
CEventMessageMaxWideChars = 32768;
CTokenBufferMaxWideChars = 32768;
CFilenameMaxLength = 1024;
{The size of the memory block reserved for maintaining the list of registered memory leaks.}
CExpectedMemoryLeaksListSize = 64 * 1024;
CHexDigits: array[0..15] of Char = '0123456789ABCDEF';
{The maximum size of hexadecimal and ASCII dumps.}
CMemoryDumpMaxBytes = 256;
CMemoryDumpMaxBytesPerLine = 32;
{The debug block fill pattern, in several sizes.}
CDebugFillPattern8B = $8080808080808080;
CDebugFillPattern4B = $80808080;
CDebugFillPattern2B = $8080;
CDebugFillPattern1B = $80;
{The first few frames of a GetMem or FreeMem stack trace are inside system.pas and this unit, so does not provide any
useful information. Specify how many of the initial frames should be skipped here. Note that these are actual
frames, so routines that do not have frames will also be skipped.}
CFastMM_StackTrace_SkipFrames_GetMem = 0;
CFastMM_StackTrace_SkipFrames_FreeMem = 0;
{The number of bytes in a memory page. It is assumed that pages are aligned at page size boundaries, and that memory
protection is set at the page level.}
CVirtualMemoryPageSize = 4096;
CCopyrightMessage: PAnsiChar = 'FastMM (c) 2004 - 2020 Pierre le Riche';
type
{Event log token values are pointers #0 terminated text strings. The payload for the tokens is in TokenData.}
TEventLogTokenValues = array[0..CEventLogMaxTokenID] of PWideChar;
TMoveProc = procedure(const ASource; var ADest; ACount: NativeInt);
TIntegerWithABACounter = record
case Integer of
0: (IntegerAndABACounter: Int64);
1: (IntegerValue, ABACounter: Integer);
end;
TBlockStatusFlags = Word;
{$PointerMath On}
PBlockStatusFlags = ^TBlockStatusFlags;
{$PointerMath Off}
{------------------------Small block structures------------------------}
{Small blocks have a 16-bit header.}
TSmallBlockHeader = record
{
Bit 0: Block is free flag
0 = Block is in use
1 = Block is free
Bit 1: Debug flag
0 = the block contains no additional debug information
1 = the block contains a debug mode sub-block
Bit 2: Small block indicator
Must be 1
Bits 3..15 (0..8191):
The offset of the block from the start of the small block span header, divided by 64.
}
BlockStatusFlagsAndSpanOffset: TBlockStatusFlags;
end;
{$PointerMath On}
PSmallBlockHeader = ^TSmallBlockHeader;
{$PointerMath Off}
{Small block layout:
Offset: -2 = This block's header
Offset: 0 = User data / Pointer to next free block (if this block is free)}
PSmallBlockSpanHeader = ^TSmallBlockSpanHeader;
{Always 64 bytes in size in order to fit inside a cache line, under both 32-bit and 64-bit. It should preferably be
aligned to 64 bytes.}
TSmallBlockManager = record
{The first/last partially free span in the arena. This field must be at the same offsets as
TSmallBlockSpanHeader.NextPartiallyFreeSpan and TSmallBlockSpanHeader.PreviousPartiallyFreeSpan.}
FirstPartiallyFreeSpan: PSmallBlockSpanHeader; //Do not change position
LastPartiallyFreeSpan: PSmallBlockSpanHeader; //Do not change position
{The offset from the start of SequentialFeedSmallBlockSpan of the last block that was fed sequentially, as well as
an ABA counter to solve concurrency issues.}
LastSmallBlockSequentialFeedOffset: TIntegerWithABACounter;
{The span that is current being used to serve blocks in sequential order, from the last block down to the first.}
SequentialFeedSmallBlockSpan: PSmallBlockSpanHeader;
{Singly linked list of blocks in this arena that should be freed. If a block must be freed but the arena is
currently locked by another thread then the block is added to the head of this list. It is the responsibility of
the next thread that locks this arena to clean up this list.}
PendingFreeList: Pointer;
{The fixed size move procedure used to move data for this block size when it is upsized. When a block is downsized
(which typically occurs less often) the variable size move routine is used.}
UpsizeMoveProcedure: TMoveProc;
{0 = unlocked, 1 = locked, cannot be Boolean due to RSP-25672}
SmallBlockManagerLocked: Integer;
{The minimum and optimal size of a small block span for this block type}
MinimumSpanSize: Integer;
OptimalSpanSize: Integer;
{The block size for this small block manager}
BlockSize: Word;
{$ifdef 64Bit}
Padding: array[0..1] of Byte;
{$else}
Padding: array[0..21] of Byte;
{$endif}
end;
PSmallBlockManager = ^TSmallBlockManager;
TSmallBlockArena = array[0..CSmallBlockTypeCount - 1] of TSmallBlockManager;
PSmallBlockArena = ^TSmallBlockArena;
TSmallBlockArenas = array[0..CFastMM_SmallBlockArenaCount - 1] of TSmallBlockArena;
{This is always 64 bytes in size in order to ensure proper alignment of small blocks under all circumstances.}
TSmallBlockSpanHeader = packed record
{The next and previous spans in this arena that have free blocks of this size. These fields must be at the same
offsets as TSmallBlockManager.FirstPartiallyFreeSpan and TSmallBlockManager.LastPartiallyFreeSpan.}
NextPartiallyFreeSpan: PSmallBlockSpanHeader; //Do not change position
PreviousPartiallyFreeSpan: PSmallBlockSpanHeader; //Do not change position
{Pointer to the first free block inside this span.}
FirstFreeBlock: Pointer;
{Pointer to the small block manager to which this span belongs.}
SmallBlockManager: PSmallBlockManager;
{The total number of blocks in this small block span.}
TotalBlocksInSpan: Integer;
{The number of blocks currently in use in this small block span.}
BlocksInUse: Integer;
{$ifdef 64Bit}
Padding: array[0..21] of Byte;
{$else}
Padding: array[0..37] of Byte;
{$endif}
{The header for the first block}
FirstBlockHeader: TSmallBlockHeader;
end;
{------------------------Medium block structures------------------------}
TMediumBlockHeader = packed record
{Multiply with CMediumBlockAlignment in order to get the size of the block.}
MediumBlockSizeMultiple: Word;
{The offset from the start of medium block span header to the start of the block. Multiply this with
CMediumBlockAlignment and subtract the result from the pointer in order to obtain the address of the medium block
span.}
MediumBlockSpanOffsetMultiple: Word;
{True if the previous medium block in the medium block span is free. If this is True then the size of the previous
block will be stored in the Integer immediately preceding this header.}
PreviousBlockIsFree: Boolean;
{True if this medium block is used as a small block span.}
IsSmallBlockSpan: Boolean;
{The block status and type}
BlockStatusFlags: TBlockStatusFlags;
end;
{$PointerMath On}
PMediumBlockHeader = ^TMediumBlockHeader;
{$PointerMath Off}
{Medium block layout:
Offset: - SizeOf(TMediumBlockHeader) - 4 = Integer containing the previous block size (only if PreviousBlockIsFree = True)
Offset: - SizeOf(TMediumBlockHeader) = This block's header
Offset: 0 = User data / Pointer to previous free block (if this block is free)
Offset: SizeOf(Pointer) = Next Free Block (if this block is free)
Offset: BlockSize - SizeOf(TMediumBlockHeader) - 4 = Size of this block (if this block is free)
Offset: BlockSize - SizeOf(TMediumBlockHeader) = Header for the next block}
PMediumBlockManager = ^TMediumBlockManager;
{The medium block span from which medium blocks are drawn. This is always 64 bytes in size.}
PMediumBlockSpanHeader = ^TMediumBlockSpanHeader;
TMediumBlockSpanHeader = packed record
{Points to the previous and next medium block spans. This circular linked list is used to track memory leaks on