The FullDebugMode support library hands its stack trace text over as PAnsiChar, and FastMM widens it again assuming Latin-1. Both halves lose data, and the loss is not limited to exotic setups.
Where it happens
-
FastMM_FullDebugMode.dpr, in the JCL branch of LogStackTrace:
LPInfo := LReturnAddressInfoCache.AddEntry(LAddress, AnsiString(LTempStr));
LTempStr is a string. Casting it to AnsiString replaces everything the system ANSI code page cannot represent with question marks, permanently.
-
FastMM5.pas, in FastMM_DebugLibrary_LegacyLogStackTrace_Wrapper:
Result^ := WideChar(LPCurPos^); //Assume it is Latin-1 text
The library produced that text in the ANSI code page, not in Latin-1. On a Russian system a Cyrillic path fits into the AnsiString perfectly well and is then mangled here. On a Western system the two agree except for 0x80..0x9F, which is where €, – and the typographic quotes live.
So a path survives only if it is pure ASCII, or if the system code page is 1252 and the path avoids that range.
Reproducing it
A unit whose file name mixes scripts, so no single code page can hold it:
Built with -GD, traced through the support library, the frame comes back as
before: ÐÑÐ¾ÐµÐºÑæ¥æ¬Grösse.pas
Five characters are simply gone. They are the ones that land in 0x80..0x9F on the way down and become control characters on the way back up.
A second thing, while in here
TFastMM_LegacyConvertStackTraceToText has no buffer end parameter, so the library cannot know where the caller's buffer stops - the wrapper just trusts it with a 16 KB stack buffer. TFastMM_ConvertStackTraceToText, which FastMM already uses for custom handlers, does carry one.
Not a FastMM bug, but visible in the same output
The remaining mojibake above is the JCL: Delphi writes the .map file as UTF-8 and the JCL reads it as ANSI. I verified that against the raw bytes (D0 9F D1 80 D0 BE ... = Проект in UTF-8). That belongs upstream in the JCL and I intend to take it there; I mention it only so the sample output above is not mistaken for something FastMM could fix.
What I did on my side
No pull request, per your note in #101. For what it is worth, the shape that worked was: export LogStackTraceW from the library using the existing TFastMM_ConvertStackTraceToText signature, prefer it in FastMM_LoadDebugSupportLibrary and keep the legacy lookup as the fallback, so an older support library keeps working untouched. That removes the wrapper and its stack buffer entirely rather than adding to them. The info cache becomes WideChar, which doubles it in memory (~900 KB at the default 4096 entries) but costs about 512 bytes in the file, since it is uninitialised data.
For the third party backends it differs: madExcept exposes StackAddrToStr, which returns a UnicodeString per address, so that build needs no code page at all. EurekaLog and the map file reader only offer ANSI, so the best available there is converting through the actual code page instead of assuming Latin-1 - enough for a single script non-Western system, not for mixed scripts.
It is on fulldebugmode-unicode-stacktrace in my fork if you want to look, and it builds on Delphi 7, 2009 and 13.1. Happy to open a PR if you ask for one.
The FullDebugMode support library hands its stack trace text over as
PAnsiChar, and FastMM widens it again assuming Latin-1. Both halves lose data, and the loss is not limited to exotic setups.Where it happens
FastMM_FullDebugMode.dpr, in the JCL branch ofLogStackTrace:LTempStris astring. Casting it toAnsiStringreplaces everything the system ANSI code page cannot represent with question marks, permanently.FastMM5.pas, inFastMM_DebugLibrary_LegacyLogStackTrace_Wrapper:Result^ := WideChar(LPCurPos^); //Assume it is Latin-1 textThe library produced that text in the ANSI code page, not in Latin-1. On a Russian system a Cyrillic path fits into the AnsiString perfectly well and is then mangled here. On a Western system the two agree except for 0x80..0x9F, which is where
€,–and the typographic quotes live.So a path survives only if it is pure ASCII, or if the system code page is 1252 and the path avoids that range.
Reproducing it
A unit whose file name mixes scripts, so no single code page can hold it:
Built with
-GD, traced through the support library, the frame comes back asFive characters are simply gone. They are the ones that land in 0x80..0x9F on the way down and become control characters on the way back up.
A second thing, while in here
TFastMM_LegacyConvertStackTraceToTexthas no buffer end parameter, so the library cannot know where the caller's buffer stops - the wrapper just trusts it with a 16 KB stack buffer.TFastMM_ConvertStackTraceToText, which FastMM already uses for custom handlers, does carry one.Not a FastMM bug, but visible in the same output
The remaining mojibake above is the JCL: Delphi writes the
.mapfile as UTF-8 and the JCL reads it as ANSI. I verified that against the raw bytes (D0 9F D1 80 D0 BE ...=Проектin UTF-8). That belongs upstream in the JCL and I intend to take it there; I mention it only so the sample output above is not mistaken for something FastMM could fix.What I did on my side
No pull request, per your note in #101. For what it is worth, the shape that worked was: export
LogStackTraceWfrom the library using the existingTFastMM_ConvertStackTraceToTextsignature, prefer it inFastMM_LoadDebugSupportLibraryand keep the legacy lookup as the fallback, so an older support library keeps working untouched. That removes the wrapper and its stack buffer entirely rather than adding to them. The info cache becomes WideChar, which doubles it in memory (~900 KB at the default 4096 entries) but costs about 512 bytes in the file, since it is uninitialised data.For the third party backends it differs: madExcept exposes
StackAddrToStr, which returns aUnicodeStringper address, so that build needs no code page at all. EurekaLog and the map file reader only offer ANSI, so the best available there is converting through the actual code page instead of assuming Latin-1 - enough for a single script non-Western system, not for mixed scripts.It is on
fulldebugmode-unicode-stacktracein my fork if you want to look, and it builds on Delphi 7, 2009 and 13.1. Happy to open a PR if you ask for one.