Skip to content

Add debug mode options to skip block fill patterns and checksums (lightweight profiling mode)#92

Open
TetzkatLipHoka wants to merge 2 commits into
pleriche:masterfrom
TetzkatLipHoka:debug-mode-no-fill-no-checksum-options
Open

Add debug mode options to skip block fill patterns and checksums (lightweight profiling mode)#92
TetzkatLipHoka wants to merge 2 commits into
pleriche:masterfrom
TetzkatLipHoka:debug-mode-no-fill-no-checksum-options

Conversation

@TetzkatLipHoka

Copy link
Copy Markdown

This extends the FastMM_DebugModeOptions mechanism from #82 with two options that skip the expensive parts of debug mode while keeping the debug block headers fully populated:

  • dmoNoBlockFillPatterns - skip the freed/allocated block fill patterns. While FastMM_Begin/EndEraseAllocatedBlockContent or FastMM_Begin/EndEraseFreedBlockContent are active, blocks are still erased regardless of this option, so the security-oriented erase feature keeps its guarantees.
  • dmoNoBlockCheckSums - skip the header/footer checksum calculation and verification.

Motivation: with both options enabled, debug mode still records allocation numbers, thread IDs, timestamps and the allocation/free stack traces - everything needed for allocation profiling and leak analysis via FastMM_WalkBlocks - but at a fraction of the cost. In a quick benchmark (200k GetMem/FreeMem pairs of 4000 bytes, debug mode, Delphi 13.1 Win64) the loop drops from ~110 ms to below the GetTickCount resolution, since the dominant cost is the fill/checksum work over the user area. The trade-offs are documented in the option comments: no use-after-free / uninitialized-memory detection without fill patterns, no corruption detection without checksums.

Consistency under option changes: the features actually applied to each block are recorded per block in the previously reserved byte of TFastMM_DebugBlockHeader (now DebugFeatureFlags). Every verification site (free, realloc, freed-block reuse, FastMM_ScanDebugBlocksForCorruption, HandleInvalidFreeMemOrReallocMem) consults the block's own flags rather than the currently active options, so the options may be toggled while debug mode is active and blocks allocated under different settings coexist safely. Default behaviour (options not set) is byte-for-byte unchanged.

Verified with Delphi 10 Seattle and 13.1 (Win32 + Win64), warning-free: reference behaviour intact, both options individually effective, option changes while debug mode is active, and FastMM_ScanDebugBlocksForCorruption over mixed blocks.

🤖 Generated with Claude Code

@pleriche

Copy link
Copy Markdown
Owner

Thanks for the suggestions. It certainly makes sense to add these debug mode tweaks to FastMM_DebugModeOptions. As you rightly point out it would make it necessary to store the options that were in effect in the debug header, but fortunately there's a perfect spot for it - the TFastMM_DebugBlockHeader.ReservedSpace2 field.

A risk with allowing the disabling of checksums is that if a memory corruption happens to clear the flag that indicates whether the block has checksums then that corruption could go unnoticed (even though the block actually has checksums). At the very least I would want to replace the checksums with magic numbers to serve as header and footer guards against buffer overruns.

I've got some ideas for other options I would like to add to FastMM_DebugModeOptions. I'm probably not going to be able to get to it during the week, but I'll try to make time over the weekend.

@TetzkatLipHoka

Copy link
Copy Markdown
Author

Good point regarding the flag-corruption risk - I've updated the PR with your magic number suggestion (6427e64): blocks allocated with dmoNoBlockCheckSums now carry a fixed guard value in both checksum slots, and it is verified wherever the checksums would have been verified.

Besides catching buffer overruns into the debug header/footer, the guards cross-check the DebugFeatureFlags byte itself: if a corruption flips the checksums feature flag in either direction, the slot content no longer matches what the flag promises (a real checksum practically never equals the guard value, and vice versa), so the corruption is still reported. I verified both directions explicitly (flag forced on for a guard block, flag cleared for a checksummed block - both raise on the next free), plus the full option matrix, with 10 Seattle and 13.1 (Win32+Win64).

Feel free to reshape any of this to fit the additional options you have in mind - happy to adjust the PR as well.

…htweight profiling mode)

Adds two options to TFastMM_DebugModeOption, extending the mechanism
introduced for pleriche#82:

- dmoNoBlockFillPatterns: skip the freed/allocated block fill patterns.
  While FastMM_Begin/EndEraseAllocatedBlockContent or
  FastMM_Begin/EndEraseFreedBlockContent are active, blocks are still
  erased regardless of this option.
- dmoNoBlockCheckSums: skip the header/footer checksum calculation and
  verification.

With both options enabled, debug mode still maintains the full debug
block headers (allocation number, allocating/freeing thread, timestamps
and the allocation/free stack traces), but skips the expensive per-
operation work. This makes it practical to keep debug mode enabled for
allocation profiling or leak analysis in scenarios where full debug
mode would be too slow, at the cost of forgoing the use-after-free and
corruption detection. 200k GetMem/FreeMem pairs of 4000 bytes in debug
mode drop from ~110 ms to under the timer resolution (Delphi 13.1,
Win64).

The features actually applied to each block are recorded in the block
header (in the previously reserved byte, now DebugFeatureFlags), and
all verification sites consult the block's own flags rather than the
currently active options. The options can thus be changed while debug
mode is active without invalidating existing blocks - blocks allocated
under different settings coexist safely.
…NoBlockCheckSums

Addresses review feedback on the risk that a corruption clearing the
has-checksums feature flag could mask a corrupted block: without
checksums, the header and footer checksum slots now hold a fixed guard
value (CDebugBlockNoCheckSumsGuardValue) that is verified wherever the
checksums would have been verified.

The guards catch buffer overruns into the debug header/footer, and they
also cross-check the DebugFeatureFlags byte itself: if a corruption
flips the checksums feature flag in either direction, the slot content
no longer matches what the flag promises (a real checksum practically
never equals the guard value and vice versa), so the corruption is
still reported. Verified in both directions, plus the full option test
matrix, with Delphi 10 Seattle and 13.1 (Win32+Win64).
@TetzkatLipHoka
TetzkatLipHoka force-pushed the debug-mode-no-fill-no-checksum-options branch from 4592a96 to 231e185 Compare July 23, 2026 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants