我有一个使用第三方库的C++应用程序。我的代码中的每个地方都有对该库的调用。我想跟踪所有这些电话。如何将所有调用跟踪到C++中预定义的函数?
这很容易,如果这些是我的代码中的函数 - 我会插入一个宏,它将获得当前函数的名称和调用启动时间,并将其传递给本地对象构造函数,然后在函数退出时,该对象将销毁并追踪必要的数据。该宏将扩展为配置的空字符串,我不需要跟踪来消除相关开销。
有没有一些简单的方法可靠地做一些类似的调用外部库?我拥有的库的所有接口都是带有函数原型的.h文件,它包含在我的代码中。
我有一个使用第三方库的C++应用程序。我的代码中的每个地方都有对该库的调用。我想跟踪所有这些电话。如何将所有调用跟踪到C++中预定义的函数?
这很容易,如果这些是我的代码中的函数 - 我会插入一个宏,它将获得当前函数的名称和调用启动时间,并将其传递给本地对象构造函数,然后在函数退出时,该对象将销毁并追踪必要的数据。该宏将扩展为配置的空字符串,我不需要跟踪来消除相关开销。
有没有一些简单的方法可靠地做一些类似的调用外部库?我拥有的库的所有接口都是带有函数原型的.h文件,它包含在我的代码中。
那么你可以在第三方lib调用的顶部添加另一个图层。这样你可以添加任何复杂的追踪包装你想要的。
例如
struct trace
{
static void myfoo() { cout << "calling foo" << endl; foo(); }
// or
// static void myfoo() { if (_trace) {..} foo(); }
};
你可以试着写暴露相同的接口和内部重定向到原来的lib中调用一个包装库。
然后,您可以轻松地将您的跟踪代码添加到包装函数。 您项目的所有变化都是您要链接的库。
要防止定义多个符号,可以将外部库头包含在单独的名称空间中。
编辑:
包含在一个命名空间中的外部库头不解决该符号的问题。您必须在头中使用一个宏,以重命名原始函数和代码中的每一处。使用这样的新包装库头:
#define originalExportedFunction WRAPPED_originalExportedFunction
extern "C" int originalExportedFunction(int);
你在包装lib中实现,那么可能是:
extern "C" int WRAPPED_originalExportedFunction(int i)
{
//trace code here...
return originalExportedFunction(i);
}
如果你碰巧Unix/Linux下使用工作
ltrace
跟踪库调用,
strace的
系统调用。尽管如此,这些命令在代码解决方案中没有。您还可以使用-callgrind选项查看valgrind以进行配置。
由于您似乎知道要调用的函数(以及这些调用的签名),因此仍然可以使用宏/类包装器的想法。例如:
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;
}
尝试为所有接口apis创建一个宏,例如, 假设API被称为:
obj->run_first(var1);
然后创建下面的宏:
#define obj->run_first(args) \
dumptimestamp(__FUNCTION__, __LINE__); \
obj->run_first(args); \
dumptimestamp(__FUNCTION__, __LINE__);
您可以从一个lib的头文件生成类似于宏的列表中,因为它具有的所有的接口方法列表。
dumptimestamp
将转储时间戳以及函数和行号。
如果你不想改变你的代码,那么有办法通过仪器来做这样的事情。如果你有兴趣以这种方式,来看看被称为一个很好的动态二进制仪表工具包PIN(由英特尔维护):
http://www.pintool.org/downloads.html
随着PIN,您可以在功能插入自己的代码进入/退出。一个例子是捕获的malloc /免费:
http://www.pintool.org/docs/29972/Pin/html/index.html#FindSymbol
这是完全不同的方式来跟踪函数调用。但是,值得一看。
如果您将标头包含在命名空间中,则标头中定义的符号将与库中定义的符号位于不同的命名空间中。 – 2009-09-02 14:30:30
@Jon:你说得对,使用命名空间并不能解决符号问题。 但作为替代方案,您始终可以使用重新命名被调用函数的宏函数,并让您的库导出重命名的函数名称。然后重命名的函数将调用原始函数,并且没有符号冲突。 – 2009-09-02 21:52:25