C/C++ need a clever way to track function calls

15,536

Solution 1

Most compilers allow you to inject an instrumentation function before and after the function call.

In MSVC they are _penter and _pexit. A nice article: http://www.drdobbs.com/184403601.

In GCC you would use the -finstrument-functions option, see the docs.

You can use debug libaries or map files to get more info.

Solution 2

A quite intrussive solution is using RAII to control the scope of the function. This will have a great impact in performance, but will be quite explicit in the logs without requiring the user to add instrumentation in all possible code paths that may leave the function:

class ScopeLogger {
public:
   ScopeLogger( std::string const & msg ) : msg(msg)
   {   std::cout << "Enter: " << msg << std::endl; }
   ~ScopeLogger()
   {   std::cout << "Exit:  " << msg << std::endl; }
   std::string msg;
};
#if DEBUG
#define FUNCTION(x) ScopeLogger l_##x##_scope(x);
#endif

void foo( int value ) {
   FUNCTION( __FUNCTION__ );
   if ( value > 10 ) throw std::exception;
   std::cout << "." << std::endl;
}

int main() {
   foo(0);    // Enter: foo\n.\nExit:  foo
   foo(100);  // Enter: foo\nExit:  foo
}

If the code is single threaded, you might even want to add a static variable with some indentation level to ScopedLogger without adding too much to the already heavy performance impact:

class ScopeLogger {
public:
   ScopeLogger( std::string const & msg ) : msg(msg)
   {   std::cout << std::string(indent++,' ') << "Enter: " << msg << std::endl; }
   ~ScopeLogger()
   {   std::cout << std::string(--indent,' ') << "Exit:  " << msg << std::endl; }
   std::string msg;
   static int indent;
};
int ScopeLogger::indent = 0;

Solution 3

Since you are using GCC, you can also use linker function wrapping.

Link-Time Replacement / Wrapping
– GCC option: -Wl,--wrap,function_name

Basically, you can take a function called "function_name()" and wrap it with a function called "__wrap_function_name()". You can access the original function by calling "__real_function_name()".

Solution 4

#define BEGIN_FUNC(X) printf("Function %s Entered",X)
#define END_FUNC(X)  printf("Function %s End",X)

foo()
{
BEGIN_FUNC(__func__);

//Your code here


END_FUNC(__func__);


}

I think if you write a macro like above and use it for every function as described then you can get the logs on the terminal.

Solution 5

You may want to look at Valgrind's Callgrind which can track function calls into a pretty graph. It will show function calls, but not the parameter or return values.

Share:
15,536
Matthew FL
Author by

Matthew FL

http://github.com/matthewfl

Updated on July 10, 2022

Comments

  • Matthew FL
    Matthew FL almost 2 years

    I am looking for a clever way to track function calls and returns. I know I can use the debugger, but I would like a way to just have it print something out to the terminal when calling a function vs having to step through code.
    I am thinking that I might be able to use the preprocessor, but I am not sure what would be the best way to go about this.
    Or is there a way to use gdb to print out the information that would be useful, while not having to step through the code.

  • Matthew FL
    Matthew FL almost 14 years
    You would not happen tp know how to look up the function in the symbol table in gcc
  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 14 years
    This is quite fragile. If you are programming c++, you are better off writing a small scope logger that will log on construction and on object destruction. Else you might see functions that are entered and never returned from if exceptions are thrown or if the user forgets to write the END_FUC macro in any returning code path.
  • Praveen S
    Praveen S almost 14 years
    Will it not help in debugging by seeing the logs? The OP stated any method to know call stack without using debugger. We can use this on every API of the executable and hence get the stack at any point of time.
  • Praveen S
    Praveen S almost 14 years
    @Nyan- I think its implied when we say start and end of function calls that it has to be added before every return.
  • Stephen
    Stephen almost 14 years
    Quite intrusive, but less than Praveen's.
  • Jonathan Fischoff
    Jonathan Fischoff almost 14 years
    Do you want to find the name, or look up things like parameters etc. in the debug objects?
  • Mohamed Bana
    Mohamed Bana about 12 years
    Does your code compile? Where is the contructor for std::string to make this std::string(indent++," ") valid?
  • David Rodríguez - dribeas
    David Rodríguez - dribeas about 12 years
    @bruce.banner: Good catch, that's what happens when you type into a webpage rather than in an editor and compile. The second argument must be a char, not a char* (i.e. I mistakenly had double quotes where single quotes were required). At any rate, use this just as an idea, the code has other issues, including but probably not limited to thread safety (or the lack of it).
  • reader
    reader about 9 years
    Just the name would be fine. #delay
  • Daniel Moodie
    Daniel Moodie over 8 years
    link Dl_info this_fn_info; dladdr( this_fn, &this_fn_info );
  • Michael
    Michael over 8 years
    @DanielMoodie There is a "call site" parameter, can I use this to find the file and line number the call was made from, e.g. _FILE_ and _LINE_?