Location>code7788 >text

NET a host computer vision program stuck analysis

Popularity:47 ℃/2024-09-19 11:37:27

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 thisprocessidcap (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=0x80What 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 explorationsstyleManager1was 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.

  1. Focus on the styleManager1 control and observe the program running by elimination.
  2. 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!

图片名称