FPC Debug Information

FPC can use several debug file information format. The most useful is the DWARF debugging format.

DWARF integrates very well with the gdb debugger as used by Lazarus. But even if it tries to be optimized in size, it is always a huge file, order of magnitudes bigger than its associated executable itself. To be fair, the DWARF format offers much more information that we needed for a simple stack trace and symbol lookup. And publishing this extensive information may be a security concern, because it greatly help hacking the executable...

New DWARF Parser

We did have to re-implement in mormot.core.log.pas a full DWARF parser, since the one included in the FPC RTL is very slow and limited. Its purpose was to be cross-platform and emit a stack trace at program fatal error. Whereas we wanted something light and fast, potentially with our own binary format.

To be honest, DWARF is a very complex format, and developing an efficient parser for it, able to extract our needed information was quite a challenge. It seems to be working fairly well, but feedback is always welcome! We have seen some problems which may be linked to FPC itself, since the resulting .dbg file seems clearly incorrect when cross-compiling from Linux to Win32/Win64. Perhaps you may have some insight for us.

mORMot .mab File Format

Thanks to the optimized process available in mormot.code.data.pas like TDynArray and mormot.core.buffers.pas like TFastReader and TBufferWriter binary streams, then with our SynLZ compression algorithm, the resulting .mab file is more than 60 times smaller than the original .dbg file.

Loading the .mab file and symbol lookup are almost instant, thanks to binary search O(log(n)) algorithms over optimized data structures, and memory consumption is minimal.
Our unit parses the debug information only when needed, or can directly read an existing .mab file, potentially parsed at build time - even embedded to the executable.

Retrieve Symbol Information

You could also directly retrieve any symbol location from its address, using:

// return a function/method location according to the supplied code address
// - returns the address as hexadecimal by default, e.g. '004cb765'
// - if mormot.core.log.pas is defined in the project, will redirect to
// TDebugFile.FindLocationShort() method using .map/.dbg/.mab information, and
// return filename, symbol name and line number (if any) as plain text, e.g.
// '4cb765 ../src/core/mormot.core.base.pas statuscodeissuccess (11183)' on FPC
  GetExecutableLocation: function(aAddress: pointer): shortstring;

Here aAddress could be any function, method or global variable, or even the current get_caller_addr() value.

And you have full access to the TDebugFile parser and loader, if you need more low-level information.

Log Useful Exception Stack Trace At Runtime

Now you can distribute your FPC executable including debug information with no fear, and have clean exception traces at runtime, logged automatically by mormot.core.log.pas in an developer-friendly way:

24/06/2021 09:13:44.546 Exception 1 ENetSock {Message:"SetOptions(1,20) Fatal Error - #6"} at 49cac5 ../../src/net/mormot.net.sock.pas tnetsocketwrap.setopt (1285) ../../src/net/mormot.net.sock.posix.inc tnetsocketwrap.setreceivetimeout (303) ../../src/net/mormot.net.sock.pas tcrtsocket.bind (2120) ../../src/net/mormot.net.server.pas thttpserver.execute (1626)

Note that symbols are all lowercase. This is a limitation of the DWARF format as generated by FPC: it folded the case to avoid any case sensitivity problem during debuging with gdb, which expects case-sensitive languages like C.

Feedback Needed!

Feedback is welcome on our forum, as usual.