我有一個使用第三方庫的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