成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

記一次 .NET某工業設計軟件崩潰分析

開發 前端
一般來說崩潰在clr里都不是什么好事情,這預示著 clr 在執行自身代碼的時候拋了異常,即災難的 ExecutionEngineException,可以用 !t 驗證下。

一、背景

1. 講故事

前些天有位朋友找到我,說他的軟件在客戶那邊不知道什么原因崩掉了,從windows事件日志看崩潰在 clr 里,讓我能否幫忙定位下,dump 也抓到了,既然dump有了,接下來就上 windbg 分析吧。

二、WinDbg 分析

1. 為什么崩潰在 clr

一般來說崩潰在clr里都不是什么好事情,這預示著 clr 在執行自身代碼的時候拋了異常,即災難的 ExecutionEngineException,可以用 !t 驗證下。

0:000> !t
ThreadCount:      18
UnstartedThread:  0
BackgroundThread: 7
PendingThread:    0
DeadThread:       11
Hosted Runtime:   no
                                                                         Lock  
       ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
   0    1 52e8 18998d50     24220 Preemptive  639B0D58:00000000 18c361f0 0     STA System.ExecutionEngineException 1f421120
   ...

既然是災難性異常,那為什么會出現呢?可以用 !analyze -v 觀察下。

0:000> !analyze -v
CONTEXT:  0115a98c -- (.cxr 0x115a98c)
eax=00000000 ebx=00000000 ecx=00000000 edx=18c364a4 esi=00030000 edi=18998d50
eip=552bfff1 esp=0115ae6c ebp=0115af24 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
clr!VirtualCallStubManager::ResolveWorker+0x33:
552bfff1 8bb968020000    mov     edi,dword ptr [ecx+268h] ds:002b:00000268=????????
Resetting default scope

READ_ADDRESS:  00000268 

STACK_TEXT:  
0115af24 552c0698     0115afdc 1f4222c0 00030000 clr!VirtualCallStubManager::ResolveWorker+0x33
0115affc 552c070b     0115b010 1f4222c0 00030000 clr!VSD_ResolveWorker+0x1d2
0115b024 28a3a949     639b0d38 00000000 00000000 clr!ResolveWorkerAsmStub+0x1b
0115b0a4 28a3a8bd     00000000 00000000 00000000 xxxx!xxx
...

我去,真無語了,我卦中數據看,這是一個接口Stub調用的崩潰,在這里崩潰真的是少之又少,從匯編代碼 edi,dword ptr [ecx+268h] ds:002b:00000268=???????? 上看就是因為 ecx =0 導致的,接下來觀察下方法的匯編代碼。

圖片圖片

從匯編上看這個 ecx 其實就是這個方法的 this 指針,那為什么 this =null 呢?這就很奇葩了。

2. 為什么 this =null

要想找到這個答案,只能看clr源代碼,簡化后如下:

PCODE VSD_ResolveWorker(TransitionBlock* pTransitionBlock,
                        TADDR siteAddrForRegisterIndirect,
                        size_t token
                        )
{
    ...
    VirtualCallStubManager::StubKind stubKind = VirtualCallStubManager::SK_UNKNOWN;
    VirtualCallStubManager* pMgr = VirtualCallStubManager::FindStubManager(callSiteTarget, &stubKind);
    
    ...
    target = pMgr->ResolveWorker(&callSite, protectedObj, representativeToken, stubKind);
}

從卦中代碼看,問題就是 pMgr=null 導致的,無語了,這個 VirtualCallStubManager::FindStubManager 方法的本意就是根據 callSite的stub的前綴找到對應的 虛調用管理器,它的核心邏輯如下:

StubKind getStubKind(PCODE stubStartAddress, BOOL usePredictStubKind = TRUE)
{
    StubKind predictedKind = (usePredictStubKind) ? predictStubKind(stubStartAddress) : SK_UNKNOWN;
    ...
    if (predictedKind == SK_LOOKUP)
    {
        if (isLookupStub(stubStartAddress))
            return SK_LOOKUP;
    }
    ...
    return SK_UNKNOWN;
}

VirtualCallStubManager::StubKind VirtualCallStubManager::predictStubKind(TADDR stubStartAddress)
{
    StubKind stubKind = SK_UNKNOWN;

    WORD firstWord = *((WORD*)stubStartAddress);

    if (firstWord == 0x05ff)
    {
        stubKind = SK_DISPATCH;
    }
    else if (firstWord == 0x6850)
    {
        stubKind = SK_LOOKUP;
    }
    else if (firstWord == 0x8b50)
    {
        stubKind = SK_RESOLVE;
    }

    return stubKind;
}

接下來需要找到 stubStartAddress 的地址是多少?這個只需要提取 ResolveWorker 方法的第一個參數 callSite 即可。

0:000> dp poi(0115afdc) L1
0c740040  0c746012

0:000> u 0c746012
0c746012 50              push    eax
0c746013 6800000300      push    30000h
0c746018 e9d3a6b748      jmp     clr!ResolveWorkerAsmStub (552c06f0)
0c74601d 0000            add     byte ptr [eax],al
0c74601f 0000            add     byte ptr [eax],al
0c746021 005068          add     byte ptr [eax+68h],dl
0c746024 0000            add     byte ptr [eax],al
0c746026 46              inc     esi

0:000> dp 0c746012 L1
0c746012  00006850

對比剛才的代碼既然都返回來了 SK_LOOKUP 那為什么還是 SK_UNKNOWN 呢?這個也可以通過在線程棧上找到 &stubKind 變量得到驗證。

0:000> uf 552c0698
...
clr!VSD_ResolveWorker+0x1ab:
552c065f 8b85e0ffffff    mov     eax,dword ptr [ebp-20h]
552c0665 83a5ecffffff00  and     dword ptr [ebp-14h],0
552c066c 8d95ecffffff    lea     edx,[ebp-14h]
552c0672 8b08            mov     ecx,dword ptr [eax]
552c0674 e858feffff      call    clr!VirtualCallStubManager::FindStubManager (552c04d1)
552c0679 ffb5ecffffff    push    dword ptr [ebp-14h]
552c067f 51              push    ecx
552c0680 8bcc            mov     ecx,esp
552c0682 8931            mov     dword ptr [ecx],esi
552c0684 ffb5e8ffffff    push    dword ptr [ebp-18h]
552c068a 8d8de0ffffff    lea     ecx,[ebp-20h]
552c0690 51              push    ecx
552c0691 8bc8            mov     ecx,eax
552c0693 e823f9ffff      call    clr!VirtualCallStubManager::ResolveWorker (552bffbb)
552c0698 8bf0            mov     esi,eax
...

0:000> dp 0115affc-0x14 L1
0115afe8  00000000

我感覺這邏輯也只有clr團隊幫忙解釋,我已經搞不清楚了,接下來我們回頭看托管方法,看能不能繼續下去。

3. 在托管層尋找突破口

高級調試就是這樣,一個方向走不通就需要在另一個方向上突破,接下來使用 !clrstack 觀察一下。

0:000> !clrstack
OS Thread Id: 0x52e8 (0)
Child SP       IP Call Site
0115af50 775c2aac [GCFrame: 0115af50] 
0115afac 775c2aac [StubDispatchFrame: 0115afac]xxx.GetListDrawerType(System.String)
0115b02c 28a3a949 xxx.PluginInvoker.InvokeMothod[[System.__Canon, mscorlib]](System.String, System.Object[])
0115b0b0 28a3a8bd xxx.xxx.OnFinishSizeCheck(Int64)
...

從調用棧來看,貌似是用反射來實現功能增強,不管怎么說先看下xxxCheck 方法干了什么?簡化后的代碼如下:

public string OnFinishSizeCheck(long uuid)
{
    return PluginInvoker.InvokeMothod<string>("xxxCheck", new object[1] { uuid });
}

public static T InvokeMothod<T>(string methodName, params object[] args)
{
    IPluginInvoker pluginInvoker = GetPluginInvoker();

    return (T)pluginInvoker.InvokeMothod(methodName, args);
}

從代碼上可以看到原來是使用 (T)pluginInvoker.InvokeMothod(methodName, args); 實現的接口調用,在coreclr層面也能觀察得到,找到對象 1f4222c0 之后按圖索驥即可。

0:000> !do 1f4222c0
Name:        xxx.xxx.BusinessAppDomainInvoker
MethodTable: 0c73a144
EEClass:     0c6d6f0c
Size:        12(0xc) bytes
File:        E:\xxx\xxx.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
0c73a4e8  400000a        4 ....AppDomainManager  0 instance 1f42236c appDomainManager
0c73a2dc  4000009       18 ..., xxx]]  0   static 1f422214 lazy

0:000> !dumpmt -md 0c73a144
EEClass:         0c6d6f0c
Module:          0c7383dc
Name:            xxx.xxx.BusinessAppDomainInvoker
mdToken:         02000006
File:            E:\xxx\xxx.dll
BaseSize:        0xc
ComponentSize:   0x0
Slots in VTable: 10
Number of IFaces in IFaceMap: 1
--------------------------------------
MethodDesc Table
   Entry MethodDe    JIT Name
   ...
0c6c3400 0c73a110    JIT xxx.xxx.InvokeMothod(System.String, System.Object[])

0:000> !do  poi(0c73a144+0x24)
Name:        xxx.IPluginInvoker
MethodTable: 0c739f30
EEClass:     0c6d6d34
Size:        0(0x0) bytes
File:        E:\xxx\xxx.dll
Fields:
None
ThinLock owner 1 (18998d50), Recursive 0

對比那個 token=30000h 發現什么地方都沒有問題,奇葩的就是一個簡單接口調用就出現了問題,仔細觀察代碼之后發現了兩個和別人不一樣的地方。

4. 與眾不同的地方在哪里

第一個是他的程序是多 AppDomain 的,可以用 !dumpdomain 觀察。

0:000> !dumpdomain
--------------------------------------
System Domain:      55a6caa0
...
--------------------------------------
Shared Domain:      55a6c750
LowFrequencyHeap:   55a6cdc4
Stage:              OPEN
--------------------------------------
Domain 1:           18b04690
LowFrequencyHeap:   18b04afc
Name:               DefaultDomain
--------------------------------------
Domain 2:           18c361f0
LowFrequencyHeap:   18c3665c
...

第二個是我發現托管調用棧上還有很多 托管C++,這種混合編程真的是無語了。

到這里我想到了三個辦法:

1)如果可以先把接口方法預熱,clr會直接把方法入口塞到匯編里,就不會再走clr底層邏輯了。

2)能否將 托管C++ 和 C# 隔離,不要混合編程。

3)重點觀察下多Domain下這個托管調用是不是有什么問題。

三、總結

這種 多domain + 托管C++混合C# 編程,真出問題了基本上就是無解,一般人hold不住,無語了。

責任編輯:武曉燕 來源: 一線碼農聊技術
相關推薦

2024-12-27 13:31:18

.NETdump調試

2023-06-26 00:12:46

2024-03-28 12:56:36

2024-07-12 11:20:34

.NET崩潰視覺程序

2023-03-26 20:24:50

ERP網站系統

2024-03-26 00:44:53

.NETCIM系統

2022-10-25 14:17:01

.NET代碼程序

2023-06-29 17:55:00

.NET日志WinDbg

2024-07-09 11:51:20

Windows線程池源碼

2024-06-13 17:09:55

2024-06-04 10:54:34

.NET代碼程序

2023-09-27 07:23:10

.NET監控軟件

2023-05-15 11:15:50

.NET門診語句

2023-10-07 13:28:53

.NET軟件賬本

2022-10-09 10:47:37

NET視覺軟件

2023-04-06 10:52:18

2024-08-27 13:08:50

2023-07-06 10:11:38

.NET模式dump

2022-10-13 18:40:05

.NETOA后端

2024-05-20 09:39:02

.NETurl線程池
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲欧美日本在线 | 国产视频精品在线观看 | 国内精品久久久久久久影视简单 | 国产乱码精品一区二区三区忘忧草 | 91一区| 一区二区国产精品 | 黄网站在线播放 | av高清| 福利视频一区二区 | 欧美综合一区二区 | www久久 | 欧美亚洲视频在线观看 | 99精品99| 午夜影晥 | 一区二区在线 | 亚洲精品字幕 | 日韩福利在线观看 | av中文字幕在线 | 国产美女视频黄a视频免费 国产精品福利视频 | 国产91在线 | 亚洲 | 日韩精品一区二区三区在线观看 | 奇米av | 久久久久久黄 | 欧美一区二区三区久久精品 | 黄频视频 | 欧洲精品在线观看 | 成人在线视频免费观看 | 精品欧美一区二区三区 | 在线国产一区二区 | 成人深夜小视频 | 精品视频在线一区 | 日韩视频一区在线观看 | 国产电影一区二区在线观看 | 一区二区视频在线 | 欧美日韩在线高清 | 999热在线视频 | 成年人黄色免费视频 | 尤物在线视频 | 久久久.com | 婷婷综合色 | 欧美日韩国产一区二区三区 |