目的
- 了解特定模組或類別的使用方式。
- 找出執行時間過長的函式
方法一: 使用 gcc 參數 -finstrument-functions
參考資料:
優點:
- 自動含蓋所有函式。
缺點:
- 可能增加太多執行負擔, 雖然可以配合 -finstrument-functions-exclude-file-list 去除不想觀察的函式, 對於大專案來說有些不方便。
方法二: 自己寫 logger
使用 C/C++ 或 gcc 提供的語法, 自己寫 logger 沒有想像中的麻煩:
- 可用 gcc 的 __PRETTY_FUNCTION__ 或標準的 __func__ 取得函式名稱
- 用 __LINE__ 取得行數
- 用 __FILE__ 取得檔名
於是可定個巨集:
#define LOG_IT() MyFunctionLogger logger(__FILE__, __LINE__, __PRETTY_FUNCTION__)
然後用 vim 的全域取代:
:% s/^{/&\r LOG_IT();\r\r/c
這會逐個詢問是否取代下列字串:
void foo() { // blah blah
為
void foo() { LOG_IT(); // blah blah
Btw, 由此可知, 函式的左大括號放在行首比放在行尾來得方便。
MyFunctionLogger 只是個 wrapper, 目的是利用 RAII 記錄進入和離開函式, 這樣即使函式中間有任何 return, 都不會漏記離開函式:
class MyFunctionLogger { public: MyFunctionLogger(const char *file, int line, const char *func) : m_file(file), m_line(line), m_func(func) { MyLogger::Instance()->EnterFunctionCall(m_file, m_line, m_func); } ~MyFunctionLogger() { MyLogger::Instance()->LeaveFunctionCall(m_file, m_line, m_func); } private: const char *m_file; int m_line; const char *m_func; };
MyLogger 可以做的事很多, 比方說:
- 用 file 做為 tag 分類輸出。
- 在 EnterFunctionCall() 和 LeaveFunctionCall() 記錄層級, 輸出 function call 時, 視 function call 的深度做對應的縮排。
- 在 EnterFunctionCall() 和 LeaveFunctionCall() 記錄目前時間, 可在 LeaveFunctionCall() 時計算執行時間, 協助 profiling。
若 MyLogger 會被多個 thread 使用, 別忘了將 MyLogger 寫成 thread-safe, 不過以觀察或除錯的角度來說, 偷懶不作應該也OK吧。
若目標模組是純 C 的程式, 可用 gcc 的 cleanup attribute 做出 RAII 的效果, 達到和 MyFunctionLogger 一樣的效果。
沒有留言:
張貼留言