快速、低延迟且非同步的跟踪类






4.75/5 (3投票s)
2004 年 4 月 17 日
4分钟阅读

61003

356
一篇关于现成可用的、专门针对多线程编程的跟踪类的文章。
引言
您是否曾经尝试使用通常提供的 TRACE()
或 OutputDebugString()
等方法来查找棘手的多线程或同步 bug?您的调试版本运行良好,但您的发布版本却出现页面错误?
传统方法丢失了大量可以自动生成但需要您自己提供的信息。大多数宏只在调试模式下工作,在发布模式下不可用。
但最重要的一点是,使用调试工具会改变被调试应用程序的计时和流程。
我曾尝试在一个应用程序中查找线程同步 bug,并开发了此处提供的跟踪例程作为解决方案。
问题
第一个问题是计时,它会发生显著变化,例如使用 OutputDebugString()
。不幸的是,时间取决于连接的调试器是否显示输出。
我在一个循环中进行了 100 万次跟踪,并测量了它的时间
TRACE -宏调试代码 未连接调试器 | 28 秒。 |
TRACE -宏调试代码 已连接 VS.NET 2003 | 1600 秒(基于 20,000 次跟踪) |
TRACE -宏调试代码 DbgView | 210 秒(基于 100,000 次跟踪) |
Tracer 调试代码 | 9 秒。 |
Tracer 发布代码 | 5 秒。 |
您可以看到,Tracer 速度快得多,并且即使在发布模式代码中也可用。
OutputDebugString()
带来的第二个问题是,该例程本身似乎是同步的。通过在两个并发线程中频繁使用此例程,您可能会因为 OutputDebugString()
的内部同步而改变计时 ***。
基础
跟踪类将它们的输出写入您驱动器上的普通文本文件。通过使用一些特殊的技巧,可以最大程度地减少同步和延迟。对于跟踪某些信息的每个线程,跟踪器会在应用程序目录中创建一个文件,命名为“<exe-name>. trace.<threadid>”。跟踪中的每一行都具有以下形式
00000128fd9f283f-00000320 : CCameraView::SetOptions() entered
第一部分是 QueryPerformanceCounter()
生成的硬件计时器值。第二个值是跟踪生成的线程 ID。通过使用提供的 TraceMerge.exe 实用程序,可以将不同的文件合并到一个大文件中,按时间排序。
跟踪通过环境变量开启和关闭。可以在启动调用中指定名称。
内部
跟踪代码实现为单例类。可以使用 CTracer::Get()->Trace()
访问单例。单例维护一个映射,其中包含每个线程的 TLSDataObject
对象。此类包含一个大的写入缓冲区,其中包含所有跟踪字符串和跟踪文件的句柄。提供了一个临界区来保护缓冲区。
当首次为特定线程进行跟踪时,跟踪器会创建一个 TLSDataObject
,将其存储在映射和线程本地存储(TLS)索引中。通过使用 TLS,线程可以访问自己的 TLSDataObject
,而无需与其他线程同步。只有插入映射(因此是第一次跟踪)需要同步。
Tracer 启动一个后台线程,该线程将每个线程的缓冲区写入文件。因此,在短时间内,跟踪必须停止。但请注意,只有后台线程和应用程序线程一次只有一个被链接在一起。
已关闭的线程会不时地从映射中删除,以释放写入缓冲区内存。
Using the Code
将“singleton.h”、“tracer.cpp”、“tracer.h”、“CritSecLocker.cpp”和“CritSecLocker.h”包含到您的项目中。在您要跟踪的所有源文件中包含“tracer.h”。
在 InitInstance()
(或类似函数)中,使用以下方法启动跟踪:
CTracer::Get()->Startup("MYAPP_TRACE");
在 ExitInstance()
(或类似函数)中,不要忘记调用:
CTracer::Get()->Shutdown();
在您的代码中,使用:
CTracer::Get()->Trace("A number %d",123);
来生成跟踪行。请注意,不允许附加“\n
”,因为它会破坏合并工具。
如果您想刷新所有可用的缓冲区,可以使用:
CTracer::Get()->Flush();
这将写入所有线程的所有缓冲区,并刷新 Win32 文件缓冲区。例如,在异常处理程序中中止应用程序之前,这很有用。
如果您有一个执行大量跟踪的成员函数,提供一个局部引用会很有用:
void foo(void)
{
CTracer &l_tracer = CTracer::Get();
...
l_tracer->Trace("fewfewoifnwefewf");
...
l_tracer->Trace("fewfewoifnwefewf");
}
使用 TraceMerger
为了从每个线程的各个文件创建一个跟踪文件,提供了一个小型实用程序。
像这样使用它
TraceMerger.exe <Application Exe Path>
TraceMerger
将获取所有跟踪文件,并根据时间合并排序。结果将保存到一个名为 <Application Exe Path>.trace.merge. 的新文件中。
许可证
本软件包根据 The Artistic License 获得许可。
*** 如果您对 OutputDebugString()
同步有任何了解,请告诉我……