This article is a part of working with bug reports.
Important note: please read Using EurekaLog in multi-threaded applications article before reading this article.
Call stacks in multi-threaded applications contain additional information.
For example:
Exception in background thread (GUI)
Call Stack Information: ---------------------------------------------------------------------------------------------- |Address |Module |Unit |Class |Procedure/Method |Line | ---------------------------------------------------------------------------------------------- |*Exception Thread: ID=2940; Parent=3696; Priority=0 | |Class=TMyThread; Name= (Unit1.TMyThread.Execute) | |DeadLock=0; Wait Chain= | |Comment= | |--------------------------------------------------------------------------------------------| |0069209C|Project1.exe|Unit1 | |DoCrash |77[0] | |006920CA|Project1.exe|Unit1 |TMyThread |DoWork |82[2] | |00692080|Project1.exe|Unit1 |TMyThread |Execute |69[1] | |004DCA96|Project1.exe|System.Classes | |ThreadProc |14569[12] | |004094AC|Project1.exe|System | |ThreadWrapper |21627[45] | |005919AE|Project1.exe|EExceptionManager| |DefaultThreadHandleException|2852[5] | |0056F32B|Project1.exe|EThreadsManager | |ThreadWrapper |611[11] | |75B9F26F|kernel32.dll|kernel32 | |BaseThreadInitThunk | | |00692075|Project1.exe|Unit1 |TForm1 |Button2Click |63[2] | |--------------------------------------------------------------------------------------------| |Calling Thread: ID=3696; Parent=0; Priority=0 | |Class=; Name=MAIN | |DeadLock=0; Wait Chain=thread: [ 0E70 / 3696 ] is blocked | |Comment= | |--------------------------------------------------------------------------------------------| |76718390|USER32.dll |USER32 | |NtUserWaitMessage | | |006811AB|Project1.exe|Vcl.Forms |TApplication|HandleMessage |10238[1] | |006814D9|Project1.exe|Vcl.Forms |TApplication|Run |10376[26] | |0069BCFD|Project1.exe|Project1 | |Initialization |22[4] | |75B9F26F|kernel32.dll|kernel32 | |BaseThreadInitThunk | | ----------------------------------------------------------------------------------------------
Exception in background thread (text)
It contains 2 call stacks: one per each thread. There are two threads in this application: main thread and a background thread based on TThread class. There was exception raised in the background thread.
This example illustrates several multi-threaded features of EurekaLog:
Call stacks for multiple threadsBug report contains a single call stack by default. This call stack is created for thread which raised the exception. Such thread is called "Exception thread". This is the only thread in single-threaded application (it will be main thread for single-threaded application). Sometimes you may be interested in other threads, you may want to know what other threads are doing when exception had occurred. You can capture call stacks of other threads in application by checking "Capture stack of RTL threads" or "Capture stack of Windows threads" options.
Notes:
Different call stacks typesEurekaLog marks call stacks as:
Note: the meaning of exception and calling threads are changed significantly for exceptions within synchronized methods (see below).
Different thread typesEurekaLog can recognize the following thread types:
It's recommended to create your own threads via TThreadEx class or via BeginThreadEx function (or via any other function which use TThread or BeginThread to create threads). Do not use CreateThread function to create threads. This will allow you to distinguish between your threads and system threads (system or 3rd party code may create additional threads in your application for background/service tasks).
Naming threadsYou can name threads to simplify debugging. Since you can run multiple threads with the same thread class (or thread function) - this means that you can not distinguish between these threads by using only class name (or thread function's name). You need some additional marker. Such marker is a thread name.
You can supply thread name directly to TThreadEx class or BeginThreadEx function:
uses
If you are not using TThreadEx/BeginThreadEx - then you can name a thread from the thread itself. You can set thread's name by using NameThread function from EThreadsManager unit. You should call this function as a very first action inside thread function (for BeginThread) or Execute method (for TThread). Thread name can be arbitrary string, so you can append any parameters that you need to distinguish between threads. For example:
{$IFDEF EUREKALOG} uses {$ENDIF} // ... any other arguments
Any thread name will be shown in the call stack's header as "Name=your-thread-name" (without parentheses; part inside parentheses is a name of thread function - see above). For the example (the one directly above) it will be:
Name=FileName=C:\Sample.txt (MyThreadProc)
(assuming that Edit1.Text holds 'C:\Sample.txt' string)
Note: main thread will always have a name "MAIN".
Indicating creator of the threadSince you can run multiple threads with the same thread class (or thread function) and with the same arguments from multiple locations - this means that you can not distinguish between these threads by using class name (or thread function's name) and any properties of the thread itself. You need to use name of thread's creator.
EurekaLog appends creator of the thread to the end of thread's call stack. Typically this is a single entry below BaseThreadInitThunk system function. For example, this example of the call stack shows that TMyThread was created in Unit1.TForm1.Button2Click method, line 63:
Call Stack Information: ----------------------------------------------------------------- |Address |Module |Unit |Class |Procedure/Method |Line | ----------------------------------------------------------------- |*Exception Thread: ID=2940; Parent=3696; Priority=0 | |Class=TMyThread; Name= (Unit1.TMyThread.Execute) | |DeadLock=0; Wait Chain= | |Comment= | |---------------------------------------------------------------| |0069209C|Project1.exe|Unit1 | |DoCrash |77[0]| <- ..........................................................-> |75B9F26F|kernel32.dll|kernel32| |BaseThreadInitThunk| | |00692075|Project1.exe|Unit1 |TForm1|Button2Click |63[2]| |---------------------------------------------------------------|
Notes:
Indicating threads optionsEurekaLog lists some options of threads, such as:
Wait Chain Traversal feature supportEurekaLog has support for WCT (Wait Chain Traversal) feature available since Windows Vista. WCT is a mechanism for debugging blocked threads and processes, and detecting deadlocks.
Using WCT, debugging software can analyze executing processes and report on the state of threads, including information such as what a blocked thread is waiting for and whether a deadlock condition exists. Debugging software can analyze and then reports the state of threads. A thread's state (unblocked, blocked, or deadlocked) is reported as a wait chain. A wait chain is an alternating directed graph of threads and synchronization objects. In the graph, an edge from a thread to an object indicates that the thread is waiting for the object; an edge from an object to a thread indicates that the thread is the current owner of the object. For example, the following wait chain represents a thread (Thread A) that is blocked waiting for a mutex object that is owned by Thread B.
Thread A -> Mutex 1 -> Thread B
As illustrated here, the first node in a wait chain is the thread being analyzed and the remaining nodes are the elements (synchronization objects, processes, threads, and so on), if any, that are directly or indirectly causing the thread to block. The simplest wait chain reflects a thread that is not blocked. It is composed of a single node, representing the unblocked thread. A blocked thread is represented by a wait chain containing multiple nodes that is non-cyclic: that is, there are no two nodes in the chain that represent the same thread or object. When a thread is deadlocked, the wait chain that represents it is cyclic. Consider the scenario where Thread A owns Object 1 and is blocked waiting for Object 2, while Thread B owns Object 2 and is blocked waiting for Object 1. The wait chain is as follows:
Thread A -> Object 2 -> Thread B -> Object 1 -> Thread A
Note that Thread A appears twice. The second node for Thread A could be replaced by an edge (->) that connects Object 1 to the first Thread A node, creating a loop that represents the cyclic dependency.
WCT feature is accessible only on Windows Vista and above. "Deadlock" and "Wait chain" values will be empty on older OS versions.
Parenting threadsEurekaLog lists TID ("ID") and PTID ("Parent") values to help you identify child/parent threads. Additionally, a parent thread for exception thread is marked as "Calling thread" and is shown below exception thread for your convenience. It will be not shown if this thread was terminated before exception or if the corresponding "Capture stack of XYZ threads" option is turned off.
Merging call stacks for SynchronizeEurekaLog will merge call stacks of main thread and invoking thread (that is the thread which called Synchronize method) when possible. The result may look like this:
Exception inside synchronized routine (GUI)
Call Stack Information: -------------------------------------------------------------------------------------------------- |Address |Module |Unit |Class |Procedure/Method |Line | -------------------------------------------------------------------------------------------------- |*Exception Thread: ID=400; Parent=0; Priority=0 | |Class=; Name=MAIN | |DeadLock=0; Wait Chain=thread: [ 0190 / 400 ] is blocked | |Comment= | |------------------------------------------------------------------------------------------------| |0069211C|Project1.exe|Unit1 | |DoCrash |81[1] | |0069214F|Project1.exe|Unit1 |TMyThread |DoWork |85[1] | |004DC958|Project1.exe|System.Classes | |CheckSynchronize |14525[30] | |------------------------------------------------------------------------------------------------| |Calling Thread: ID=2072; Parent=400; Priority=0 | |Class=TMyThread; Name= (Unit1.TMyThread.Execute) | |DeadLock=0; Wait Chain= | |Comment= | |------------------------------------------------------------------------------------------------| |004DD5A2|Project1.exe|System.Classes |TThread |Synchronize |15085[38] | |006920B8|Project1.exe|Unit1 |TMyThread |Execute |71[3] | |004DCA96|Project1.exe|System.Classes | |ThreadProc |14569[12] | |004094AC|Project1.exe|System | |ThreadWrapper |21627[45] | |005919BE|Project1.exe|EExceptionManager| |DefaultThreadHandleException|2853[5] | |0056F32B|Project1.exe|EThreadsManager | |ThreadWrapper |611[11] | |75B9F26F|kernel32.dll|kernel32 | |BaseThreadInitThunk | | |00692085|Project1.exe|Unit1 |TForm1 |Button2Click |63[2] | |------------------------------------------------------------------------------------------------| |Calling Thread: ID=400; Parent=0; Priority=0 | |Class=; Name=MAIN | |DeadLock=0; Wait Chain=thread: [ 0190 / 400 ] is blocked | |Comment= | |------------------------------------------------------------------------------------------------| |76718390|USER32.dll |USER32 | |NtUserWaitMessage | | |006811AB|Project1.exe|Vcl.Forms |TApplication |HandleMessage |10238[1] | |006814D9|Project1.exe|Vcl.Forms |TApplication |Run |10376[26] | |0069BCFD|Project1.exe|Project1 | |Initialization |22[4] | |75B9F26F|kernel32.dll|kernel32 | |BaseThreadInitThunk | | --------------------------------------------------------------------------------------------------
Same exception in text
The above example should be translated as:
Notes:
See also:
|