I: Background
1. Storytelling
Some time ago a friend found me, said his form program in the customer side of the card dead, let me help look at how the dump also generated, since there is a dump then on windbg analyze it.
II: WinDbg Analysis
1. Why is it stuck?
Forms program stuck, the entry threshold is very low, subsequent analysis down the line is not necessarily, in any case, first use !clrstack to look at the main thread, the output is as follows:
0:000> !clrstack
OS Thread Id: 0x3118 (0)
Child SP IP Call Site
000000c478afd1d8 00007ffc284e9a84 [HelperMethodFrame_1OBJ: 000000c478afd1d8] (, UInt32, Boolean, Boolean)
000000c478afd300 00007ffbf2cc19ac (, Int64, Boolean, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\ @ 243]
000000c478afd330 00007ffbf2cc197f (Int32, Boolean) [f:\dd\ndp\clr\src\BCL\system\threading\ @ 194]
000000c478afd370 00007ffbf1421904 ()
000000c478afd3e0 00007ffbf0c8e2f4 (, , [], Boolean)
000000c478afd520 00007ffbf1425124 (, [])
000000c478afd590 00007ffb995d6fe8 (, )
000000c478afd5f0 00007ffb995d69ff .set_ColorTint()
000000c478afd680 00007ffb995d694c .set_ManagerColorTint()
...
000000c478afd6b0 00007ffb995d50f9 ()
Experienced friends see the above trigrams believe that we know how things, that is, there are working threads created by the user control, and this control seems to be related to DevComponents, the next routine is to dig a WindowsFormsSynchronizationContext object to see which thread is actually created, using the !dso.
0:000> !dso
OS Thread Id: 0x3118 (0)
RSP/REG Object Name
000000C478AFCF98 000002093b9143c0
...
0:000> !do poi(20939c91588)
Name:
MethodTable: 00007ffbf2769580
EEClass: 00007ffbf288c658
Size: 96(0x60) bytes
00007ffbf276aaf8 4001934 4c System.Int32 1 instance 1 m_ManagedThreadId
According to the script then WindowsFormsSynchronizationContext should have 2, but here only 1, this one is still the main thread synchronization context, this is the end of the game. Completely not according to the script, which is the complexity of the real dump analysis, so who actually created it? Is the sky going to be the limit?
2. Where to go from here
Everything lands in assembly, which in turn is in methods, so the breakthrough is to look for methods in the thread stack, next to the Take a look in the method to see what big stuff is available, simplified as follows:
private object MarshaledInvoke(Control caller, Delegate method, object[] args, bool synchronous)
{
bool flag = false;
if ((new HandleRef(this, Handle), out var _) == () && synchronous)
{
flag = true;
}
ThreadMethodEntry threadMethodEntry = new ThreadMethodEntry(caller, this, method, args, synchronous, executionContext);
lock (threadCallbackList)
{
if (threadCallbackMessage == 0)
{
threadCallbackMessage = ( + "_ThreadCallbackMessage");
}
(threadMethodEntry);
}
if (flag)
{
InvokeMarshaledCallbacks();
}
else
{
(new HandleRef(this, Handle), threadCallbackMessage, , );
}
if (synchronous)
{
if (!)
{
WaitForWaitHandle();
}
return ;
}
return threadMethodEntry;
}
From the code in the trigram, this method is key, it gets the window created by this
processid
cap (a poem)threadid
, next observe the simplified assembly code.
0:000> !U /d 00007ffbf0c8e2f4
preJIT generated code
(, , [], Boolean)
Begin 00007ffbf0c8dec0, size 4e9
00007ffb`f0c8dec0 55 push rbp
00007ffb`f0c8dec1 4157 push r15
00007ffb`f0c8dec3 4156 push r14
00007ffb`f0c8dec5 4155 push r13
00007ffb`f0c8dec7 4154 push r12
00007ffb`f0c8dec9 57 push rdi
00007ffb`f0c8deca 56 push rsi
00007ffb`f0c8decb 53 push rbx
00007ffb`f0c8decc 4881ecf8000000 sub rsp,0F8h
00007ffb`f0c8ded3 488dac2430010000 lea rbp,[rsp+130h]
...
00007ffb`f0c8dff0 488d55b0 lea rdx,[rbp-50h]
00007ffb`f0c8dff4 ff151e1eddff call qword ptr [System_Windows_Forms_ni+0x8fe18 (00007ffb`f0a5fe18)] ((, Int32 ByRef), mdToken: 00000000060033c4)
00007ffb`f0c8dffa 448bf0 mov r14d,eax
According to the compilation in trigrams and the x64 calling protocols, thelea rdx,[rbp-50h]
is our processid, and themov r14d,eax
The r14d in r14d is our threadid. The breakthrough has been found, so the next step is to dig deeper.
3. How to dig out process IDs and thread IDs
One thing to know 000000c478afd520 and MarshaledInvoke method rsp separated by a 0x8, at the same time the method affects the rsp of push and sub should be counted, here will not go into detail, you can refer to the article:/huangxincheng/p/ A simple calculation is as follows:
0:000> ? 000000c478afd520-0x8-(0n8*0n8)-0xF8+0x130
Evaluate expression: 843838379280 = 000000c4`78afd510
0:000> dp 000000c4`78afd510-0x50 L1
000000c4`78afd4c0 00000000`000029dc
0:000> r r14
r14=000000c478afcf14
0:000> dp 000000c478afcf14 L1
000000c4`78afcf14 00000000`00000080
From the trigramsprocessid=29dc ,threadid=0x80
What is this thing? Let's use ~ to find out what it really is.
0:000> ~
...
18 Id: 29dc.80 Suspend: 0 Teb: 000000c4`7890d000 Unfrozen
...
0:018> k
# Child-SP RetAddr Call Site
00 000000c4`7a2ffcc8 00007ffc`28028ba3 ntdll!NtWaitForSingleObject+0x14
01 000000c4`7a2ffcd0 00007ffb`fa651cf8 KERNELBASE!WaitForSingleObjectEx+0x93
02 000000c4`7a2ffd70 00007ffb`fa652a51 wpfgfx_v0400!CPartitionManager::GetWork+0x17b
03 000000c4`7a2ffdc0 00007ffb`fa67a2fb wpfgfx_v0400!CPartitionThread::Run+0x21
04 000000c4`7a2ffdf0 00007ffc`2a037bd4 wpfgfx_v0400!CPartitionThread::ThreadMain+0x2b
05 000000c4`7a2ffe20 00007ffc`2a76ced1 kernel32!BaseThreadInitThunk+0x14
06 000000c4`7a2ffe50 00000000`00000000 ntdll!RtlUserThreadStart+0x21
Now it's a bit confusing, how come there's a wpf rendering thread in winform? It's possible that it's a third-party control like DevComponents that's introduced at the bottom. Here the way is blocked again, where to go next? Three steps forward and one step back, continue to look at the main thread on the method code.
4. Finding answers in the source code
Although the breakout failed on both paths, it was clear to see that it was getting really close to the truth, and a great deal of combat information had been harvested, through the aboveset_ManagerColorTint
The decompilation of the method is referenced below:
private void InitializeComponent()
{
this. = ;
}
[Description("Indicates color current style is tinted with.")]
[Category("Appearance")]
public Color ManagerColorTint
{
get
{
return ColorTint;
}
set
{
ColorTint = value;
}
}
Too speechless after seeing the source code, which is actually a simplecolor assignment
, based on the previous explorationsstyleManager1
was created by the rendering thread, so naturally the main thread's assignment to it gets no feedback from the rendering thread.
So what to do about this problem? Probably the following two.
- Focus on the styleManager1 control and observe the program running by elimination.
- See if the documentation is using the styleManager1 control in the wrong way.
III: Summary
It's still interesting to see how this production accident can exist in WinForm.CPartitionThread
The rendering thread, and ultimately the bane of its existence, added a dash of color to my journey through hundreds of cases of dump analysis!