Click here to Skip to main content
11,928,352 members (51,522 online)
Click here to Skip to main content
Add your own
alternative version


29 bookmarked

Fast, low latency and non-synchronizing trace class

, 16 Apr 2004
Rate this:
Please Sign up or sign in to vote.
An article about a ready to use trace class specialized for multithreaded programming.


Did you ever tried to find a wired multithreading or synchronization bug using the normally provided method like TRACE() or OutputDebugString()? Did your debug compiled running fine, but your release application got a page fault?

A lot of useful information, which can be generated automatically, is missing in the traditional approaches and you have to provide it on your own. Most of the macros only work in debug mode and are not available in release mode.

But the major point is that, the timing and the flow of the application to be debugged is changed by using the debug tools.

I tried to find a thread synchronization bug in one of my applications and I developed the trace routines provided here as a solution.


The first problem is the timing, which is changed significantly, for example, by OutputDebugString(). Unfortunately the time depends on the debugger attached to show the output.

I have done 1.000.000 traces in a loop and measured the time for it:

No Debugger attached
28 sec.
VS.NET 2003 attached
1600 sec. (based on 20.000 traces)
210 sec. (based on 100.000 traces)
9 sec.
5 sec.

As you can see, Tracer is much faster and available even in release mode code.

The second problem which comes with OutputDebugString() is that, this routine seems to be synchronized itself. By using this routine in two concurrent threads very often, you can shift the timing by the internal synchronization of OutputDebugString() ***.


The trace classes write their output to normal text files on your drive. By using some special tricks, there is as less synchronization and latency as possible. For each thread tracing some information, the tracer creates one file in the application's directory named like "<exe-name>. trace.<threadid>". Each line in the tracing has the form:

00000128fd9f283f-00000320 : CCameraView::SetOptions() entered

The first part is the hardware timer value generated by QueryPerformanceCounter(). The second value is the thread id, which is generated by the tracing. By using the provided TraceMerge.exe utility, the different files can be merged to one big file, sorted by time.

The tracing is switched on and off using an environment variable. The name can be provided in the startup call.


The tracing code is implemented as a singleton class. The singleton can be accessed using CTracer::Get()->Trace(). The singleton maintains a map containing a TLSDataObject object for each thread. This class contains a big write buffer containing all the trace strings and the handle to the trace file. A critical section is provided for protecting the buffer.

When doing the first trace for a specific thread, the tracer create a TLSDataObject, stores it in the map and in a thread local storage (TLS) index. By using the TLS, a thread can access its own TLSDataObject without any synchronization to other threads. Only the insertion in the map (so the first trace) has to be synchronized.

Tracer starts a background thread which writes down the buffers of each thread. So for a short time, the tracing has to be stopped. But notice, that only the background thread and one application thread at a time are linked together.

Threads, which have been closed, are removed from the map from time to time to release the write buffer memory.

Using the code

Include "singleton.h", "tracer.cpp", "tracer.h", "CritSecLocker.cpp" and "CritSecLocker.h" to your project. Include "tracer.h" in all sources you want to trace.

In InitInstance() (or something similar), startup the tracing using:


In ExitInstance() (or something similar) don't forget to call:


In your code, use:

CTracer::Get()->Trace("A number %d",123);

to produce a trace line. Note that appending "\n" is not allowed because it breaks the merging tool.

If you want to flush all available buffers, you may use


This writes all buffers of all threads and flushes the Win32 file buffers too. It's useful, for example, before aborting the application in an exception handler.

If you have a member function doing a lot of traces, providing a local reference is useful:

void foo(void)
    CTracer &l_tracer = CTracer::Get();





Using TraceMerger

To create one trace file from all the different files for each thread, a small utility has been provided.

Use it like this:

TraceMerger.exe <Application Exe Path>

TraceMerger will get all the trace files and merge sort them based on time. The results will be saved to a new file named <Application Exe Path>.trace.merge.


This package is licensed under The Artistic License.

*** If you know something about the OutputDebugString() synchronization, tell it to me...


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Tim Hagemann
Web Developer
Germany Germany
Being a software developer since 1992, I have seen many different programming languages, operating systems and methodologies.

My primary technical interest are C++ and Windows-Programming, but due to my work as a project manager, I have even seen J2EE.

My everyday work since a couple of years is leading software development teams, mostly in an agile way.

You may also be interested in...

Comments and Discussions

GeneralUnderstanding Win32 "OutputDebugString" Pin
Dandy Cheung28-Jul-08 6:13
memberDandy Cheung28-Jul-08 6:13 
GeneralOutputDebugString Synchronization Pin
Toby Opferman9-May-04 10:36
memberToby Opferman9-May-04 10:36 
GeneralRe: OutputDebugString Synchronization Pin
Toby Opferman9-May-04 10:56
memberToby Opferman9-May-04 10:56 
GeneralRe: OutputDebugString Synchronization Pin
Toby Opferman9-May-04 13:36
memberToby Opferman9-May-04 13:36 
QuestionVC 6? Pin
WREY17-Apr-04 10:08
memberWREY17-Apr-04 10:08 
AnswerRe: VC 6? Pin
Thomas Freudenberg18-Apr-04 6:25
memberThomas Freudenberg18-Apr-04 6:25 
GeneralRe: VC 6? Pin
WREY18-Apr-04 8:12
memberWREY18-Apr-04 8:12 
How many times have you seen articles for which the authors indicated the samples were VC6 and VC7 compatible, and included in the files were workspace and project files for VC6? I've seen a lot!

And how many times have you seen articles for which the authors indicated the samples were VC6 and VC7 compatible, and included in the files were NO workspace and project files for VC6? I've seen a lot of those too.

I've also seen where the authors indicated the samples were for VC6 and VC7 with ONLY files compatible for VC7 present, and when those users (like myself) went to create the VC6 project and workspace files, he/she encountered MFC functions and API's that were specific to VC7. I've also seen a lot of those, likewise.

The moral: What you see, is NOT always what you get.


Fortes in fide et opere!
GeneralRe: VC 6? Pin
Tim Hagemann19-Apr-04 2:29
memberTim Hagemann19-Apr-04 2:29 
GeneralRe: VC 6? Pin
Toby Opferman9-May-04 9:16
memberToby Opferman9-May-04 9:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.151126.1 | Last Updated 17 Apr 2004
Article Copyright 2004 by Tim Hagemann
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid