我有一个使用第三方库的C ++应用程序。我的代码中的每个地方都有对该库的调用。我想跟踪所有这些电话。
如果那些是我的代码中的函数会很容易 - 我会插入一个宏来获取当前函数名和调用start的时间并将它们传递给本地对象构造函数,然后在函数退出时对象将被销毁并追踪必要的数据。对于我不需要跟踪以消除相关开销的配置,宏将扩展为空字符串。
是否有一些简单的方法可靠地执行类似的调用外部库?我所拥有的库的所有接口都是.h文件,函数原型包含在我的代码中。
答案 0 :(得分:4)
您可以尝试编写一个暴露相同接口的包装器库,并在内部将调用重定向到原始库。
然后,您可以轻松地将跟踪代码添加到包装器函数中。 您项目的所有更改都是您要链接的lib。
要防止定义多个符号,可以在单独的命名空间中包含外部libs标头。
修改强>
在命名空间中包含外部libs标头不能解决符号问题。您必须在标题中使用一个宏来重命名原始函数以及代码中的每个实例。对新的包装器库头使用类似的东西:
#define originalExportedFunction WRAPPED_originalExportedFunction
extern "C" int originalExportedFunction(int);
然后,您在包装器lib中的实现可能如下所示:
extern "C" int WRAPPED_originalExportedFunction(int i)
{
//trace code here...
return originalExportedFunction(i);
}
答案 1 :(得分:3)
如果你碰巧在unix / linux下工作
ltrace
跟踪库调用,
strace的
用于系统调用。这些是代码解决方案中的命令no。您还可以使用-callgrind选项查看valgrind以进行配置文件。
答案 2 :(得分:0)
那么你可以在第三方lib调用之上添加另一个层。这样你可以添加你想要的任何复杂的跟踪包装。
e.g。
struct trace
{
static void myfoo() { cout << "calling foo" << endl; foo(); }
// or
// static void myfoo() { if (_trace) {..} foo(); }
};
答案 3 :(得分:0)
由于您似乎知道要调用的函数(以及这些调用的签名),您仍然可以使用宏/类包装器的想法。类似的东西:
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;
}
答案 4 :(得分:0)
尝试为所有接口apis创建一个宏,例如 假设api被称为:
obj->run_first(var1);
然后创建下面的宏:
#define obj->run_first(args) \
dumptimestamp(__FUNCTION__, __LINE__); \
obj->run_first(args); \
dumptimestamp(__FUNCTION__, __LINE__);
您可以从lib的头文件生成类似宏的列表,因为它具有所有接口方法的列表。
dumptimestamp
将转储时间戳以及函数和行号。
答案 5 :(得分:0)
如果您不想更改代码,则可以通过 instrumentation 来执行此类操作。如果您对此感兴趣,请查看一个名为 PIN 的良好动态二进制检测工具包(由英特尔维护):
http://www.pintool.org/downloads.html
使用PIN,您可以在功能输入/退出时插入自己的代码。一个例子是捕获malloc / free:
http://www.pintool.org/docs/29972/Pin/html/index.html#FindSymbol
跟踪函数调用的方式完全不同。但是,值得一看。