成人免费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調試

2024-03-28 12:56:36

2023-06-26 00:12:46

2022-10-25 14:17:01

.NET代碼程序

2023-06-29 17:55:00

.NET日志WinDbg

2024-07-09 11:51:20

Windows線程池源碼

2024-07-12 11:20:34

.NET崩潰視覺程序

2023-03-26 20:24:50

ERP網站系統

2024-03-26 00:44:53

.NETCIM系統

2024-06-04 10:54:34

.NET代碼程序

2024-06-13 17:09:55

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 | 亚洲一区中文字幕在线观看 | 午夜成人免费视频 | 国产农村一级片 | 久久精品免费 | 成人影音 | 中文字幕一区二区三区精彩视频 | 欧美视频福利 | 成人在线视频一区 | 婷婷开心激情综合五月天 | 久久久久久国产精品久久 | 午夜三区 | 亚洲国产激情 | 亚洲成人一区二区三区 | 久草资源网站 | 激情欧美一区二区三区中文字幕 | 国产精品视频久久 | 天天操夜夜爽 | 精品久久国产 | 日韩最新网站 | 天天操夜夜操免费视频 | 国产精品69毛片高清亚洲 | 国产精品视屏 | 91精品一区 | 不卡一区 | 最新日韩精品 | 国产亚洲网站 | 日韩在线视频一区二区三区 | 一区二区三区在线看 | 视频二区 | 成人精品一区二区三区四区 | 成年人网站国产 | 免费视频一区二区 | 国产成人一区二区三区久久久 | 国产一级片av | 亚洲成人免费av | 婷婷丁香综合网 | 国产精品一区二区三级 | 亚洲视频在线播放 | 成人1区2区 | 亚洲综合婷婷 |