views:

222

answers:

7

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.

+2  A: 

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

Yes. Set a breakpoint only at the functions that you actually care about. Use "continue" until you get to those functions or until your program crashes. Then use "backtrace" (or "bt") to get a stack trace.

Michael Aaron Safyan
Thanks for the backtracking, it is working well.
Matthew
+3  A: 
#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.

Praveen S
+1 What my answer is lacking
PostMan
This is basically how invasive profiling does it.
Michael Dorgan
This is what I do many times... A good technique...
Microkernel
+1 this what we do in our project.
Kedar
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.
David Rodríguez - dribeas
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
What if function has many exiting points?
Nyan
@Nyan- I think its implied when we say start and end of function calls that it has to be added before every return.
Praveen S
Of course, which is troublesome.
Nyan
A: 

There is a __FUNCTION__ (Reference) macro used to determine what method (in the format Class::Method) you're in, but this is more of a manual process.

However, when I needed the same 'trace' information recently, I could not find a automatic method.

PostMan
A: 

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.

Stephen
+7  A: 

Most compiler's allow you to inject an instrumentation function before and after the function call.

in msvc they are _penter and _pexit
nice article http://www.drdobbs.com/184403601

in gcc you would use the -finstrument-functions
http://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Code-Gen-Options.html

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

Jonathan Fischoff
You would not happen tp know how to look up the function in the symbol table in gcc
Matthew
Do you want to find the name, or look up things like parameters etc. in the debug objects?
Jonathan Fischoff
+1  A: 

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

Jerry Coffin
+2  A: 

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;
David Rodríguez - dribeas
Quite intrusive, but less than Praveen's.
Stephen