pleriche / FastMM5

FastMM is a fast replacement memory manager for Embarcadero Delphi applications that scales well across multiple threads and CPU cores, is not prone to memory fragmentation, and supports shared memory without the use of external .DLL files.
283 stars 73 forks source link

returning the procedure's Local Variables ? #38

Open dcabale opened 1 year ago

dcabale commented 1 year ago

Hey there, RADStudio Memory management documentation says this about procedure's local variables: image

These "Local Variables" can be accessed, using the IDE debugger, with a [Name, Value] pair list, such as this : image

The application Stack is accessible by FastMM5, at the memory allocation event. Considering this, would FastMM5 be able, on that event, to return also these Local Variables, just as the IDE debugger does? Thanks for your support.

pleriche commented 1 year ago

Theoretically it would be possible to walk the stack backwards to the calling procedure and obtain the values of the local variables that are stored on the stack, but the local variables held in registers would not be accessible.

Practically though it's not really feasible. Obtaining the local variables would be computationally expensive. If the local variable values are required later they would need to be copied, increasing memory overhead as well. Finally, when it comes to interpreting the local variables there would have to be code that is able to convert the local variable values to human readable form.

dcabale commented 1 year ago
  1. Thanks for your quick answer.
  2. Please excuse my ignorance on this, but what fundamental difference does exist, at the time of a memory block allocation, between what is available to FastMM and what is available to the RADStudio IDE, displaying the Local Variables table? How does the IDE read the registers ?
  3. Let me put aside your computer-expensiveness argument, as my goal would be to analyze what the application does, and thus set in Debug configuration only.
  4. About the local variables interpretation in human readable form : I suppose this could be done with the help of the .map file, couldn't it? Again, the IDE does that.
pleriche commented 1 year ago

(2) The debugger displays the variables for the code block in which the debugger has stopped. At that point the registers contain the values for the local variables. If I understand you correctly you want FastMM to capture the local variables for the routine that called into FastMM (via GetMem, FreeMem, etc.). At the point that FastMM gains control the registers will contain the parameters for the e.g. GetMem request. FastMM would have to walk the call stack backwards through all the callers and collect whatever local variables it can get off the stack. Many of the local variables would have been in registers before the call sequence, thus not available anymore.

(4) The IDE uses debugger visualizers. They are many and they can be complex. Consider for example records, you would probably want to drill down into the fields for them. It's not a simple task to emulate this runtime.

I'm sure it will be possible to do something in line with what you're asking, but the quality of the results may disappoint due to (2), with the consequence that doing (4) would probably not be a good investment.

dcabale commented 1 year ago
  1. FastMM would have to walk the call stack backwards through all the callers and collect whatever local variables it can get off the stack.

Correct.

  1. Many of the local variables would have been in registers before the call sequence, thus not available anymore.

I'm not following you there : the local variables should be stored in the stack for each called function: from RADStudio Memory management documentation image and from Memory Management: Frame Allocation image Is that not correct ? What is the "registers" you are referring to ?

  1. If someone would ask you to do it, how would you emulate IDE-like debugger /variable visualizer?

Thanks for your help.

pleriche commented 7 months ago

What is the "registers" you are referring to ?

I am referring to the CPU registers. With optimization enabled the CPU registers will be used to store many local variables (instead of the stack). Before a subroutine is called the local variables of the caller that are held in registers will either be pushed onto the stack or shuffled to non-volatile registers if they need to persist beyond the call, or otherwise they will just be discarded. Determining the local variables of the caller from the callee will thus be very hard, and in many cases impossible.

dcabale commented 7 months ago

Thanks for your reply

  1. It is no question to "enable optimization" when it stands for program analysis. => I expect the local variables to be held in the stack frame, not in CPU registers
  2. How is that the Delphi IDE can retrieve the caller's local variables, when the program is stopped at a callee's breakpoint ? image
pleriche commented 7 months ago

With optimization disabled the local variables will be on the stack, and in that case it would be possible to collect the local variables for all the callers, provided you have access to all the debug information the debugger has.

Assuming you are able to get to all the local variables of the caller routine like the debugger does, you would still face the issue of how and where you're going to store all those values in case you need to display them later in a crash dump. If you were to capture all the local variables of the entire call stack every time GetMem, FreeMem, etc. is invoked you would run out of memory very quickly.

The point I made in my first response stands: It's too expensive both computationally as well as in storage required to make adding capturing of local variables together with stack traces practical. It's also not a small development undertaking to develop the code to be able to present captured local variable values in human readable form.

dcabale commented 6 months ago

Let me precise my goal: my goal is to use FastMM as a back-bone for building my own call-graph application, as a code-comprehension tool Here is how I imagine the graph display: image

But let's come back to your arguments:

  1. "issue of how and where you're going to store all those values in case you need to display them later in a crash dump." My intent is not to use that feature in a crash dump. As I said before, I would use FastMM as a back-bone of my program.
  2. "all the local variables of the entire call stack every time GetMem, FreeMem, etc. is invoked you would run out of memory very quickly".."It's too expensive both computationally as well as in storage required to make adding capturing of local variables together with stack traces practical" Correct. Given this, parts of the application only could be zoomed in. Could be only a single method. How ? (just an idea) we could use the "Allocation Group" feature that would filter the allocated blocks, minimizing the overhead. But I believe you better than me could bring the best solution for this issue.
  3. "It's also not a small development undertaking to develop the code to be able to present captured local variable values in human readable form." The IDE already does that. May not Embarcadero help us with this?

Anyway, if you consider that FastMM cannot be held as the back-bone for such a tool, then what would you advise to satisfy my need ?

pleriche commented 6 months ago

What you're describing would be a great debugging tool, if you can pull it off. In my opinion that is a big "if". You have to ask yourself why this has not been done before. I believe the answer lies in the issues I have touched on in my previous responses, and likely additional stumbling blocks that may only be revealed once you try to get it off the ground.