29

Is there any way to know the name of a method I'm currently in?

So that:

procedure TMyObject.SomeMethod();
begin
  Writeln('my name is: ' + <hocus pocus>); 
end;

would produce this output:

my name is: SomeMethod

tomazy
  • 905
  • 1
  • 8
  • 11

3 Answers3

32

JCL is free and has functions for that. It does depend on how well a stack trace can be made and how much debug information is present.

JclDebug.pas

function FileByLevel(const Level: Integer = 0): string;
function ModuleByLevel(const Level: Integer = 0): string;
function ProcByLevel(const Level: Integer = 0): string;
function LineByLevel(const Level: Integer = 0): Integer;
Lars Truijens
  • 42,837
  • 6
  • 126
  • 143
11

See also our TSynMapFile class.

It is able to load a .map file, and compress it into an optimized binary format. It will be much smaller than the .map itself (e.g. 900 KB .map -> 70 KB .mab). This .mab can be easily embedded within the exe. It is therefore smaller than the format used by JCL or MadExcept, and also smaller than the information embedded at compile time by Delphi.

You'll use it as such:

Map := TSynMapFile.Create; // or specify an exe name
try
  i := Map.FindSymbol(SymbolAddr);
  if i>=0 then 
    writeln(Map.Symbols[i].Name);
  // or for your point:
  writeln(Map.FindLocation(Addr)); // e.g. 'SynSelfTests.TestPeopleProc (784)'
finally
  Map.Free;
end;

For instance, here is how it is used from our logging classes.

procedure TSynLog.Log(Level: TSynLogInfo);
var aCaller: PtrUInt;
begin
  if (self<>nil) and (Level in fFamily.fLevel) then begin
    LogHeaderLock(Level);
    asm
      mov eax,[ebp+4]  // retrieve caller EIP from push ebp; mov ebp,esp
      sub eax,5        // ignore call TSynLog.Enter op codes
      mov aCaller,eax
    end;
    TSynMapFile.Log(fWriter,aCaller); // here it will call TSynMapFile for the current exe
    LogTrailerUnLock(Level);
  end;
end;

This method is able to retrieve the caller's address, and log its unit name, method name and line number.

Note/edit: the source code of the mORMot log unit is SynLog.pas. The updated documentation is reacheable at this URI.

Arnaud Bouchez
  • 42,305
  • 3
  • 71
  • 159
  • Could you use this for logging a call stack from a normal debug build (or release with an external map?) without having to add, eg, Jedi debug code? In some specific circumstances it could be very useful to have code that could log and report where it was called from. – David Jan 28 '14 at 19:27
  • @DavidM Yes, you can do this. If there is no .map/.mab attached, it would log the hexadecimal addresses. Then our LogView tool is able to retrieve the source code line from an existing .map file matching the .exe. But of course, since our .mab format is very small, I do not see any reason not to embed it to the .exe, at compile time. – Arnaud Bouchez May 25 '15 at 09:21
  • @ArnaudBouchez Can you tell how to embed it to the .exe at compile time ? – SOUser Oct 18 '15 at 02:18
  • @XichenLi Please see https://github.com/synopse/mORMot/blob/master/SQLite3/Samples/11%20-%20Exception%20logging/Map2Mab.dpr – Arnaud Bouchez Oct 20 '15 at 08:57
  • @ArnaudBouchez the links at the top of the answer are broken. For anyone else coming across this post, the relevant unit appears to be here: https://github.com/synopse/mORMot/blob/master/SynLog.pas – Dave Nottage Jul 15 '20 at 21:25
1

If you have EurekaLog:

uses
  EDebugInfo;

procedure TMyObject.SomeMethod();
begin
  Writeln('my name is: ' + __FUNCTION__); 
end;

There is also __FILE__, __MODULE__, __UNIT__, __LINE__, as well as a generic GetLocationInfoStr function.

However:

  1. It will only work if you are compiling with some debug information (and the corresponding debug info provider is enabled):
  • EurekaLog has its own debug info format, which can be optionally compressed (which is actually not recomended, as you will spend more memory and CPU).
  • It also supports JCL/JEDI, Synopse/MAB, as well as .map, .tds/TD32, .dbg, .pdb.
  1. It is NOT a constant. The name will be looked up dynamically, so it have some run-time costs.
Alex
  • 5,477
  • 2
  • 36
  • 56