我有一个项目,我想在其中动态构建特定函数调用的图形。例如,如果我有 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()
跟踪的功能应该是可调的。如果根是可调整的(如上例所示),那将是最好的。有没有简单的方法来实现这一目标?
我正在考虑为被跟踪的功能引入一个宏和一个单例图形对象,它将被跟踪的功能注册为节点。但是,我不确定如何确定哪个是调用堆栈中最后一个跟踪的函数,或者(从图形的角度)当我想添加一个新节点时,哪个图形节点应该是父节点。
最佳答案
一般来说,你有两种策略:
对于 1,这将需要您手动插入更多代码或类似 _penter/_pexit for MSVC 的内容。手动或创建某种 ScopedLogger这将(希望如此!)将异步记录到某些外部文件/流/进程。这不一定是坏事,因为在被跟踪进程崩溃的情况下,拥有单独的进程控制跟踪跟踪可能会更好。无论如何,您可能必须重构您的代码,因为 C++ 对元编程在模块/全局级别重构/检测代码没有很好的一流支持。然而,对于较大的应用程序来说,这并不是一种罕见的模式。例如,AWS X-Ray是商业跟踪服务的一个示例(不过,通常,我认为它适合跟踪网络调用和 RPC 调用而不是进程内函数调用的用例)。
对于 2,您可以尝试类似 utrace 的内容或特定于编译器的东西:MSVC 有各种工具,例如 Performance Explorer , LLVM 有 XRay , GCC 有
gprof
.您基本上以某种“调试++”模式进行编译,或者有一些特殊的操作系统/硬件/编译器魔术来自动插入跟踪指令或标记,以帮助运行时跟踪您所需的代码。这些启用跟踪的程序/运行时通常发出某种独特的跟踪格式,然后必须由唯一的跟踪格式读取器读取。最后,在内存中动态构建图形也是一个类似的故事。与上面的跟踪策略一样,有多种应用程序和运行时级别的库可以帮助跟踪您可以以编程方式与之交互的代码。即使是创建记录到跟踪文件的 ScopedTracer 对象的最简单版本,也可以配备一个消费者线程,该线程拥有并根据您具有的任何所需延迟和数据持久性要求更新跟踪图。
关于c++ - 如何构建特定函数调用的图形?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68769119/