记一次 Visual Studio 2022 卡死分析

sw

一:背景1.讲故事

最近不知道咋了,各种程序有问题都寻上我了,你说.NET程序有问题找我能理解,Windows崩溃找我,我也可以试试看,毕竟对Windows内核也知道一丢丢,那VisualStudio有问题找我就说不过去了,但又不好拒绝,就让朋友发下卡死的dump我看一看。

二:WinDbg分析1.到底是哪里的卡死

因为VS是窗体程序,所以在卡死的时候看下主线程便知,使用~0s;!clrstack即可。


0:000k
#Child-SPRetAddrCallSite
000000004b`acaf9b90000001ed`309f0f280x00007ffb`1b77bfe8
010000004b`acaf9b9800007ffb`4a03e3970x000001ed`309f0f28
020000004b`acaf9ba000007ffb`4a04c08ePresentationFramework_ni!+0x1d7
030000004b`acaf9c7000007ffb`4ab3bd36PresentationFramework_ni!+0x8e
040000004b`acaf9ce000007ffb`4ab3bd6ePresentationFramework_ni!+0xc6
050000004b`acaf9d2000007ffb`4ab3c94fPresentationFramework_ni!+0xe
060000004b`acaf9d6000007ffb`4ba3f72cPresentationFramework_ni!+0xf
070000004b`acaf9d9000007ffb`4ba42026PresentationCore_ni!+0x3c
080000004b`acaf9de000007ffb`4ba41e8bPresentationCore_ni!+0x36
090000004b`acaf9e2000007ffb`4bcc5632PresentationCore_ni!+0x7b
0a0000004b`acaf9e7000007ffb`4c182cf8PresentationCore_ni!__DisplayClass11_0.Invokeb__0+0x32
0b0000004b`acaf9eb000007ffb`4c182bf6WindowsBase_ni!+0x68
0c0000004b`acaf9f2000007ffb`4c180202WindowsBase_ni!+0x36
0d0000004b`acaf9f7000007ffb`4bca4423WindowsBase_ni!+0x172
0e0000004b`acafa01000007ffb`480629e1PresentationCore_ni!+0xb3
0f0000004b`acafa07000007ffb`7af71059UIAutomationTypes_ni+0x729e1
100000004b`acafa0e000007ffb`7ae13ebaclr!COMToCLRDispatchHelper+0x39
110000004b`acafa11000007ffb`7af70fb7clr!COMToCLRWorker+0x1ea
120000004b`acafa1b000007ffb`625b4cc9clr!GenericComCallStub+0x57


我丢,这线程栈一看有意外发现哈,这PresentationCore_ni不是WPF的专用库嘛,下面还有clr,看样子VS的UI是WPF写的,顿时有一种亲切感,那既然是.NET程序我还是可以分析的。

进一步观察线程栈,可以看到它没有非托管的部分,诸如:,,也就说明此时的卡死只是托管层面,接下来使用sos专有的!clrstack观察,删减后如下:


0:000!clrstack
OSThreadId:0x8144(0)
ChildSPIPCallSite
0000004bacafa22000007ffb1b77bfe8[ComMethodFrame:0000004bacafa220]
0000004bacafb0d800007ffb1b77bfe8[InlinedCallFrame:0000004bacafb0d8]()
0000004bacafb0d800007ffb4c357e22[InlinedCallFrame:0000004bacafb0d8]()
0000004_STUB_PInvoke()
0000004(,Boolean)
0000004(,,,Boolean,,Boolean,Boolean)
0000004(,,Boolean,Boolean)
0000004+c__DisplayClass176_0.b__0()
0000004()

0000004(,)
0000004(,)
0000004(,Boolean,)
0000004(,Boolean,)
0000004(,)

0000004_STUB_COMtoCLR(IntPtr,Int32,Int32,Int64,Int64)
0000004bacafde0000007ffb7af71011[ComMethodFrame:0000004bacafde00]

从卦中看,是在处理剪贴板OleSetClipboard的逻辑中一直出不来,而且还有一个外来的插件,看样子朋友的某些操作让Resharper介入了。

为了进一步验证是不是Resharper导致的,可以根据ip找到所属的模块。


0:000!ip2md00007ffb2edd5407
MethodDesc:00007ffb28e984b0
MethodName:(,Boolean,)
Class:00007ffb28ea1b40
MethodTable:00007ffb28e98578
mdToken:00000000060000aa
Module:00007ffb201c48b8
IsJitted:yes
CodeAddr:00007ffb2edd4f60
Transparency:Critical
0:000!DumpModule/d00007ffb201c48b8
Name:C:\Users\Administrator\AppData\Local\JetBrains\Installations\ReSharperPlatformVs17_265273ed_001\
Attributes:PEFileSupportsUpdateableMethods
Assembly:000001edb0cce780
LoaderHeap:0000000000000000
TypeDefToMethodTableMap:00007ffb201d0020
TypeRefToMethodTableMap:00007ffb201d03a8
MethodDefToDescMap:00007ffb201d10a0
FieldDefToDescMap:00007ffb201d21c0
MemberRefToDescMap:0000000000000000
FileReferencesMap:00007ffb201d2ae0
AssemblyReferencesMap:00007ffb201d2ae8
MetaDatastartaddress:000001edb6b8a960(103320bytes)

从卦中的Name来看,再一次确认了ReSharper的问题。

2.ReSharper是阻塞还是死锁

本着4S店只换不修的思路,让朋友直接卸载掉VS中的ReSharper肯定是没问题的,但为了兴趣继续探究下Resharper正在做什么?


从汇编代码看,当前正准备做两个if判断,而且都是true,最后跳转到属性中,不管怎么说,这里还是不断的处理,所以我觉得这里的阻塞要么是死循环出不来,要么还需要再等等。

可能有些朋友好奇,Resharper塞入到剪贴板中到底是什么数据,要想挖这个信息,可以看汇编从线程栈提取,这次就不搞这么复杂了,换个思路吧,先看CriticalSetDataObject方法源码,输出如下:


internalstaticvoidCriticalSetDataObject(objectdata,boolcopy)
{

IComDataObjectdataObject;

if(dataisDataObject)
{
dataObject=(DataObject)data;
}
elseif(dataisIComDataObject)
{
();
dataObject=(IComDataObject)data;
}
else
{
dataObject=newDataObject(data);
}

}

接下来用!dso看下有没有类似的DataObject和IComDataObject对象,输出如下:


0:000!dso
OSThreadId:0x8144(0)
RSP/REGObjectName

0000004


0:000!mdt000001ed401e9ec8
000001ed401e9ec8()
_innerData:000001ed401e9ee0(+DataStore)
0:000!mdt000001ed401e9ee0
000001ed401e9ee0(+DataStore)
_data:000001ed401e9ef8()
0:000!mdt000001ed401e9ef8
000001ed401e9ef8()
buckets:000001ed401e9f48(+bucket[],Elements:3,ElementMT=00007ffb79123af8)
count:0x2()
occupancy:0x1()
loadsize:0x2()
loadFactor:0.720000()
version:0x2()
isWriterInProgress:false()
keys:()
values:()
_keycomparer:()
_syncRoot:()
expandall2items
0:000!mdt000001ed401e9f48
000001ed401e9f48(+bucket[],Elements:3,ElementMT=00007ffb79123af8)
expandall3items
0:000!mdt-e:2000001ed401e9f48
000001ed401e9f48(+bucket[],Elements:3,ElementMT=00007ffb79123af8)
[0](+bucket)VALTYPE(MT=00007ffb79123af8,ADDR=000001ed401e9f58)
key:000001ed310ea6f0()Length=11,String="UnicodeText"
val:000001ed401e9fd0(+DataStore+DataStoreEntry[],Elements:1)
hash_coll:0xf337c502()
[1](+bucket)VALTYPE(MT=00007ffb79123af8,ADDR=000001ed401e9f70)
key:000001ed310eaa08()Length=16,String="RichTextFormat"
val:000001ed401ea018(+DataStore+DataStoreEntry[],Elements:1)
hash_coll:0x30818946()
[2](+bucket)VALTYPE(MT=00007ffb79123af8,ADDR=000001ed401e9f88)
key:()
val:()
hash_coll:0x0()
increasedepth
0:000!mdt000001ed401ea018
000001ed401ea018(+DataStore+DataStoreEntry[],Elements:1)
expandall1items
0:000!mdt-e:2000001ed401ea018
000001ed401ea018(+DataStore+DataStoreEntry[],Elements:1)
[0]000001ed401e9ff0(+DataStore+DataStoreEntry)
_data:000001ed401635e8()Length=1165,String="{\rtf\ansi{\fonttbl{\f0NSimSun;}}{\colortbl;\red0\green0\blue255;\red0\green0\blue0;\red0\green128\blue0;}\f0\fs19\cf1\cb0\highlight0var\cf2hostname=();\[]hostaddrs=(hostname);\par\par\cf1var\cf2localIPList=\cf1new\cf2ListIPAddress();\par\cf1for\cf2(\cf1int\cf2i=0;;i++)\par\{\par\cf1if\cf2(==hostaddrs[i].AddressFamily)\par\{\par\cf1if\cf2(hostaddrs[i].ToString().Equals(_localIP))\par\{\(0,hostaddrs[i]);\cf3//\uc1\u20248?\uc1\u20808?\uc1\u20351?\uc1\u29992?\uc1\u19978?\uc1\u27425?\uc1\u-28706?\uc1\u25509?IP\cf2\par\}\par\cf1else\cf2\par\{\(hostaddrs[i]);\par\}\par\}\par\}}"
_autoConvert:true()
_aspect:0x1()
_index:0x0()
increasedepth

简单整理了下大概是这样的代码。

varhostname=();
[]hostaddrs=(hostname);

varlocalIPList=newListIPAddress();
for(inti=0;;i++)
{
if(==hostaddrs[i].AddressFamily)
{
if(hostaddrs[i].ToString().Equals(_localIP))
{
(0,hostaddrs[i]);//优先使用上次连接IP
}
else
{
(hostaddrs[i]);
}
}
}

三:总结

Ctrl+C复制这段代码的时候,Resharper插件介入,然后在处理富文本时出问题了,不知道大家可踩过类似的坑,算是给后来人一点定位经验吧。

文章版权声明:除非注明,否则均为纵投光影网原创文章,转载或复制请以超链接形式并注明出处。

上一个 锐取信息:如何保养和清洗录播镜头

下一个 「图文+视频」C4D野教程:实景照片合成动画制作案例