博客迁移

http://junchu25.cnblogs.com/

Leave a comment

Microsoft Network Monitor capture local

由于Microsoft Network Monitor是基于硬件层面监控底层的网卡数据,所以对于本地的连接(127.0.0.1)并不经过网卡它无法capture。解决方法是为本地连接的IP加一条到网关的路由,这样连本机的IP会先到网关,再由网关转发数据到本机。
添加路由命令:
route add mask 255.255.255.255
删除路由命令:
route delete

Posted in Debugging, Windows | Leave a comment

IIS6 System.OutOfMemoryException

周五有客户反馈自己的一个Web应用每隔3个小时左右会停止,同时在Windows日志中提示System.OutOfMemoryException。Web前端的环境是Windows Server 2003 x86、IIS6。在IIS重启后,大约2个小时左右用procexp导出w3p进程的Full Dump,用DebugDiag分析是否存在Memory Leak,发现没有异常。继续用procexp查看CLR部分占用的内存依然没有异常(由于客户的Web应用没有负载均衡,只做了大量缓存)。继续观察w3p的内存变化情况,在一段时间内会突然增长200、300MB,而当出现crash后,内存大致被用尽,应用程序池来不及回收。许多人提到ASP.NET的回收机制,在Web.config中的processModel的memoryLimit属性,默认为60,对于x86的操作系统单个进程只有4G内存,2G由操作系统使用、2G由应用程序使用,而实际应用程序真正可以支配的应该在1.8G左右。对于4G x 60 = 2.4G,当w3p进程使用内存超过2.4G时才回收,而实际上当超过2G就已经将进程的内存用完。由于客户Web应用的内存会突然有一个增长阀值,于是设置将应用程序池在达到1G时就自动回收,以避免这个阀值导致w3p来不及自动回收,并设置每天不同时间段的w3p自动回收策略。最后建议客户将操作系统升级至x64。

Posted in .NET, Debugging, Windows | Leave a comment

x64、x32下JIT不同的一个优化处理

今天开发人员反馈使用一个类库的函数导致程序卡住,而唯一的区别就是新的程序在x64的机器上运行。于是编写了一个大致模拟调用的程序到x64环境运行,场景再现。出错函数中有一个不断向上遍历获取StackFrame的操作,这是导致死循环的原因。但是这个循环的跳出条件就是向上遍历直到获取类库的函数StackFrame,理论上来说这个断言是绝对正确的。在x86的机器上运行不存在任何问题。
于是用WinDbg调试,发现程序确实停留在那个while的循环,也就是说没有找到类库函数入口的StackFrame。加载sos,切换到运行线程,!clrstack,发现类库的入库函数没有出现在callstack中,而是直接执行了里面的一个函数。定位到该函数的retaddr进入到了更外层,用!u查看当前汇编,原来在x64环境下的JIT将这个函数调用inline,所以没有产生入口函数的StackFrame。被内敛的关联代码大致如下:
public GenerateResult Generate(IQueryableObject queryableObject)
{
……
return GenerateQueryableAttributes(queryableObject);
}

private GenerateResult GenerateQueryableAttributes(IQueryableObject queryableObject)
{
……
}

Generate函数被inline,导致GenerateQueryableAttributes内部的一个类型在获取StackFrame时无法找到Generate的StackFrame。
解决方法为Generate函数添加MethodImplAttribute,将它设置为NoInlining。

Posted in .NET, Debugging | Leave a comment

[MethodImpl(MethodImplOptions.Synchronized)](二)

继续关于之前x86 Debug版本在x64下运行deadlock的话题。函数上添加[MethodImpl(MethodImplOptions.Synchronized)]并编译后,il会被添加cil managed synchronized。JIT在看到函数关联的il包含cil managed synchronized自动添加Monitor相关的Enter和Exit调用。这一点可以通过WinDeg查看,先编写一个简单的测试程序:
class TestClass
{
[MethodImpl(MethodImplOptions.Synchronized)]
public void Func()
{
Console.ReadKey();
}
}

class Program
{
static void Main(String[] args)
{
TestClass testClass = new TestClass();

testClass.Func();

Console.ReadKey();
}
}
用WinDbg启动程序,由于在函数没有被调用前并不会被JIT,这样也就看不到cil managed synchronized的本地代码。需要让函数先执行让JIT编译。加载CLR的调试扩展sos.dll。用~0 s切换到主线程,输入!dumpstackobjects查看TestClass的地址,!do查看TestClass的定义。!dumpmt -md查看TestClass的Method Table,找到对应的Func函数,可以看到它已经被JIT。!u 反汇编查看本地代码:
Normal JIT generated code
ConsoleApplication8.TestClass.Func()
Begin 00980118, size 38
00980118 55 push ebp
00980119 8bec mov ebp,esp
0098011b 83ec10 sub esp,10h
0098011e 894df0 mov dword ptr [ebp-10h],ecx
00980121 833db031820000 cmp dword ptr ds:[8231B0h],0
00980128 7405 je 0098012f
0098012a e892f1d362 call mscorwks!JIT_DbgIsJustMyCode (636bf2c1)
0098012f 8b4df0 mov ecx,dword ptr [ebp-10h]
00980132 e89e2aac62 call mscorwks!JIT_MonEnterWorker (63442bd5)
00980137 90 nop
00980138 8d4df4 lea ecx,[ebp-0Ch]
*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v2.0.50727_32\mscorlib\f6deb187f24bb3185841092b89fbfdbb\mscorlib.ni.dll
0098013b e82c398a60 call mscorlib_ni+0x6d3a6c (61223a6c) (System.Console.ReadKey(), mdToken: 060007a3)
00980140 90 nop
00980141 90 nop
00980142 eb00 jmp 00980144
00980144 8b4df0 mov ecx,dword ptr [ebp-10h]
00980147 e8032dac62 call mscorwks!JIT_MonExitWorker (63442e4f)
0098014c 8be5 mov esp,ebp
0098014e 5d pop ebp
0098014f c3 ret
确实和使用Monitor的方式相同,再比对使用lock的反编译:
Normal JIT generated code
ConsoleApplication8.TestClass.Func()
Begin 00970118, size 84
00970118 55 push ebp
00970119 8bec mov ebp,esp
0097011b 57 push edi
0097011c 56 push esi
0097011d 53 push ebx
0097011e 83ec28 sub esp,28h
00970121 8bf1 mov esi,ecx
00970123 8d7dcc lea edi,[ebp-34h]
00970126 b909000000 mov ecx,9
0097012b 33c0 xor eax,eax
0097012d f3ab rep stos dword ptr es:[edi]
0097012f 8bce mov ecx,esi
00970131 33c0 xor eax,eax
00970133 8945e8 mov dword ptr [ebp-18h],eax
00970136 894dd0 mov dword ptr [ebp-30h],ecx
00970139 833db0311c0000 cmp dword ptr ds:[1C31B0h],0
00970140 7405 je 00970147
00970142 e87af1d462 call mscorwks!JIT_DbgIsJustMyCode (636bf2c1)
00970147 33d2 xor edx,edx
00970149 8955cc mov dword ptr [ebp-34h],edx
0097014c 90 nop
0097014d 8b45d0 mov eax,dword ptr [ebp-30h]
00970150 8945cc mov dword ptr [ebp-34h],eax
00970153 8b4dcc mov ecx,dword ptr [ebp-34h]
00970156 e87a2aad62 call mscorwks!JIT_MonEnterWorker (63442bd5)
0097015b 90 nop
0097015c 90 nop
0097015d 8d4dd4 lea ecx,[ebp-2Ch]
00970160 e807398b60 call mscorlib_ni+0x6d3a6c (61223a6c) (System.Console.ReadKey(), mdToken: 060007a3)
00970165 90 nop
00970166 90 nop
00970167 90 nop
00970168 c745e400000000 mov dword ptr [ebp-1Ch],0
0097016f c745e8fc000000 mov dword ptr [ebp-18h],0FCh
00970176 6893019700 push 970193h
0097017b eb00 jmp 0097017d
0097017d 8b4dcc mov ecx,dword ptr [ebp-34h]
00970180 e8ca2cad62 call mscorwks!JIT_MonExitWorker (63442e4f)
00970185 90 nop
00970186 58 pop eax
00970187 ffe0 jmp eax
00970189 90 nop
0097018a 90 nop
0097018b 8d65f4 lea esp,[ebp-0Ch]
0097018e 5b pop ebx
0097018f 5e pop esi
00970190 5f pop edi
00970191 5d pop ebp
00970192 c3 ret
00970193 c745e800000000 mov dword ptr [ebp-18h],0
0097019a ebed jmp 00970189

两者加锁原理相同,但是在x64上前者在一个线程抛出了System.Threading.SynchronizationLockException,用!SyncBlk命令查看发现lock的线程和抛出异常的线程不同,最有可能的是两者在mscorwks!JIT_MonEnterWorker时锁住的RuntimeType不同,但在离开时却相同,产生了在不同步的代码块中调用同步代码。

Posted in .NET, Debugging | Leave a comment