We used it extensively in our new QuickBooks wrapper. The debug strings can be enabled/disabled at the DLL level, the template level and the class level.
Then we used markers to identify where the code was executing as you can see in this screen capture.
Being able to turn it on/off as needed, plus being able to write anything out anywhere in the app makes it a very useful tool.
In developing the template or classes there was never any doubt as to what the state of a variable was (as needed) and it was easy to follow the code execution stream.
The comment lines and the Method Start, Method End statements also made it easier to follow what was happening without having to study the Debugview output to know where you were.
AI Usage
One other benefit of having the markers and start/end comments is that we could copy/paste that portion of the log out and send it to an AI for help determining why something was not working. Not having to explain to the AI what we were seeing and giving it targeted info rather than exhausting the token window with the entire log made it work much better.
We allow the developer to turn it on/off in the template or in the classes and since you can do it in the classes it can be enabled/disabled on the fly.
I’ve added the date and time recently, but it takes up too much screen space, the time picture is helpful, as is the raw long format, but not precision enough, so I might add a counter to the function that increments every time the debugoutputstring is called, just to be sure the messages are being recieved in order.
You know there are viewers other than DebugView, right?
For the last 20+ years I’ve been using dbwin32, which has much fewer features but still gets the job done.
You desire a Linear Order view of Debug output from Multiple Threads?
Windows is preemptive multitasking plus symmetric multiprocessing (SMP). The execution of a thread can be interrupted by the OS at any time to switch to another thread. Like be switched right after the code you care about, but before Output Debug, so the Debug View is not Linear. And you likely have multiple CPU’s that can be running different threads simultaneously … so the order is ??? the same.
The only way to get your debug linear is to add your own synchronization, and that would be a mistake creating a bottleneck just for debug.
Your first question should be how exactly does OutputDebugString work. AFAIK there’s nothing from MS, but some reverse engineered info on the web that it uses a Mutex and Events. That could be dated. Since Vista Microsoft has done major work to improve Kernel Objects for SMP.
I suppose everybody’s experience with this is different but I’ve never run into a problem pushing data from multiple threads. That’s how I debugged one of the worst contracts I’ve ever dealt with. Not only were they multiple threads, they were from multiple EXE’s running side by side sharing data.
The original developer was fighting this when he passed away so I had no one to speak with about the problem. Turned out it was a synchronicity problem. Once I detected what was causing it, with multiple EXE’s on multiple threads, it was an easy fix.
I still have multiple TXT files retrieved from this and other problems and they include data from multiple EXE’s running concurrently.
Your mileage may vary but it worked for me.
Granted I run everything on a TUF gamers MB with 128GB ram and M.2 drives but still!
This Sample file was running on my box with multiple IP addresses sharing data across a test network. Both EXE’s running side by side. I was trying to determine why a new scale added to the mix was causing the comms to shutdown. This is one of the smallest files.
There’s no “problem” with multiple threads. Its that with preemptive multitasking and multiple processors it is possible that a thread switch before the Output Debug would delay output so the info may be stale because another thread changed it.
E.g. some code that starts 5 threads that will work on Global Glo:Cnt:
PROGRAM
Glo:Cnt LONG
Glo:Cs4nt CriticalSectionTYPE
MAP
ThreadWorker1 PROCEDURE()
END
CODE
LOOP 5 TIMES ; RESUME(START(ThreadWorker1)) ; END
RETURN
That Thread Worker increments the Count then outputs that Count to Debug View:
ThreadWorker1 PROCEDURE
CODE
LOOP 123456 TIMES
InterlockedIncrement( Glo:Cnt ) !thread safe Cnt += 1
SLEEP(0) !Likely Thread Switch
DebugOut('Cnt='& Glo:Cnt &' in Thread '& Thread())
END
In Debug View you hope to see the numbers increase perfectly by 1. But No, a Thread Switch between the Increment and DebutOut will prevent that. My including Sleep(0) makes a switch to one of the other 5 threads that will change the Count much more likely.
Without that Sleep(0) a switch could still happen, that’s all I’m saying. The Debug will likely be very helpful, but the order is not assured … unless …
To insure perfect debug order you would have to protect the Increment and DebugOut() call in a block with sync code.
ThreadWorker2 PROCEDURE
CODE
LOOP 123456 TIMES
EnterCriticalSection(Glo:Cs4Cnt)
Glo:Cnt += 1
DebugOut('Cnt='& Glo:Cnt &' in Thread '& Thread())
LeaveCriticalSection(Glo:Cs4Cnt)
END
I would not do the above except maybe for testing.
MS advise against using a Sleep(0) in this situation and advise using
Threads that are under concurrency control. For example, an I/O completion port or thread pool limits the number of associated threads that can run. If the maximum number of threads is already running, no additional associated thread can run until a running thread finishes. If a thread uses Sleep with an interval of zero to wait for one of the additional associated threads to accomplish some work, the process might deadlock.
For these scenarios, use MsgWaitForMultipleObjects or MsgWaitForMultipleObjectsEx, rather than Sleep.
But they recommend use of MsgWaitForMultipleObjects(Ex) when a thread has a window and accept loop, and WaitForMultipleObjects(Ex) for threads without a window and accept loop.
“In this situation” the example has no Window and no Accept Loop, so no Messages to wait on, which should be obvious that your post does not apply. The Sleep(0) is just for example of a way to force a Thread switch, and is just fine with no Accept loop.