如何构建特定函数调用的图形?

6
我有一个项目,希望动态构建特定函数调用的图。例如,如果我有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()

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

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


1
澄清一下:您想构建一个函数调用图吗?类似于“堆栈跟踪”的(过滤后的)树形视图? - Wolf
@Wolf,我明白你的意思,但我想要的不仅仅是图形输出,而是图形对象。 - Gábor Pálovics
1
所以你想要一个动态图形对象。我不理解的是,在“输出”上看不出调用堆栈(实际上是一个堆栈)和图之间的关系。我猜你想要一个图(这是一棵树),显示由特定调用(在你的情况下,我认为是root())引起的所有(被跟踪的)调用。 - Wolf
“only include specific elements” - 这就是您所说的“可调整”的意思。您展示的代码在运行时不会在跟踪和未跟踪的调用之间切换。 - Wolf
请检查我对标题、文本和标签所做的更改是否符合您的意图。也许您应该尝试进一步解释可调节和动态这些术语。 - Wolf
显示剩余10条评论
1个回答

2
通常,您有两种策略:
  1. 使用某种日志/跟踪框架来为应用程序进行仪器化,然后尝试复制某种类似混入的跟踪功能,以应用于代码中哪些部分取决于您应用这些混入的方式。

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

对于第一点,这将需要您手动插入更多代码或类似于 MSVC 的 _penter/_pexit 这样的东西,或者创建某种 ScopedLogger,它(希望!)会异步记录到某个外部文件/流/进程中。这并不一定是坏事,因为在被跟踪的进程崩溃的情况下,有一个单独的进程控制跟踪追踪可能会更好。无论如何,您可能需要重构代码,因为 C++ 并没有很好的一流元编程支持来重构/仪器化模块/全局级别的代码。然而,对于较大的应用程序来说,这并不是一个不常见的模式;例如,AWS X-Ray 就是一个商业追踪服务的例子(尽管通常我认为它适用于跟踪网络调用和 RPC 调用,而不是进程内函数调用)。
对于第二个问题,你可以尝试使用utrace或特定于编译器的工具,例如MSVC有各种工具,如Performance Explorer,LLVM有XRay,GCC有gprof。你需要在一种“debug++”模式下编译,或者有一些特殊的操作系统/硬件/编译器魔法自动插入跟踪指令或标记来帮助运行时跟踪你想要的代码。这些启用跟踪的程序/运行时通常会发出某种独特的跟踪格式,必须由独特的跟踪格式读取器读取。
最后,动态构建内存中的图形也是一个类似的过程。与上述跟踪策略一样,有各种应用程序和运行时级别的库可帮助跟踪您的代码,您可以以编程方式与之交互。即使是创建记录到跟踪文件的ScopedTracer对象的最简单版本,也可以配备一个消费者线程,该线程拥有并更新跟踪图形,以满足您所需的任何延迟和数据耐久性要求。
编辑:如果您愿意,OpenTelemetry/Jaeger可能是一个很好的起点,用于在提取数据后可视化跟踪(如果您想要,也可以直接向其报告),尽管它更喜欢树形呈现格式:Jaeger文档-跟踪详细信息视图

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接