Recent

Author Topic: Getting name / line of calling code  (Read 378 times)

Prime

  • Jr. Member
  • **
  • Posts: 77
Getting name / line of calling code
« on: May 10, 2026, 02:48:50 pm »
Hi all,

Is there a way of getting the source file and line of the code calling a procedure?

Why I want to do this, I have a debug unit, that sends debug messages to a server process (which may or may not be running n the same machine) so that I can insert Debug.Log('blah %d',[SomeVar]) and in the debug process I get 'blah 2112' (or whatever the var's value is in the server's window). It would be useful, if this message could include the line and sourcefile name. Obviously I realise that my code would have to be compiled with debugging symbols enabled or somesuch.

Is there a way to do this?

Cheers.

Phill.

Thaddy

  • Hero Member
  • *****
  • Posts: 19278
  • Glad to be alive.
Re: Getting name / line of calling code
« Reply #1 on: May 10, 2026, 03:27:47 pm »
Best bet is to compile with -glh which provides that information.
objects are fine constructs. You can even initialize them with constructors.

simone

  • Hero Member
  • *****
  • Posts: 704
Re: Getting name / line of calling code
« Reply #2 on: May 10, 2026, 03:29:14 pm »
I use the following procedure in these cases. I hope it helps you too.

Code: Pascal  [Select][+][-]
  1. procedure WriteCaller;
  2.   //require lineinfo unit. better use -gl compiler switch
  3.   var
  4.     PrevFrame: Pointer;
  5.     CallerAddr: Pointer;
  6.     SourceFile : ShortString='';
  7.     Func : ShortString='';
  8.     LineNum: longint=0;
  9.   begin
  10.     // Get the stack frame of the procedure that called LogCaller
  11.     PrevFrame:=Get_Caller_Frame(Get_Frame);
  12.     // Get the return address from that frame
  13.     CallerAddr:=Get_Caller_Addr(PrevFrame);
  14.     // Translate address to source info
  15.     {$WARN 4055 OFF}if GetLineInfo(PtrUInt((CallerAddr)),Func,SourceFile,LineNum) then {$WARN 4055 ON}
  16.       begin
  17.         writeln('Detected Caller:');
  18.         writeln('  Function: ', Func);
  19.         writeln('  File:     ', SourceFile);
  20.         writeln('  Line:     ', LineNum);
  21.       end
  22.     else
  23.       writeln('Could not determine caller. Ensure -gl is enabled.');
  24.   end;
Microsoft Windows 10/11 64 bit - Lazarus 3.8/4.0 FPC 3.2.2 x86_64-win64-win32/win64

Prime

  • Jr. Member
  • **
  • Posts: 77
Re: Getting name / line of calling code
« Reply #3 on: May 10, 2026, 04:23:19 pm »
Excelent, that's exactly what I needed.

Cheers.

Phill.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12430
  • Debugger - SynEdit - and more
    • wiki
Re: Getting name / line of calling code
« Reply #4 on: May 10, 2026, 05:06:55 pm »
If this is for distributed/released apps:

You can also send addresses, and then have the receiver do the look up. => but you need the exact matching debug info for the exe that supplied the addresses.

The Lazarus "LeaksAndTraces" package has an implementation for that. It can take a stacktrace with addresses only, and if it has a copy of the exe with debug info (before they were removed with strip) then it can get the line info.

jamie

  • Hero Member
  • *****
  • Posts: 7776
Re: Getting name / line of calling code
« Reply #5 on: May 10, 2026, 05:24:44 pm »
This is why I liked/Like the MAP File Delphi can generate. I place that in the final revision that gets released, any reports, I can just look at that.

Jamie
The only true wisdom is knowing you know nothing

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12430
  • Debugger - SynEdit - and more
    • wiki
Re: Getting name / line of calling code
« Reply #6 on: May 10, 2026, 05:46:37 pm »
This is why I liked/Like the MAP File Delphi can generate. I place that in the final revision that gets released, any reports, I can just look at that.

Compile you final release either
- with external debug info
- with debug info, and then (when done) strip it

use "objdump --dwarf=decodedline  your_exe" to get addresses for all lines in a human readable form. (their also is a tool "dwarfdump" that should be able to do that).

Or use gdb or lazarus (or probably lldb) together with the binary dwarf debug info file, and look up addresses as you need them.


Of course any such look up may fail if address space randomization shuffled stuff around.

Zvoni

  • Hero Member
  • *****
  • Posts: 3404
Re: Getting name / line of calling code
« Reply #7 on: May 11, 2026, 08:17:09 am »
IIRC, the Include-Directive should always work, irrespective of Debug-Infos yes/no, since it's resolved at compile-time

Code: Pascal  [Select][+][-]
  1. Writeln('I am in Unit='+{$I %file%}+' - at LineNum='+{$I %line%});
See also: https://wiki.freepascal.org/$include
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

MarkMLl

  • Hero Member
  • *****
  • Posts: 8572
Re: Getting name / line of calling code
« Reply #8 on: May 11, 2026, 10:54:12 am »
IIRC, the Include-Directive should always work, irrespective of Debug-Infos yes/no, since it's resolved at compile-time

Furthermore I believe %file% and %line% have been available since time immemorial although some of the more detailed expansions are more recent.

Simone's suggestion of Get_Caller_Addr() etc. have been available since at least v2.2.2, although referring to the RTL sources their presence appears to be announced by platform-specific defines.

I've been habitually using this sort of thing for a while, particularly in e.g. a recursive-descent parser:

Code: Pascal  [Select][+][-]
  1. (* Parse the PEC stitchlist section.
  2. *)
  3. function pec_stitchListSubsection(): boolean;
  4.  
  5. const
  6.   thisName= 'pec_stitchListSubsection()';
  7.  
  8. begin
  9. {$ifdef HAS_CURRENTROUTINE }
  10.   Assert(thisName = {$I %CURRENTROUTINE%} + '()', 'Internal error: bad name in ' +
  11.                                                 {$I %CURRENTROUTINE%} + '()');
  12. {$endif HAS_CURRENTROUTINE }
  13.   result := false;
  14.   pushRule(thisName);
  15. ...
  16.  

However the %CURRENTROUTINE% expansion is only available from 3.2.0 onwards, so at the start of the unit (or in a common .inc file) I have something like

Code: Pascal  [Select][+][-]
  1. {$undef HAS_CURRENTROUTINE     }
  2. {$if FPC_FULLVERSION >= 030200 }        (* Requires FPC 2.2.4 minimum           *)
  3. {$define HAS_CURRENTROUTINE    }        (* Requires FPC 3.2.0 minimum           *)
  4. {$assertions on                }        (* Make sure name checks are operative  *)
  5. {$endif FPC_FULLVERSION        }
  6.  

which requires a minimum of 2.2.4... I'd be surprised if anybody is still running anything that old unless they're testing GTK1 compatibility.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

ccrause

  • Hero Member
  • *****
  • Posts: 1119
Re: Getting name / line of calling code
« Reply #9 on: May 11, 2026, 01:09:43 pm »
This is why I liked/Like the MAP File Delphi can generate. I place that in the final revision that gets released, any reports, I can just look at that.

Jamie

Pass -Xm to compiler to get linker to generate map file.

Thaddy

  • Hero Member
  • *****
  • Posts: 19278
  • Glad to be alive.
Re: Getting name / line of calling code
« Reply #10 on: May 11, 2026, 01:58:46 pm »
Yes. Seldom necessary but -Xm generates a fine map file.
objects are fine constructs. You can even initialize them with constructors.

 

TinyPortal © 2005-2018