Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In debugging, how to know the stats (max-min, average, distribution...) of an argument in a repetitive calls to a function?

Supposed I have a function void Myclass::func(x), and various other code make thousands of calls to it. Now I want to know some stats of the argument x, for example, the average, max, min, or even a distribution graph.

void Myclass::func(int x) {
    while(foo.doFancyStuff(x)) {
        // ...
    }
}

Here's some ad-hoc methods that come to my mind:

  1. Print each value of x to log. Then use external tools/scripts to analyze them.
    • Caveats: Mixed with other info in logs. Writing each value of x into external log or file on filesystem is slow.
  2. Define global variable to store them and analyze at the end of execution of interest.
    • Caveats: Global variables are bad. They will get confusing in the future.
  3. Store them in class Myclass.
    • Caveats: Not reusable code. What about next time I want to analyze Otherclass::doOtherStuff(y) ? And bad integration, because that stats code should not be coupled with the Myclass itself.

Is there any tool/library to do this? I'm using Visual Studio on Windows, so would like an answer usable for this platform. Cross-platform tools are welcome, too.

like image 344
oldherl Avatar asked Mar 04 '23 15:03

oldherl


2 Answers

Here is an example using the scripting API of lldb (which works on Windows, too). Take this trivial program,

void func(int x) {}

int main(int, char **)
{
    for (int i = 0; i < 1000; ++i)
        func(i);
}

which you can analyze with such a script

import lldb
import os

fArgs = []

def analyzeFrame(frame, bpLocation, dict):
    variables = frame.GetVariables(True, False, False, False)
    x = variables.GetValueAtIndex(0).GetValueAsSigned()
    fArgs.append(x)
    return False

debugger = lldb.SBDebugger.Create()
debugger.SetAsync(False)
target = debugger.CreateTargetWithFileAndArch("pathToYourExecutable", "")

bp = target.BreakpointCreateByName("func", 4, lldb.SBFileSpecList(), lldb.SBFileSpecList())

bp.SetScriptCallbackFunction("analyzeFrame")

process = target.Launch(target.GetDebugger().GetListener(), [], [],
    None, None, None, os.getcwd(), 0, False, lldb.SBError())

print("max: {}".format(max(fArgs)))
print("min: {}".format(min(fArgs)))

You need to make sure the python interpreter finds the lldb module. The path can be seen by executing lldb -P on the command line.

like image 75
lubgr Avatar answered May 10 '23 20:05

lubgr


Unfortunately, there is no single simple answer. What you want is a variation of instrumentation debugging, which means that someone needs to inject additional code in your class to handle this case.

For a portable way, the only option you have is to add a cache of the previous values, then in your class destructor, output the statistics that you want. The way you cache data is up to you, you can design a simple Stats<> class, member of the class that you want to monitor and make calls to it to store new values. That would be what I would try first, as it's portable, almost clean and reusable.

like image 28
Matthieu Brucher Avatar answered May 10 '23 18:05

Matthieu Brucher