【问题标题】:How to build a graph of specific function calls?如何构建特定函数调用的图表?
【发布时间】:2021-10-16 12:21:55
【问题描述】:

我有一个项目,我想在其中动态构建特定函数调用的图表。例如,如果我有 2 个模板类 A 和 B,其中 A 有一个跟踪方法(保存为图形节点),B 有 3 个方法(非跟踪方法、跟踪方法和一个调用 A 的跟踪方法的跟踪方法),那么我希望能够仅将跟踪的方法调用作为节点注册到图形对象中。图形对象可以是单例。

template <class TA>
class A
{
public:
    void runTracked()
    {
        // do stuff
    }
};

template <class TB>
class B
{
public:
    void runNonTracked()
    {
        // do stuff
    }

    void runTracked()
    {
        // do stuff
    }

    void callATracked()
    {
        auto a = A<TB>();
        a.runTracked();
        // do stuff
    }
};

void root()
{
    auto b1 = B<int>();
    auto b2 = B<double>();
    b1.runTracked();
    b2.runNonTracked();
    b2.callATracked();
    
}

int main()
{
    auto b = B<int>();
    b.runTracked()
    root();
    return 0;
}

这应该会输出一个与下面类似的图形对象:

root()
\-- B<int>::runTracked()
\-- B<double>::callATracked()
    \-- A<double>::runTracked()

跟踪的功能应该是可调的。如果根是可调节的(如上例所示),那将是最好的。 有没有简单的方法来实现这一点?

我正在考虑为跟踪的功能引入一个宏和一个单例图形对象,它将跟踪的函数注册为节点。但是,我不确定如何确定调用堆栈中最后跟踪的函数,或者(从图形的角度)当我想添加新节点时哪个图形节点应该是父节点。

【问题讨论】:

  • 澄清一下:你想建立一个函数调用图吗?类似于“堆栈跟踪”的(过滤的)树形视图?
  • @Wolf 正是您所说的,但这不仅仅是我想要输出的图形,而是图形对象。
  • 所以你想要一个动态图对象。我不明白,从“输出”中无法推断的是调用堆栈(实际上是 stack)和 tree 之间的关系图形。我猜你想要一个由特定调用引起的所有(跟踪)调用的图表(那是一棵树)(我认为你的情况是root())?
  • 可以,但只包含特定元素。
  • 我认为查看 Log4j 可能会有所帮助,这允许动态配置日志记录活动。即使这不是您想要的,那里的概念也可以帮助您理解您的问题。顺便说一句,使用模板是问题的一部分还是解决方案的一部分?

标签: c++ c++11 templates trace runtime-configuration


【解决方案1】:

一般来说,你有两种策略:

  1. 使用某种日志记录/跟踪框架对您的应用程序进行测试,然后尝试复制某种类似跟踪 mixin 的功能,以根据您应用 mixin 的代码部分来应用全局/本地跟踪。

  2. 使用为您的编译器或运行时启用的某种跟踪检测功能重新编译您的代码,然后使用相关的跟踪编译器/运行时特定工具/框架来转换/筛选数据。

对于 1,这将需要您手动插入更多代码或类似 _penter/_pexit for MSVC 的东西,或创建某种 ScopedLogger (希望如此!)将异步记录到某些外部文件/流/进程。这不一定是坏事,因为在被跟踪的进程崩溃的情况下,拥有一个单独的进程控制跟踪跟踪可能会更好。无论如何,您可能必须重构您的代码,因为 C++ 没有一流的元编程支持,无法在模块/全局级别重构/检测代码。然而,对于大型应用程序来说,这并不是一个不常见的模式。例如,AWS X-Ray 是商业跟踪服务的一个示例(不过,通常我认为它适合跟踪网络调用和 RPC 调用而不是进程内函数调用的用例)。

对于 2,您可以尝试类似 utrace 或特定于编译器的东西:MSVC 有各种工具,如 Performance Explorer,LLVM 有 XRay,GCC 有 gprof。您基本上以一种“调试++”模式进行编译,或者有一些特殊的操作系统/硬件/编译器魔法可以自动插入跟踪指令或标记,帮助运行时跟踪您想要的代码。这些启用跟踪的程序/运行时通常会发出某种独特的跟踪格式,然后必须由唯一的跟踪格式阅读器读取。

最后,在内存中动态构建图形也是一个类似的故事。与上面的跟踪策略一样,有各种应用程序和运行时级别的库来帮助跟踪您可以以编程方式与之交互的代码。然后,即使是创建记录到跟踪文件的 ScopedTracer 对象的最简单版本,也可以配备一个消费者线程,该线程拥有并更新跟踪图,满足您所需的延迟和数据持久性要求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-05
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    相关资源
    最近更新 更多