C++中的函數調用跟蹤Trace功能簡單實現


我們所使用的每個軟件產品都包含這樣或那樣的跟蹤功能。
跟蹤,英文Trace,又叫做追蹤。
軟件中的跟蹤就是僅僅地跟在執行者的后面進行監視。
當代碼超過幾千行時,跟蹤就顯得很重要了。
調試、維護和理解大中型軟件的執行流程是很重要的,這是跟蹤的基本功能。
在C++中,有許多方法可以進行函數調用跟蹤。
其中最簡單的方法是在剛進入函數時打印
"Entering function X",在返回函數之前進行打印"Leaving function X"
但是,這需要進行很多工作,尤其是在函數具有多個
return語句的情況下。

解決上述問題的一種方法是使用跟蹤對象。
您創建一個類,該類的構造函數將打印輸入消息,存儲函數名稱,並且其析構函數將打印離開消息。
然后,將該類的實例創建為局部變量。當實例化對象時(調用函數時會發生這種情況),將打印enter消息。
當函數離開時,無論何時,何地或如何發生,對象都會被破壞,並會顯示離開消息。
注意,trace對象不應該添加到小的、頻繁執行的函數中去。

現在,這樣是如何實現的呢?可能是這樣簡單的事情:

 1 struct tracer
 2 {
 3     std::string name_;  // Name of the function
 4 
 5     tracer(std::string const& name)
 6         : name_(name)
 7     {
 8         std::clog << "Entering function " << name_ << std::endl;  // Flushing is important
 9     }
10 
11     ~tracer()
12     {
13         std::clog << "Leaving function " << name_ << std::endl;  // Flushing is still important
14     }
15 };

這就是所需要的基礎。現在使用它非常簡單:

1 void some_class::some_member_function()
2 {
3     tracer _unique_name_{"some_class::some_member_function");
4 
5     // Lots of code and multiple `return` statements...
6 
7 }

這是基本用法。它具有一些缺點,因為將始終啟用跟蹤。例如,發布版本很少需要它,因此只有在_DEBUG定義宏時才使用它才是一個好的開始。擁有一個特殊的TRACING_ENABLED可能會更好,因此即使在某些有用的發行版中也可以啟用它。還可以添加額外的邏輯來檢查在運行時設置的標志。

下面一個完整的示例解決方案,在編譯時使用預處理器宏來啟用和禁用跟蹤。

 1 #pragma once
 2 #ifndef TRACING_H
 3 #define TRACING_H
 4 
 5 #include <string>
 6 #include <iostream>
 7 #include <iomanip>
 8 
 9 // Simple structure to handle function-call tracing.
10 
11 // On debug builds, always build with tracing enabled unless explicitly disabled
12 #if defined(_DEBUG) && !defined(TRACING_DISABLED)
13 # define TRACING_ENABLED
14 #endif
15 
16 // Define a preprocessor macro to help with the tracing
17 #ifdef TRACING_ENABLED
18 # define TRACE() tracing::tracer _tracer_object__ ## __COUNTER__ {__func__, __FILE__, __LINE__}
19 #else
20 # define TRACE() // Nothing
21 #endif
22 
23 #ifdef TRACING_ENABLED
24 namespace tracing
25 {
26     class tracer
27     {
28     public:
29         tracer() = delete;  // Disallow default construction
30         tracer(tracer const&) = delete;  // Disallow copy construction
31         tracer(tracer&&) = delete;  // Disallow move construction
32         tracer& operator=(tracer const&) = delete;  // Disallow copy assignment
33         tracer& operator=(tracer&&) = delete;  // Disallow move assignment
34 
35         tracer(std::string const& fun, std::string const& file, int const line)
36             : function_name{fun}, file_name{file}, line_number{line}
37         {
38             std::clog << "TRACE: Entering function " << function_name << " (" << file_name << ':' << line_number << ')' << std::endl;
39         }
40 
41         ~tracer()
42         {
43             std::clog << "TRACE: Leaving function " << function_name << std::endl;
44         }
45 
46     private:
47         std::string function_name;
48         std::string file_name;
49         int         line_number;
50     };
51 }
52 #endif // TRACING_ENABLED
53 
54 #endif // TRACING_H

使用上述頭文件的示例程序:

#include "tracing.h"

struct foo
{
    int bar(int value)
    {
        TRACE();

        if (value < 10)
            return value * 2;
        else
            return value * 3;
    }
};

int main()
{
    TRACE();

    foo my_foo;
    my_foo.bar(20);
    my_foo.bar(5);
}

如上所示,程序的輸出可能類似於

 

輸出可能會因所使用的編譯器而異。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM