Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2026-02

A little something I've been working on...

21 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Unknown author

They say Object Pascal is a classic, but every classic deserves some modern love.

I've silently spent the last few months building something that has actually been in the planning stages, on and off, for years. I finally put head down to turn those notes into reality, and while it's not quite ready for its full debut, I couldn't resist sharing a "behind the scenes" look.

More soon. ;-)

----------------------
$ ../../pdr-cli/target/pdr DemoApp
PDR (Reference Implementation) v0.2.0
Copyright (c) 2025-2026 Graeme Geldenhuys

[INFO] Loading program: DemoApp
[INFO] OPDF version: 1
[INFO] Target architecture: x86_64
[INFO] Pointer size: 8 bytes
[INFO] Total records: 0
[INFO] Loaded 717 type(s), 8 variable(s), 4 function scope(s), and 34 line mapping(s)
[INFO] Program loaded successfully

[INFO] No arguments provided. Use "args" command to set them, then "run"

Type "help" for available commands

(pdr) run
[INFO] Running program: DemoApp
[INFO] Program started and paused at entry point
[INFO] You can now set breakpoints and use "continue" to start execution (pdr) break DemoApp.pas:61
(pdr) continue
[INFO] Hit breakpoint at 0x00000000004012AE
(pdr) print ShortLabel
Shortlabel = 'PDR'
(pdr) print FullTitle
Fulltitle = 'OPDF version 1 🚀'
(pdr) print UniLabel
Unilabel = 'Unicode: Hello FPC 🎉'
(pdr) print WorkWeek
Workweek = [Mon, Tue, Wed, Thu, Fri]
(pdr) inspect MyCircle
[INSPECT] MyCircle: TCircle (class, 8 bytes)
[INSPECT] parent chain: TCircle -> TObject
[INSPECT] fields (3):
FName = 'Alpha' [AnsiString(0), offset +16]
FKind = skCircle (2) [<enumeration type>, offset +24] FRadius = 3.14159 [Double, offset +32]
[INSPECT] properties (5):
Name = 'Alpha' [read FName]
Kind = skCircle (2) [read FKind]
Radius = 3.14159 [read FRadius]
Area = <method> [read GetArea() - use 'call' to evaluate] Perimeter = <method> [read GetPerimeter() - use 'call' to evaluate]
(pdr) print MyCircle.FRadius
MyCircle.FRadius = 3.14159
(pdr) inspect MyShape
[INSPECT] MyShape: IShape (interface)
[INSPECT] parent: IUnknown
[INSPECT] methods (3):
Area = <method>
Perimeter = <method>
Description = <method>
(pdr) print Radii[2..4]
Radii[2] = 2.5
Radii[3] = 3.5
Radii[4] = 4.5
(pdr) locals
ACircle = TCircle(@$000074A0EC33A5F0) { FName: 'Alpha', FKind: skCircle (2), FRadius: 3.14159, Name: 'Alpha', Kind: skCircle (2), Radius: 3.14159 }
Info = 'Inspecting: Alpha'
Progress = 0
(pdr) callstack
[CALLSTACK]
#0 :65 (0x 4012B1)
#1 :110 (0x 40141E)
(pdr) set Counter = 99
[INFO] Counter set to 99
(pdr) print Counter
Counter = 99
(pdr) locals globals
Info = 'Inspecting: Alpha'
Progress = 0
Shortlabel = 'PDR'
Fulltitle = 'OPDF version 1 🚀'
Unilabel = 'Unicode: Hello FPC 🎉'
Workweek = [Mon, Tue, Wed, Thu, Fri]
Radii = array[1..5] of Double = [1.5, 2.5, 3.5, 4.5, 5.5] Mycircle = TCircle(@$00007C65AC7935F0) { FName: 'Alpha', FKind: skCircle (2), FRadius: 3.14159, Name: 'Alpha', Kind: skCircle (2), Radius: 3.14159 }
Counter = 99
(pdr) quit
Exiting...

----------------------

Reply by email

Unknown author
to Unknown author
Graeme Geldenhuys wrote
More soon. ;-)

What is it? A fully working, native FPC debug format and debugger. I designed a new debug format from scratch (ditching the very C-centric DWARF format and its many versions) and added support into FPC `main` (3.3.1) via a feature branch. That's all on the compiler side.

On the client side, I implemented a debug engine from scratch along with a reference CLI debugger client (seen in my previous message). For the engine, I used a Hexagonal design (aka Ports & Adapters). This means the debug engine does all the heavy lifting and exposes methods that the thin "debug client" (in my case, the CLI debugger) can simply call to display output and orchestrate user interaction.

Integrating this into Lazarus IDE or MSEide simply means implementing the equivalent `pdr-cli` [thin] layer. Nothing needs to be changed in the debug engine itself.

I'll reveal more soon with a Pull Request. I'll include links to my analysis, the OPDF specification, and Implementation Architecture documents. A huge amount of research has gone into this -- studying Kylix, Delphi, and DWARF. Finally, I've scratched that 14-year itch I had, triggered by the duby release. :)

Regards,
Graeme

Reply by email

Tomas Hajny
to Unknown author
Graeme Geldenhuys wrote
On 2026-02-20 01:31, Graeme Geldenhuys via fpc-devel wrote:

Hi Graeme,

Show quoted text 35 lines
> More soon. ;-) What is it? A fully working, native FPC debug format and debugger. I designed a new debug format from scratch (ditching the very C-centric DWARF format and its many versions) and added support into FPC `main` (3.3.1) via a feature branch. That's all on the compiler side. On the client side, I implemented a debug engine from scratch along with a reference CLI debugger client (seen in my previous message). For the engine, I used a Hexagonal design (aka Ports & Adapters). This means the debug engine does all the heavy lifting and exposes methods that the thin "debug client" (in my case, the CLI debugger) can simply call to display output and orchestrate user interaction. Integrating this into Lazarus IDE or MSEide simply means implementing the equivalent `pdr-cli` [thin] layer. Nothing needs to be changed in the debug engine itself. I'll reveal more soon with a Pull Request. I'll include links to my analysis, the OPDF specification, and Implementation Architecture documents. A huge amount of research has gone into this -- studying Kylix, Delphi, and DWARF. Finally, I've scratched that 14-year itch I had, triggered by the duby release. :)

It's certainly very interesting. :-) Obviously, it will be necessary to understand, what that means for binaries including e.g. object files written in C, where / how is the debug information stored (having it inside the binaries implies support for that in various linkers), etc. Looking forward to seeing what and how you did there. :-)

Have a nice weekend

Tomas

Reply by email

Hairy Pixels
to Unknown author: What is it? A fully working, native FPC debug format and debugger. I designed a new debug…

On Feb 20, 2026 at 7:02:09 PM, Graeme Geldenhuys via fpc-devel <

Show quoted text 7 lines
wrote: What is it? A fully working, native FPC debug format and debugger. I designed a new debug format from scratch (ditching the very C-centric DWARF format and its many versions) and added support into FPC `main` (3.3.1) via a feature branch. That's all on the compiler side.

is there anything wrong with DWARF for Pascal? I use LLDB on macOS and there's some things I'd like improved but I figured that just due to how FPC implemented it and not the format itself.

My primary debugger now is VSCode and LLDB. Making a new format means plugins for all the various editors now too right?

Reply by email

Martin Frb
to Hairy Pixels
Hairy Pixels wrote 11 lines
On Feb 20, 2026 at 7:02:09 PM, Graeme Geldenhuys via fpc-devel wrote: > What is it? A fully working, native FPC debug format and debugger. I > designed a new debug format from scratch (ditching the very C-centric > DWARF format and its many versions) and added support into FPC `main` > (3.3.1) via a feature branch. That's all on the compiler side. is there anything wrong with DWARF for Pascal? I use LLDB on macOS and there's some things I'd like improved but I figured that just due to how FPC implemented it and not the format itself.

The biggest sore with DWARF that I was aware of was properties. But they have been added to the DWARF standard (including indexed and more).

Well, there is also "managed types" that are absent => that is needed, if such data should be modified by the debugger.

One could argue, that there also is the lack of many scoping rules for Pascal....
But, partly that lack is caused by stuff not provide by FPC, which would be possible, and in other parts it would be of very limited help.

------

E.g. currently

procedure foo;
var SeenByBar: byte;
    procedure Bar;
    begin {} end;
var NOT_SeenByBar: byte;
begin {} end;

FPC currently does not include the info that the 2nd var is not in scope for Bar. There would afaik be several ways. So not a Dwarf problem.

------

What is not foreseen in Dwarf is unit search order and visibility. If the Debugger encounter TGlobal, in which order should it search other units.
But, that knowledge would in a lot of cases not be worth anything, and more so it may be contra productive.

If a unit has
    var x: TGlobal;
then debug info will point to the correct TGlobal. No problem.

If you have
  Sender: TObject
and a user enters the expression
  TGLobal(Sender)

then that search order may not help. Sender could be of TGlobal, even if TGlobal is NOT IN SCOPE for the current unit.
So if the debugger would now stay in search order, you would just get an error.
Or the wrong TGlobal is in scope of the unit, and then you get the wrong cast.
Of course, the wrong cast can happen now too, with the absent scoping. But the scoping would not add much help.

-------------

I don't know what the new format does in terms of calling params, and stack resolution....
But then again, currently we don't even fully use what is there with DWARF.

Would be interesting to know which shortcomings Graeme had particularly in mind.

Reply by email

Unknown author
to Martin Frb
Martin Frb wrote 13 lines
E.g. currently procedure foo; var SeenByBar: byte; procedure Bar; begin {} end; var NOT_SeenByBar: byte; begin {} end; FPC currently does not include the info that the 2nd var is not in scope for Bar. There would afaik be several ways. So not a Dwarf problem.

That was an interesting case - thanks for mentioning it. FPC has the information needed, I just had to emit it into the debug info. It took me about 5 minutes to include the declaration index in the debug output and then read that info back in the debugger engine.

It took a bit longer to figure out the heuristics for my 'locals' command, because the HighPC and LowPC for procedures are not nested as I initially expected. FPC processed one method after another, even though Bar() declaration is nested inside Foo().

But, got it all sorted out in the end, and I added this as an official integration test.

------
(pdr) break test_19_nested_scope.pas:14
(pdr) break test_19_nested_scope.pas:24
(pdr) c
[PROG] running test
[INFO] Hit breakpoint at 0x0000000000401113
(pdr) locals
BarLocal = 2
SeenByBar = 1
(pdr) c
[PROG] inside bar
[INFO] Hit breakpoint at 0x00000000004010B3
(pdr) locals
SeenByBar = 1
NotSeenByBar = 99
(pdr) q
Exiting...
------

Complete example program is attached.

Regards,
Graeme

Reply by email

Unknown author
to Hairy Pixels: On Feb 20, 2026 at 7:02:09 PM, Graeme Geldenhuys via fpc-devel < is there anything…
Hairy Pixels wrote
is there anything wrong with DWARF for Pascal?

Yes and no. DWARF itself is not broken -- it is a well-designed, mature standard. The problem is that DWARF was designed for procedural/C-centric languages, and Object Pascal concepts do not map naturally onto it. Some specific issues:

- Properties: DWARF has no native concept of getter/setter accessor methods as a language construct. Noted that DWARF 5 added property support, but FPC currently generates DWARF 2/3 (with partial DWARF 4), and even with DWARF 5 the encoding is a workaround rather than a native representation. GDB and LLDB still struggle with reliably calling Pascal methods to evaluate property getters during a debug session.

- String types: ShortString, AnsiString, and UnicodeString are all reference-counted heap-allocated strings with metadata (code page, reference count, length). DWARF represents these as raw byte arrays or PChar, losing the semantic meaning. fpdebug works around this by reverse-engineering FPC's DWARF quirks, but this is fragile and version-dependent.

- Sets: DWARF stores sets as raw bytes with no type information linking the bitfield back to the underlying enumeration members. A debugger shows you a numeric value instead of [Mon, Tue, Wed].

- Dynamic arrays: Display incorrectly or without correct length information, since the metadata (high value stored at a negative offset from the data pointer) is an FPC implementation detail that DWARF does not describe.

So it is partly how FPC implements DWARF, but also partly that DWARF simply does not have vocabulary for these Pascal concepts. fpdebug has made heroic efforts to work around these limitations by interpreting FPC-specific DWARF patterns, but every workaround is another piece of fragile, version-coupled code.

Regards,
Graeme

Reply by email

Martin Frb
to Unknown author: That was an interesting case - thanks for mentioning it. FPC has the information needed,…
Graeme Geldenhuys wrote 20 lines
On 2026-02-20 17:19, Martin Frb via fpc-devel wrote: > > E.g. currently > > procedure foo; > var SeenByBar: byte; >    procedure Bar; >    begin {} end; > var NOT_SeenByBar: byte; > begin {} end; > > FPC currently does not include the info that the 2nd var is not in > scope for Bar. There would afaik be several ways. So not a Dwarf > problem. That was an interesting case - thanks for mentioning it. FPC has the information needed, I just had to emit it into the debug info. It took me about 5 minutes to include the declaration index in the debug output and then read that info back in the debugger engine.

Well, how long to fix it for dwarf too?

Dwarf has all we need: location expressions.

Bar  gets a "local var" (the declaration can be used to ident it us being outside), and a location expression that points to the addr in the parent stack frame.
Then all the existing debuggers could read it correctly.

The problem is not Dwarf, it is that fpc doesn't fully use it.

Reply by email

Martin Frb
to Unknown author: Yes and no. DWARF itself is not broken -- it is a well-designed, mature standard. The…
Graeme Geldenhuys wrote 8 lines
- Properties: DWARF has no native concept of getter/setter accessor methods as a language construct. Noted that DWARF 5 added property support, but FPC currently generates DWARF 2/3 (with partial DWARF 4), and even with DWARF 5 the encoding is a workaround rather than a native representation. GDB and LLDB still struggle with reliably calling Pascal methods to evaluate property getters during a debug session.

I was referring to DWARF 6.
https://dwarfstd.org/issues/240507.1.html

And yes, its not out yet, but that needn't stop us from using it.

Show quoted text 7 lines
- String types: ShortString, AnsiString, and UnicodeString are all reference-counted heap-allocated strings with metadata (code page, reference count, length). DWARF represents these as raw byte arrays or PChar, losing the semantic meaning. fpdebug works around this by reverse-engineering FPC's DWARF quirks, but this is fragile and version-dependent.

Yes, strings need something better. But that could be added.

Also, dwarf allows custom tags.

- Sets: DWARF stores sets as raw bytes with no type information linking the bitfield back to the underlying enumeration members. A debugger shows you a numeric value instead of [Mon, Tue, Wed].

What am I missing here that makes this the fault of DWARF? With the link to the enum, all the info is there (FpDebug certainly has the info, and shows it / and does so without hacks).

So if any debugger does not, then isn't that a problem of that debugger? And not a problem of Dwarf?

- Dynamic arrays: Display incorrectly or without correct length information, since the metadata (high value stored at a negative offset from the data pointer) is an FPC implementation detail that DWARF does not describe.

Actually it does. location expressions (or some other expression). The bounds can be an expression, and the bounds can be anywhere in memory, or a constant.

What isn't avail is the refcount => Which is normally not needed to debug user code. Only if the compiler generates bad code, and one debugs internals of the data handled by the compiler, then that is needed.

So it is partly how FPC implements DWARF, but also partly that DWARF simply does not have vocabulary for these Pascal concepts.

Strings are the biggest issue. Especially when fpc follows Delphi with zero based index as option....

IIRC DWARF 4 has some string type, but I don't know if that applies at all, and it doesn't cover the whole set of possibilities.

But then again, submitting to DWARF is easy.
What it really needs is for someone to implement it into FPC. (But then for a completely new format, that is the same, only that you volunteered as the someone, creating that advantage for your format)

Reply by email

Unknown author
to Martin Frb: The biggest sore with DWARF that I was aware of was properties. But they have been added…
Martin Frb wrote
The biggest sore with DWARF that I was aware of was properties. But they have been added to the DWARF standard (including indexed and more).

ps: apologies for the wall of text ;-)

You are correct that many of these issues could theoretically be addressed within DWARF. FPC could emit better DWARF, fpdebug could interpret it more thoroughly, and the DWARF standard does continue to evolve.

However, there is a practical reality: every Pascal concept that DWARF does not natively represent requires a custom encoding convention, a corresponding interpretation in the debugger, and ongoing maintenance as both FPC and the DWARF standard evolve. Properties, managed types, sets, variant records, nested procedure scoping — each one is a separate workaround layered on top of a format designed for C.

The scoping example is illustrative. You noted that FPC does not currently emit the information that a variable declared after a nested procedure is not visible to that procedure. I implemented this in OPDF, in just a few minutes, with a simple DeclIndex field -- a 2-byte addition to two record types. In DWARF, this would require either extending DW_AT_start_scope usage (which GDB and LLDB interpret inconsistently) or inventing a custom attribute, then teaching fpdebug to interpret it. The OPDF approach took a a few minutes; the DWARF approach would require navigating compatibility constraints with multiple debugger frontends.

The question is not whether it is possible to encode Pascal semantics in DWARF, but whether it is the best use of limited development resources. Borland/Embarcadero faced the same decision and chose a custom format (TD32, later PDB hybrid). The result was a superior debugging experience that the DWARF-based open source toolchains have never matched.

I don't know what the new format does in terms of calling params, and stack resolution....

OPDF currently handles local variables, function parameters, and stack frame resolution. Each local variable and parameter record includes a ScopeID (linking it to a function scope), a LocationExpr (location expression type -- RBP-relative for stack variables, with parent-frame chain support for nested procedures), and a LocationData field (the stack offset). Function scope records define the address range (LowPC/HighPC) for each function.

The debugger uses this to resolve stack frames, evaluate local variables at any point in the call stack, and navigate the callstack with full symbol resolution. We recently tested this on a 93-unit real-world application and successfully resolved
callstacks 19 frames deep with correct local variable access.

You are right that DWARF provides richer expression evaluation capabilities (the DWARF expression machine is essentially a stack-based virtual machine). OPDF's current approach is simpler and covers the common cases. More complex location expressions can be added as needed -- but the advantage is that we add them in direct response to Pascal debugging needs, not as workarounds within a C-centric specification.

The broader point: OPDF is not about what DWARF cannot do -- it is about the cost of continuously encoding Pascal semantics into a format that was not designed for them, versus investing in a format that represents them natively.

Finally, on a personal note -- OPDF grew out of the same kind of curiosity that led me to create fpGUI Toolkit. With fpGUI, I wanted to understand how GUI frameworks worked from the ground up, so I built one. When Duby was released, it sparked a similar curiosity about how debuggers work internally. OPDF and the PDR debugger are the fruit of that exploration -- a desire to understand the full stack, from compiler-emitted debug records through to variable evaluation in a running process.

Hopefully after I make the Pull Request public [soon] and share my documentation, it will all make more sense.

Regards,
Graeme

Reply by email

Martin Frb
to Unknown author
Graeme Geldenhuys wrote
. In DWARF, this would require either extending DW_AT_start_scope usage (which GDB and LLDB interpret inconsistently) or inventing a custom attribute, then teaching fpdebug to interpret it.

Actually, as I said, defining a local var in the nested proc with location expression. Nothing custom.
Amount of work I can't compare, don't know yours. But also can't estimate how much it would be for someone who has comparable knowledge on FPC *AND* DWARF.
(I.e. for me to add to your code, would be considerable more work, because I wouldn’t have the background knowledge / so comparisons aren't that simple)

Saying "Gdb/lldb" wont understand is a bit of a strange argument. Will they understand your new format?

The question is not whether it is possible to encode Pascal semantics in DWARF, but whether it is the best use of limited development resources.

But, adding an extra debug info format, and maintain BOTH will require even more resources. I would assume...

And a complete switch would mean that there needs to be a debugger for the new format that can handle all architectures
(intel/arm/amiga/embedded/...)
And assume that no one wants to use gdb for reverse stepping (or implement that too).

I'd say, having such a debugger would be cool....
But, even then, there are people who step into kernel, or dll => those have dwarf debug info => so the new debugger needs to be able to understand both.

I am not trying to in any way "oppose" your effort. Haven't even seen it yet. More choice is going to be nice.
But your reasoning so for, sorry but some of the arguments against dwarf do not stand.

 Borland/Embarcadero faced the same decision and chose a custom format (TD32, later PDB hybrid). The result was a superior debugging experience that the DWARF-based open source toolchains have never matched.

I don't have much background on that. But isn't that a decision from a very long time ago? Back when dwarf was way less than today.

But also, yes rolling your own you can go shortcuts save some work/money, by being less generic, and only cover what you need. Again that ends when apps step into libraries and kernel.  To support that, then suddenly means twice the work.

And of course doing there own, on closed source may give them extra control over their clients....

Show quoted text 5 lines
The broader point: OPDF is not about what DWARF cannot do -- it is about the cost of continuously encoding Pascal semantics into a format that was not designed for them, versus investing in a format that represents them natively.

That is a different point from many of your previous statements.

How, then do you solve stepping into kernel, or libraries (that have dwarf debug info, and aren't written in Pascal)?
Well, yes, you still save on **encoding** Pascal into Dwarf. But you have to decode 2 debug formats. (or more, depending on which others you want to support, in any case: 1 more than otherwise)

Show quoted text 9 lines
Finally, on a personal note -- OPDF grew out of the same kind of curiosity that led me to create fpGUI Toolkit. With fpGUI, I wanted to understand how GUI frameworks worked from the ground up, so I built one. When Duby was released, it sparked a similar curiosity about how debuggers work internally. OPDF and the PDR debugger are the fruit of that exploration -- a desire to understand the full stack, from compiler-emitted debug records through to variable evaluation in a running process.

That is understandable.

My earlier point on "stack resolution" wasn't fully answered though.

How to you encode: How to find the address of the parent stack frame? This may not be in the RBP register at all times.
Some functions don't even have a frame.
And, when entering a function then the frame isn't set up yet. So finding the parent needs debug info too.

Dwarf has some info there, but of course a debugger then needs even more, as it may have to resolve through parts that don't have debug info (callbacks going through the kernel), but when going enough frames there will be further frames with debug info.
(gdb still beats fpdebug in some cases... argh).

Reply by email

Unknown author
to Martin Frb
Martin Frb wrote
My earlier point on "stack resolution" wasn't fully answered though. How to you encode: How to find the address of the parent stack frame?

You raise a valid point. My current implementation relies on frame pointers (RBP chain) being present -- [RBP] gives the parent frame, [RBP+8] gives the return address. This works reliably for FPC-compiled x86/x86_64 code because FPC generates frame pointers for all functions by default, and you only lose them with explicit -Oo optimisations, which are uncommon in debug builds. However, I am aware this will not work for all architectures. I only have access to x86/x86_64 systems at this time.

Also, it does break in the cases you describe: frame pointer omission, function prologue before the frame is set up, or callstacks passing through external C code compiled without frame pointers. DWARF handles this with .debug_frame / .eh_frame unwind tables that describe frame resolution rules at every instruction boundary.

OPDF does not have an equivalent today -- but it is not a design constraint. The format is extensible, and unwind information (a recUnwindInfo record mapping address ranges to frame resolution rules) can be added when the need arises.

At this point I'm focused on debugging FPC-compiled code rather than mixed-language stacks.

Regards,
- Graeme -

Reply by email

Martin Frb
to Unknown author
Graeme Geldenhuys wrote 10 lines
On 2026-02-21 17:36, Martin Frb via fpc-devel wrote: > My earlier point on "stack resolution" wasn't fully answered though. > How to you encode: How to find the address of the parent stack frame? You raise a valid point. My current implementation relies on frame pointers (RBP chain) being present -- [RBP] gives the parent frame, [RBP+8] gives the return address. This works reliably for FPC-compiled x86/x86_64 code because FPC generates frame pointers for all functions by default, and you only lose them with explicit -Oo optimisations, which are uncommon in debug builds.

Actually, no you are wrong (technically correct, but...). E.g. if you stop at an assert or exception.

Then you are in RTL code => Many people using Lazarus, use the prebuild RTL. That prebuild RTL is O2 (and without debug info, so encoding stack in debug info wont help in this case).

But a common issue, that we had for a very long time was, that when you
stopped in such an exception, in a way that
procedure ButtonClick(...); // or really any code of yours
begin
   SomeCall to RTL; // not all, but some
end;

Then the stack would be
0: raise
1: somecall
2: Caller_of_ButtonClick

But ButtonClick wasn't in there, because "somecall" did not have a stackframe. which means RBP still had the value of ButtonClicks stackframe. But RIP of course is in SomeCall.
Resolving from that RBP would of course go to the caller of ButtonClick (because its the frame for ButtonClick, and if you get the caller from that....)

Though of course, this issue can't be completely solved in debug info, because as I said => Somecall doesn't have any.

Reply by email

Unknown author
to Martin Frb
Martin Frb wrote
Then you are in RTL code => Many people using Lazarus, use the prebuild RTL. That prebuild RTL is O2 (and without debug info, so encoding stack in debug info wont help in this case).

Umm, an honest oversight on my part -- I had not considered the prebuilt RTL scenario. The frame-pointer chain breaking when you hit optimised code. I have added it to the OPDF roadmap as a known limitation.

One partial mitigation worth exploring: FPC could emit frame pointers for the RTL even at -O2, similar to how GCC now defaults to -fno-omit-frame-pointer on many Linux distros (it was re-enabled specifically because profiling and debugging tools kept breaking without it). Whether the FPC team would accept that trade-off for the RTL is another question -- it does cost a register.

Longer term, the OPDF debugger could fall back to reading .eh_frame sections when no OPDF data is available for a given frame. Those unwind tables should be present even in release builds, so they could bridge the gap for frameless RTL functions.

Regards,
- Graeme -

Reply by email

Martin Frb
to Unknown author
Graeme Geldenhuys wrote 9 lines
On 2026-02-21 18:42, Martin Frb via fpc-devel wrote: > Then you are in RTL code => Many people using Lazarus, use the > prebuild RTL. > That prebuild RTL is O2 (and without debug info, so encoding stack in > debug info wont help in this case). Umm, an honest oversight on my part -- I had not considered the prebuilt RTL scenario. The frame-pointer chain breaking when you hit optimised code. I have added it to the OPDF roadmap as a known limitation.

...

Show quoted text 5 lines
Longer term, the OPDF debugger could fall back to reading .eh_frame sections when no OPDF data is available for a given frame. Those unwind tables should be present even in release builds, so they could bridge the gap for frameless RTL functions.

As I said, not really an issue with debug format. No format can fix that.

And  yes exception frame info is one answer (still on my todo for fpdebug). Unwinding is never 100% possible, unless all code adheres to the rules known by the debugger. But such compliance can't be guaranteed when unknown libs are involved.

Anyway, I was just interested in you take on that side. Describing data (incl type) is one huge part. But by far not all.

Line info is of course also huge. Not so many special cases Well, fpc attributes some "end" code to the "begin" block. Afaik that is in the node/code gen, not related to debug info, but then makes it way into debug info.

Jump pads are an interesting bit too.
I don't know if you added line info to the entry points of interfaces (the small bit of code that adjust the self pointer, and jumps to the real method).
But IIRC you also can get jump pads in large loops, where the conditional jump doesn't reach the target, and so it goes to an unconditional jump statement that jumps to the real target. Again, less a question of debug info (unless you want to actually mark them as such). More a task for the engine to detect those bits of code.

Btw, how do you handle "step over" into a finally block (which is a hidden "step in")?

Reply by email

Hairy Pixels
to Martin Frb: The biggest sore with DWARF that I was aware of was properties. But they have been added…

On Feb 21, 2026 at 12:19:10 AM, Martin Frb via fpc-devel <

Show quoted text 14 lines
wrote: E.g. currently procedure foo; var SeenByBar: byte; procedure Bar; begin {} end; var NOT_SeenByBar: byte; begin {} end; FPC currently does not include the info that the 2nd var is not in scope for Bar. There would afaik be several ways. So not a Dwarf problem.

Oh yes not having the captured variables for nested procedures is VERY annoying and wastes lots of time. That's probably the biggest problem I have but I don't think that has anything to do with DWARF, just the compiler never implemented it.

Reply by email

Kostas Michalopoulos
to Martin Frb: Actually, as I said, defining a local var in the nested proc with location expression.…
Martin Frb wrote
Well, yes, you still save on **encoding** Pascal into Dwarf. But you have to decode 2 debug formats. (or more, depending on which others you want to support, in any case: 1 more than otherwise)

FWIW in practice FPC being able to emit DWARF debug information is useful for other tools too, like Valgrind and Perf (and other performance analyzers) so i do not think that dropping DWARF support is a good idea.

Of course a better debug experience with FPC + Lazarus would be great because there are always annoyances here and there but that shouldn't happen by removing existing functionality (being able to step into non-FPC code) and interoperability (being able to use FPC binaries with any tool that understands DWARF).

Reply by email

Martin Frb
to Kostas Michalopoulos
Kostas Michalopoulos wrote 15 lines
On 2/21/26 7:36 PM, Martin Frb via fpc-devel wrote: > Well, yes, you still save on **encoding** Pascal into Dwarf. But you > have to decode 2 debug formats. (or more, depending on which others > you want to support, in any case: 1 more than otherwise) FWIW in practice FPC being able to emit DWARF debug information is useful for other tools too, like Valgrind and Perf (and other performance analyzers) so i do not think that dropping DWARF support is a good idea. Of course a better debug experience with FPC + Lazarus would be great because there are always annoyances here and there but that shouldn't happen by removing existing functionality (being able to step into non-FPC code) and interoperability (being able to use FPC binaries with any tool that understands DWARF)

Aside from his "he wanted to do it for the experience" the reasoning about Dwarf is - as previously indicated - IMHO incorrect. Dwarf supports a lot more than we use. And can be extended (official and fpc-only).

I would say the biggest problems with debugging today (Win/Linux / Intel) is caused by missing implementation in the compiler.

And well, his work including a new info-format fixes this, that is true. But...
 It isn't the new format that does the fix, it is the fact that he implemented it into the compiler.
 Had he done the same using Dwarf, and he would have had the same fixes.

Yes, true as well, that it may be a little bit less work doing so with the new format. Not because Dwarf is so "C biased", but because Dwarf is way more generic. But, the upside is, that then you get a choice of way more debuggers, for way more target architectures.

So from all the arguments I saw sofar, the only one that stuck was "he wanted to do it for the experience".

For all else I couldn't help but think of: https://xkcd.com/927/ (But at the same time admit, in other case I have myself done the same an paved my own road, because I could, and I wanted)

Anyway, still haven't seen the result...

Reply by email

Tomas Hajny
to Martin Frb
Martin Frb wrote 32 lines
On 22/02/2026 19:11, Kostas Michalopoulos via fpc-devel wrote: > On 2/21/26 7:36 PM, Martin Frb via fpc-devel wrote: >> Well, yes, you still save on **encoding** Pascal into Dwarf. But you >> have to decode 2 debug formats. (or more, depending on which others >> you want to support, in any case: 1 more than otherwise) > > FWIW in practice FPC being able to emit DWARF debug information is > useful for other tools too, like Valgrind and Perf (and other > performance analyzers) so i do not think that dropping DWARF support > is a good idea. > > Of course a better debug experience with FPC + Lazarus would be great > because there are always annoyances here and there but that shouldn't > happen by removing existing functionality (being able to step into > non-FPC code) and interoperability (being able to use FPC binaries > with any tool that understands DWARF) Aside from his "he wanted to do it for the experience" the reasoning about Dwarf is - as previously indicated - IMHO incorrect. Dwarf supports a lot more than we use. And can be extended (official and fpc-only). I would say the biggest problems with debugging today (Win/Linux / Intel) is caused by missing implementation in the compiler. And well, his work including a new info-format fixes this, that is true. But...  It isn't the new format that does the fix, it is the fact that he implemented it into the compiler.  Had he done the same using Dwarf, and he would have had the same fixes.

.
.

To be honest, I don't see this as an issue. There's nothing suggesting that DWARF support would or should be dropped. FPC supports two different debug formats at the moment, there's no reason why three should be an issue, especially if there's somebody interested to continue supporting this third format. Having certain features supported in the compiler for OPDF probably provides at least some hints for somebody sufficiently knowledgeable about DWARF to implement similar support in this format as well - from this point of view it would be direct benefit in any case. And if somebody wants to debug certain code and encounters some issues with one format, having a possibility to compile the same code with a different debug information format to overcome these issues is great from my point of view...

Just my 2 cents

Tomas

Reply by email

Unknown author
to Kostas Michalopoulos: FWIW in practice FPC being able to emit DWARF debug information is useful for other tools…
Kostas Michalopoulos wrote
...so i do not think that dropping DWARF support is a good idea.

To be very clear, I have no intention of suggesting we drop DWARF support. As others have noted, its external tooling support is massive and essential.

My research into debugger internals simply led me to a "what if" scenario: I realised how much easier things could be using a more Pascal-like approach on both the compiler and client sides. By using simple, descriptive Pascal records that are easily extensible, the architecture remains remarkably clean. So far, that theory has held up well.

Every time I've envisioned a new feature, it has been trivial to add. Also, because I've designed the debug engine using the Ports & Adapters (Hexagonal) pattern, adding IDE support or external tooling requires very little effort. As a proof of concept, my reference CLI debugger is a single-unit application of only 530 lines, including comments.

I am very close to making the code public for review and feedback; I just have one or two last-minute items I'd like to polish before sharing it. I was just so excited to see the architecture taking shape that I thought I would reveal what's coming a few days early. I look forward to hearing everyone's thoughts once the source is up.

Regards,
- Graeme -

Reply by email

J. Gareth Moreton
to Unknown author

I'm certainly looking forward to taking a proper gander at it, as I'm all for supporting people exploring the internals of development, and if it proves to be extremely flexible and useful, you'll get a vote from me, but have to see it in action first!

Gareth aka. Kit

Graeme Geldenhuys wrote 34 lines
On 2026-02-22 18:11, Kostas Michalopoulos via fpc-devel wrote: > > ...so i do not think that dropping DWARF support is a good idea. To be very clear, I have no intention of suggesting we drop DWARF support. As others have noted, its external tooling support is massive and essential. My research into debugger internals simply led me to a "what if" scenario: I realised how much easier things could be using a more Pascal-like approach on both the compiler and client sides. By using simple, descriptive Pascal records that are easily extensible, the architecture remains remarkably clean. So far, that theory has held up well. Every time I've envisioned a new feature, it has been trivial to add. Also, because I've designed the debug engine using the Ports & Adapters (Hexagonal) pattern, adding IDE support or external tooling requires very little effort. As a proof of concept, my reference CLI debugger is a single-unit application of only 530 lines, including comments. I am very close to making the code public for review and feedback; I just have one or two last-minute items I'd like to polish before sharing it. I was just so excited to see the architecture taking shape that I thought I would reveal what's coming a few days early. I look forward to hearing everyone's thoughts once the source is up. Regards,   - Graeme - _______________________________________________ fpc-devel maillist  -  https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
This email has been checked for viruses by Avast antivirus software. www.avast.com

Reply by email