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);
}

如上所示,程序的输出可能类似于
C++中的函数调用跟踪Trace功能简单实现

 

输出可能会因所使用的编译器而异。

原文链接: https://www.cnblogs.com/MakeView660/p/12531566.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++中的函数调用跟踪Trace功能简单实现

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/336421

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月1日 下午10:38
下一篇 2023年3月1日 下午10:38

相关推荐