views:

241

answers:

6

I have a C++ application that uses a third-party library. Every here and there in my code there're calls to that library. I would like to trace all such calls.

It would be easy if those were functions in my code - I would insert a macro that would obtain the current function name and time of call start and pass those to a local object constructor, then on function exit the object would be destroyed and trace the necessary data. The macro would expand to an empty string for configurations where I don't need tracing to eliminate the associated overhead.

Is there some easy way to reliably do something similar for calls to an external library? All the interface to the library I have is the .h file with functions prototypes included into my code.

A: 

Well you could just add another layer on top of the 3rd party lib calls. That way you can add whatever sophisticated tracing wrapping you want.

e.g.

struct trace
{
   static void myfoo() { cout << "calling foo" << endl; foo(); }
   // or
   // static void myfoo() { if (_trace) {..} foo(); }
};
Anders K.
+4  A: 

You could try writing a wrapper library that exposes the same interface and internally redirects the calls to the original lib.

Then you can easily add your trace code to the wrapper functions. All that changes for your project is the lib your are going to link against.

To prevent multiple symbols being defined, you can include the external libs header inside a separate namespace.

EDIT:

Including the external libs header in a namespace does not solve the symbol problem. You have to use a macro in your header that renames the original function and every occurrence in your code. Use something like this for new wrapper library header:

#define originalExportedFunction   WRAPPED_originalExportedFunction

extern "C" int originalExportedFunction(int);

Your implementation in the wrapper lib then might look like:

extern "C" int WRAPPED_originalExportedFunction(int i)
{
    //trace code here...
    return originalExportedFunction(i);
}
Frank Bollack
If you include the headers inside a namespace then the symbols defined in the header will be in a different namespace to the symbols defined in the library.
jon hanson
@Jon: You are right, using namespace doesn't solve the symbol problem.But as an alternative you can always use a macro aproach that renames the called function and let your library export the renamed function name. The renamed function will then call the original function and there is no symbol clashing.
Frank Bollack
+3  A: 

If you happen to work under unix/linux use

ltrace

to track library calls,

strace

for system calls. These are commands no in code solution though. You can also look at valgrind with the -callgrind option to profile.

count0
A: 

Since you seem to know the functions you want to call (and the signatures for those calls) you can still use your macro/class wrapper idea. Something like:

typedef void (*pfun)(int);

class Foo {
    pfun call;
    public:
        Foo(pfun p) : call(p) {}
        void operator()(int x) {
            std::cout << "Start trace..." << std::endl;
            (*call)(x);
            std::cout << "End trace" << std::endl;
        }
};

void bar (int x) {
    std::cout << "In bar: " << x << std::endl;
}

int main () {

    Foo foo(&bar);
    foo (42);
    return 0;

}
ezpz
A: 

Try to create a macro for all interface apis e.g. Suppose the api is being called as:

obj->run_first(var1);

Then create below macro:

#define obj->run_first(args) \
   dumptimestamp(__FUNCTION__, __LINE__); \
   obj->run_first(args);                  \
   dumptimestamp(__FUNCTION__, __LINE__);

You can generate the list of similar macros from a lib's header file as it has the list of all interface methods.

dumptimestamp will dump the timestamp along with the function and line numbers.

Funsuk Wangadu
A: 

If you don't want to change your code, then there is a way to do such thing by instrumentation. If you're interested in this way, take a look at a nice dynamic-binary instrumentation toolkit called PIN (maintained by Intel):

http://www.pintool.org/downloads.html

With PIN, you can insert your own code on function entry/exit. One example would be capturing malloc/free:

http://www.pintool.org/docs/29972/Pin/html/index.html#FindSymbol

This is quite different way to trace the function calls. But, it's worth to take a look.

minjang