This fourth post of the ClrMD series digs into the details of figuring out which method gets called when a timer triggers. The associated code lists all timers in a dump.
Part 1: Bootstrapping ClrMD to load a dump.
Looking at my timer
In the previous post, we explained how to access a static field of TimerQueue to start iterating the list of TimerQueueTimer wrapping the created timers. Now that the currentPointer variable contains the address of each TimerQueueTimer, it is time to extract the details of the timer we have created.
The following code extracts the value of the TimerQueueTimer fields corresponding to each Timer thanks to the GetFieldValue helper presented in the previous post:
Note that the value for m_dueTime is always the same as the value of m_period. This is not a bug but it seems that .NET is only keeping the due time during construction but use the corresponding field for other purpose after.
The m_state field case is a little bit more complicated to decipher because the type of the object passed to the timer needs to be figured out in addition to its address, if the latter is not null:
As usual with ClrMD, you need to get the ClrType corresponding to the object referenced by an address before being able to access its fields or to get its name. However, instead of looking into a module as it has been done for TimerQueue, it is easier and more efficient to call the GetObjectType from ClrHeap. Remember that the mandatory test against a null value for the ClrType might seem overkill but the ClrMD implementation states that it is possible that the internal CLR state could be corrupted.
What is the timer callback?
The last piece of information to retrieve is the callback the timer will call when it triggers. The _timerCallback field references a TimerCallback instance that stores these details.
But how to get the name of the method just with the address of a TimerCallback object? Again, open up your favorite decompiler and look at the type hierarchy:
Here are the two fields of the Delegate type that are interesting:
The _methodPtr field stores the pointer to the method. By chance, the ClrRuntime GetMethodByAddress method takes this address and returns the name of the method!
If this method is static, the _target fields is null. Otherwise, it stores the value of this, the hidden parameter received by all instance methods. In case of type inheritance, it is interesting to know which override will be called. All these steps are wrapped in the following helper function:
Building a usable summary
Even though the EnumerateTimers helper provides a way to list all timers, you often don’t want to show them all; especially when thousands exist and most of them are duplicates. The sample code associated to this post lists the different timers, count the duplicates and sort the result by duplicate count as shown in the following screenshot:
After timers, the next post will show how to integrate your ClrMD-based code into an extension for WinDBG to help decyphering Task state.
Post written by:
Staff Software Engineer, R&D.
Senior Software Engineer
Our lovely Community Manager / Event Manager is updating you about what's happening at Criteo Labs.See DevOps Engineer roles