Forum > Debugger

Callstack in last call within method with generic function name only

(1/1)

CCRDude:
TL;DR: I've got a "debug logger" that I'm extending to help as a sort of profiler. I'm doing one call at the begin and one at the end of a method using the callstack. At the end of methods, the callstacks returns a more generic function name.

What I want to do is, and I prefer this one to the two below because of
* readability
* performance (without the compiler directive to enable profiling, these lines call simple inline procedures without active code inside)
* ease of maybe automatically adding code


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---function TTestClass.DoSomeThing(var AText: string): integer;begin   StartProfiling(Self, [AText]);   try      Result := Length(AText);   finally      EndProfiling([Result]);   end;end; 
I currently use the callstack (get_caller_stackinfo and GetLineInfo) to determine the method name. But during EndProfiling, those return $fin$0000002A instead.

If that would work during EndProfiling, I would use a TDictionary to simply locate the TFireflyProfilerMethod that was used to start the call (with some ugly stuff dealing with recursive calls probably).

My current workaround:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---function TTestClass.DoSomeThing(var AText: string): integer;var   profiling: TFireflyProfilerMethod;begin   profiling := StartProfiling(Self, [AText]);   try      Result := Length(AText);   finally      EndProfiling(profiling, [Result]);     // or profiling.EndProfiling([Result]);   end;end; 
or


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---function TTestClass.DoSomeThing(var AText: string): integer;begin   with StartProfiling(Self, [AText]) do   try      Result := Length(AText);   finally      EndProfiling([Result]);   end;end; 
Here's the code I use to locate the code position:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- {   Used to find the calling function that called this units code,   e.g. the one that uses the profiler.    @param(AFunction will receive the calling function name.)   @param(ASource will receive the name of the unit calling.)   @param(ALine will receive the line of code where the profiler was triggered.)    @author(Patrick Kolla-ten Venne)}procedure LocateCallingPosition(out AFunction, ASource: string; out ALine: longint); overload;var   sFunction: shortstring = '';   sSource: shortstring = '';   sThisSource: shortstring = '';   iLine: longint = 0;   pCurrentFrame: Pointer;   pCurrentAddress: CodePointer;begin   // current address is inside this unit.   pCurrentFrame := get_frame;   pCurrentAddress := get_pc_addr;   // get the parent, which is inside this unit...   get_caller_stackinfo(pCurrentFrame, pCurrentAddress);   GetLineInfo(PtrUInt(pCurrentAddress), sFunction, sThisSource, iLine);   // go up in hierarchy until first code line outside of this unit is found.   repeat      get_caller_stackinfo(pCurrentFrame, pCurrentAddress);      GetLineInfo(PtrUInt(pCurrentAddress), sFunction, sSource, iLine);      // In last lines of calling method, this returns:      // sFunction @$0179FA29 = '$fin$0000002A'   until (sSource <> sThisSource);   AFunction := sFunction;   ASource := sSource;   ALine := iLine;end;    
Here I'm a bit confused, since the debugger of Lazarus shows me a correct callstack, and I picked up the GetLineInfo hint from looking into Dump_Stack and what it uses back to the Dwarf implementation of the display of callstack information.

Martin_fr:
First of all, for profiling, I suggest to write addresses only, and then have a tool that runs the data, and resolves the addresses.
This may be a good bit faster than doing them during the run.


--- Quote from: CCRDude on July 14, 2023, 08:45:15 am ---Here I'm a bit confused, since the debugger of Lazarus shows me a correct callstack, and I picked up the GetLineInfo hint from looking into Dump_Stack and what it uses back to the Dwarf implementation of the display of callstack information.

--- End quote ---

The finally (SEH) runs in a function of it's own, hence the name....

But in order to behave like it was the same function as the "try" block is in, the finally uses the "RBP" (base pointer used as  stackframe) of the actual function.

Since the RBP is used to find the parent frame, the $fin looks like it has been called by the function that called the "pascal function containing it". So it hides all other callers. In reality it is either called by the OS (some unwind code) or by the "pascal function containing it".

The debug info does not have any link....

--------------
FpDebug does a bit of a trick here.

- The finally must have line numbers in the range of the "pascal function containing it"
- FpDebug finds the first line number of the finally (that may not be on the first address, which could be "begin" being mapped to the "end" line)e
- It then looks up the address of the line right before that (not sure there may be more than one)
- And that address can be used to find the "pascal function containing it"

If that is an other "finally" then recurse.


Mind, you can probably screw that up, if you put to much code on one line....
Not tested with anonymous functions....

------------------
It may be possible to get some info by parsing SEH records... But that is on the TODO list... So no info on it.


Because it has the same "RBP" (base pointer / stackframe)...

In your StartProfiling you could get the frame of the caller (of the function for which you log).
You could store that in a look up list
And in the finally you got look it up.

Of course that may be time consuming

Navigation

[0] Message Index

Go to full version