Recent

Author Topic: debugging "Free"  (Read 481 times)

Paolo

  • Hero Member
  • *****
  • Posts: 675
debugging "Free"
« on: January 09, 2026, 11:01:31 am »
Hello,      (laz-4.2, fpc-3.2.2, Win64)

I wrote this code as an example (code is meaningless, it is just to illustrate what I mean)

Code: Pascal  [Select][+][-]
  1.   { TA }
  2.  
  3. TA = class(TObject)
  4.   class var X : integer;
  5.   constructor Create;
  6.   destructor Destroy; override;
  7. end;
  8.  
  9. constructor TA.Create;
  10. begin
  11.   X:=-1
  12. end;
  13.  
  14. destructor TA.Destroy;
  15. begin
  16.   X:=0;
  17.   inherited Destroy;
  18. end;
  19.  
  20.   { TB }
  21.  
  22. TB = class(TA)
  23.   destructor Destroy; override;
  24. end;
  25.  
  26. destructor TB.Destroy;
  27. begin
  28.   X:=1;
  29.   inherited Destroy;
  30. end;
  31.  

then I do that, putting on both the code rows a breakpoint

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button2Click(Sender: TObject);
  2. var
  3.   B : TB;
  4. begin
  5.   B:=TB.Create; //<-- F7: here debugger goes inside and I see X = -1
  6.   B.Free;       //<-- F7: here the debugger passes over
  7. end;
  8.  

pressing F7 on breakpoint placed on B.free the debugger does no go inside the destructors,
to see X = 1 the X = 0 I have to put manually breakpoint inside both the destructors.

Is this behavior the expected one ? 

I would like pressing F7 to see my code steps executed.

Many thanks.

cdbc

  • Hero Member
  • *****
  • Posts: 2575
    • http://www.cdbc.dk
Re: debugging "Free"
« Reply #1 on: January 09, 2026, 11:27:58 am »
Hi
Yes, place the breakpoint in the 'Destroy' method.
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12030
  • Debugger - SynEdit - and more
    • wiki
Re: debugging "Free"
« Reply #2 on: January 09, 2026, 12:17:22 pm »
I assume your FPC (or more precise the RTL) is compiled without debug info?

So Free does not have debug info. Then the debugger does not step into it (like it doesn't step into any other method/routine of the RTL).
So, yes it is expected.

You can do an ASM step to get into it, and asm step to the call of Destroy. But that is not very comfortable.



The debugger always only checks the next statement, and checks if it has debug info.
- Stepping over/into means the debugger has a choice: step in if there is debug info / otherwise step over
- You will also notice that if you step the end of "Button1Click", it will return into the LCL (in this case even if there is no debug info, because it has no choice). It wont step to the OnMouseUp event, which may be the next bit of code that you wrote which will be executed. But the debugger can not know that the LCL will eventually call this, and not any other of your code.

The debugger doesn't know the meaning of "free". It doesn't even know there is "free" in the source, and even if it knew the name, that could have been re-used for some other code, that really does not want to be stepped into.

And even if the debugger knew "free", then it wouldn't know if the "call" inside free goes to a destructor with or without debug info. The latter would not want to be stepped into.


There may be a possibility for the future (a feature that could be considered). That would be to analyze the asm code of free, find it simple enough, and check if the code called by free has debug info.
This would be a sort of "jump pad" detection. Only that "free" isn't your classical jump pad, because it has actual functionality (the "IF <> nil"). Yet it is still of very small size.
It would need to be quite clever though, otherwise the debugger ends up doing a lot of extra steps...

The technique, but far far more basic, is currently used for calling methods on interfaces. They (if they go to a Pascal object) all go through a jump pad, but an actual classic form of it, very short, almost nothing but a forward. "mov reg, reg ; call xxx ; ret;"

In terms of "free" that can lead to inconsistent behaviour.

There may be other short wrapper methods that will call a debug-able bit of code. And the debugger must have a maximum search range for the nested call. (otherwise it would search for hours, if it didn't find something).
So there will always be cases were it detects the nested call, and cases were it does not. And for the end-user it will not be predictable. Because the debugger can only look at the potentially optimized asm, and then the same source can generate code that will and also will not be detected.

Paolo

  • Hero Member
  • *****
  • Posts: 675
Re: debugging "Free"
« Reply #3 on: January 09, 2026, 01:13:17 pm »
Thanks for the explaination

some notes just to give a feedback on the topic:

one is a feedback:
- from practical point of view pressing "F7" over B.free line and not going inside the "B.free -> B.destroy" gives the "false-impression" that the code in B.Destroy is not execute at all.
  might it be better to "forbid" break point on "free" if debug info are not availabe ?

two are from my "ignorance" on some technical topics, so be patient:
- how to enable "debug info" in RTL (and how to  check that) ? this shall be useful for this specific case ?
- if the debbuger can stop if a breakpoint is put inside the "TB.destoy" it seems to me that "some-kind-of-debug-info" should be there...

thanks again.

cdbc

  • Hero Member
  • *****
  • Posts: 2575
    • http://www.cdbc.dk
Re: debugging "Free"
« Reply #4 on: January 09, 2026, 01:56:11 pm »
Hi
Quote
might it be better to "forbid" break point on "free" if debug info are not availabe ?
Just put the breakpoint in 'Destroy' like the rest of us!
Quote
- if the debbuger can stop if a breakpoint is put inside the "TB.destoy" it seems to me that "some-kind-of-debug-info" should be there...
Only if __you__ override the default destructor (in TObject), if not --> NO debug-info.
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12030
  • Debugger - SynEdit - and more
    • wiki
Re: debugging "Free"
« Reply #5 on: January 09, 2026, 02:42:14 pm »
- from practical point of view pressing "F7" over B.free line and not going inside the "B.free -> B.destroy" gives the "false-impression" that the code in B.Destroy is not execute at all.
  might it be better to "forbid" break point on "free" if debug info are not availabe ?

Only because "Free" is so commonly used, it is forgotten that it is a function of its own.

If I have code like
  var MyList: TList;
  begin
....
     MyList.Sort(@MySortCompare);

Then the debugger never stops in MySortCompare (unless the rtl has debug info, and you step through the entire sorting code).

Same for

  Form2 := TForm2.create;
  Form2.Show;  // debugger does not step into OnFormShow
    // unless the LCL has debug info and you step the entire form show code

There of course you don't expect to debug the build in code for Sort or Show. And even if the LCL has debug info, then you may decide to intentionally step over it.

Well at least, in those cases it's less of a surprise. Because the code that is called within is nested deeper.
I am aware that a few people would like to be able to step-over any code between any 2 methods that they wrote. But that isn't possible. (Well, for that the debugger would have to set breakpoints at every function that the user may be interested in, so assuming the debugger can predict the interests of the user...)

Only "Free" isn't really thought of a function of its own. Its more perceived as an alias (despite adding functionality).

Hence, the idea with the "jump pad" detection... Though not sure if that will happen. There is con and pro.


Quote
- how to enable "debug info" in RTL (and how to  check that) ? this shall be useful for this specific case ?
You need to build your own FPC. The easiest way is fpcupdeluxe. Otherwise its download the sources, and call "make" with the option you want (and set up a start environment for the build....).
FpcUpDeluxe will do that for you.

Quote
- if the debbuger can stop if a breakpoint is put inside the "TB.destoy" it seems to me that "some-kind-of-debug-info" should be there...

There is debug info in TB.Destroy.
But there is none in TObject.Free. (which you call)

Just for experiment, open the ASM/assembler window, and use the step-into button in the asm view.

You will observe that there are some asm (mov/call) statements for the line "B.Free".
If you step into the "call" statement, the asm view will show you assembler without source code. This is the code from TObject.free.
This is about 5 to 10 asm statements. Some mov, cmp, je or jz, then a call.
If you step it using the "step into" button from the asm window, then the "call" will take you to your destroy code, and that will have sources (and debug info) again.







Paolo

  • Hero Member
  • *****
  • Posts: 675
Re: debugging "Free"
« Reply #6 on: January 10, 2026, 02:54:31 pm »
thanks for all the infos.

Ciao.

 

TinyPortal © 2005-2018