2010-07-23 65 views
5

我正在尋找一種聰明的方式來跟蹤函數調用和返回。 我知道我可以使用調試器,但我希望有一種方法可以在調用函數時將它們打印到終端上,而不必遍歷代碼。
我在想我可以使用預處理器,但我不確定最好的方法是什麼。
或者有沒有辦法使用gdb打印出有用的信息,而無需單步執行代碼。C/C++需要一個聰明的方式來跟蹤函數調用

回答

2

或者是有使用gdb輸出,這將是非常有用的信息,而不必單步執行代碼

是一種方式。僅在您實際關心的功能中設置斷點。使用「繼續」,直到你到達這些功能或直到你的程序崩潰。然後使用「backtrace」(或「bt」)來獲取堆棧跟蹤。

+0

感謝回溯,它運作良好。 – 2010-07-23 04:08:05

3
#define BEGIN_FUNC(X) printf("Function %s Entered",X) 
#define END_FUNC(X) printf("Function %s End",X) 

foo() 
{ 
BEGIN_FUNC(__func__); 

//Your code here 


END_FUNC(__func__); 


} 

我認爲,如果你寫一個宏象上面和描述,然後你可以在終端上的日誌,用它爲每個函數。

+0

+1我的答案缺乏 – PostMan 2010-07-23 04:01:04

+0

這基本上是如何侵入性分析。 – 2010-07-23 04:27:23

+0

這是我做了很多次... 一個很好的技術... – Microkernel 2010-07-23 05:21:32

0

有一個__FUNCTION__(Reference)宏用於確定您所在的方法(格式爲Class::Method),但這更多的是手動過程。

但是,當我最近需要相同的「跟蹤」信息時,我找不到自動方法。

2

你可能想看看Valgrind's Callgrind它可以跟蹤函數調用到一個漂亮的圖形。它將顯示函數調用,但不顯示參數或返回值。

13

大多數編譯器允許您在函數調用之前和之後插入檢測函數。

在MSVC

他們_penter和_pexit
好文章 http://www.drdobbs.com/184403601

在GCC你可以使用-finstrument函數
http://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Code-Gen-Options.html

您可以使用調試libaries或映射文件,以獲得更多信息。

+0

你不會發生TP知道如何查找符號表中的功能在gcc – 2010-07-23 04:16:03

+0

你想找到名稱或查找的東西像調試對象中的參數等? – 2010-07-23 22:08:18

+0

只要名字就沒問題。 #delay – reader 2015-02-24 03:27:09

1

如果您需要使其自動化,您可以查看TARGET_ASM_FUNCTION_END_PROLOGUETARGET_ASM_FUNCTION_BEGIN_EPILOGUE。這些是編譯器鉤子,它可以讓你指定要與正常的函數序言/結尾一起發送的程序集 - 在你的情況下,你會用它們發出一個小程序集來記錄有問題的函數的入口/出口。您還可以查看FUNCTION_PROFILE和/或PROFILE_HOOK(例如:http://gcc.gnu.org/onlinedocs/gccint/Function-Entry.html)。

4

一個非常有趣的解決方案是使用RAII來控制函數的範圍。這將有性能有很大影響,但會在日誌中相當明確的,而不需要用戶在所有可能的代碼路徑添加儀器可能會留下功能:

class ScopeLogger { 
public: 
    ScopeLogger(std::string const & msg) : msg(msg) 
    { std::cout << "Enter: " << msg << std::endl; } 
    ~ScopeLogger() 
    { std::cout << "Exit: " << msg << std::endl; } 
    std::string msg; 
}; 
#if DEBUG 
#define FUNCTION(x) ScopeLogger l_##x##_scope(x); 
#endif 

void foo(int value) { 
    FUNCTION(__FUNCTION__); 
    if (value > 10) throw std::exception; 
    std::cout << "." << std::endl; 
} 

int main() { 
    foo(0); // Enter: foo\n.\nExit: foo 
    foo(100); // Enter: foo\nExit: foo 
} 

如果代碼是單線程的,你甚至可能希望與部分縮進級別添加一個靜態變量來ScopedLogger不會增加太多本來已經很沉重的性能影響:

class ScopeLogger { 
public: 
    ScopeLogger(std::string const & msg) : msg(msg) 
    { std::cout << std::string(indent++,' ') << "Enter: " << msg << std::endl; } 
    ~ScopeLogger() 
    { std::cout << std::string(--indent,' ') << "Exit: " << msg << std::endl; } 
    std::string msg; 
    static int indent; 
}; 
int ScopeLogger::indent = 0; 
+0

相當侵擾性,但不及Praveen's。 – Stephen 2010-07-23 12:50:51

+0

你的代碼是否被編譯? 'std :: string'的構造器在哪裏可以使這個'std :: string(indent ++,「」)'有效? – 2012-04-25 15:23:35

+0

@ bruce.banner:很好的結果,當你在網頁中輸入而不是在編輯器中編譯時,會發生這種情況。第二個參數*必須是*'char',而不是'char *'(即,我錯誤地用雙引號引用了單引號)。無論如何,這只是一個想法,代碼還有其他問題,包括但不限於線程安全性(或缺少它)。 – 2012-04-25 15:27:24

3

由於您使用GCC,你也可以使用連接器的功能包裝。

Link-Time Replacement/Wrapping 
– GCC option: -Wl,--wrap,function_name 

基本上,你可以採取所謂的「函數名()」函數和一個名爲「__wrap_function_name()」函數包裹。您可以通過調用「__real_function_name()」來訪問原始函數。

0

前段時間我聽了一個關於面向方面編程的演講,其中包括你想要實現的內容。也許搜索這個詞有幫助。