Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 410
Matthew FL Avatar asked Jul 23 '10 03:07

Matthew FL


5 Answers

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.

like image 94
Jonathan Fischoff Avatar answered Oct 23 '22 01:10

Jonathan Fischoff


If you need to automate it, you might take a look at TARGET_ASM_FUNCTION_END_PROLOGUE and TARGET_ASM_FUNCTION_BEGIN_EPILOGUE. These are compiler hooks that will let you specify pieces of assembly to be emitted along with the normal function prologue/epilogue -- in your case, you'd use them to emit a little assembly to log the entry/exit from the function in question. You could also look at FUNCTION_PROFILE and/or PROFILE_HOOK (e.g., at: http://gcc.gnu.org/onlinedocs/gccint/Function-Entry.html).

like image 44
Jerry Coffin Avatar answered Sep 29 '22 20:09

Jerry Coffin


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;
like image 5
David Rodríguez - dribeas Avatar answered Oct 23 '22 00:10

David Rodríguez - dribeas


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()".

like image 4
Adisak Avatar answered Oct 23 '22 00:10

Adisak


#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.

like image 3
Praveen S Avatar answered Oct 23 '22 01:10

Praveen S