今天发现在开发的产品中出现了一个crash,表现形式是开启了多个xxbrowser程序后,退出导致了camel 程序的crash。
比较特别的是xxbrowser的dump机制竟然没有发挥作用。 由于这个问题能够比较简单的重复,所以用windbg attach之后看到如下。
从windgb给出的38行,看到是上图的加深的那一行,这儿呢,简单介绍一些背景,我们包装了log4cpp,然后这个包装类就是上述的clogshell ,而这个logshell又使用了单件模式构建,由于我曾经阅读过 《《c++设计新思维》》,这本书就讲到了单件的退出先后问题,所以我第一时间就认为是这个问题引起的,再仔细看看汇编代码。
所以我第一时间就怀疑这个m_logger 指针已经先delete了,然后又被后续调用者调用了logva,导致了crash。
但是非常郁闷的是用windbg 查看了m_logger 之后...
0:000> dt this
Local var @ 0x12fcb4 Type CLogShell*
0x655c3280
+0x000 m_logger : 0x027bce80 ILogger
0:000> !address 0x027bce80
ProcessParametrs 002a1f60 in range 002a0000 00351000
Environment 002a07f0 in range 002a0000 00351000
027b0000 : 027b0000 - 00010000
Type 00020000 MEM_PRIVATE
Protect 00000004 PAGE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsageHeap
Handle 027b0000
发现m_logger 竟然是好的,即它并没有在crash的时候被释放。
那么问题在哪儿呢?
这儿有个技巧了,一般来说当一个c++对象被delete之后,你再访问它的地址空间,一般来说不一定会出问题,这个是由于运行库可能是出于性能考虑啥的,没有马上回收这块内存,所以类似这种问题你看到的现场往往已经不是真正的事发现场,所以比较推荐的方法是开启appverify 然后仅仅选择heaps。
FAULTING_IP:
alilog!CLoggerImpl::Logva+90 [f:\src\alibrowser\camel\webim\source\log\alilog\source\logger.cpp @ 288]
05061d80 8b12 mov edx,dword ptr [edx]
EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 05061d80 (alilog!CLoggerImpl::Logva+0x00000090)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 085b2f78
Attempt to read from address 085b2f78
FAULTING_THREAD: 0000136c
PROCESS_NAME: Alibrowser.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%08lx
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - 0x%08lx
EXCEPTION_PARAMETER1: 00000000
EXCEPTION_PARAMETER2: 085b2f78
READ_ADDRESS: 085b2f78
FOLLOWUP_IP:
alilog!CLoggerImpl::Logva+90 [f:\src\alibrowser\camel\webim\source\log\alilog\source\logger.cpp @ 288]
05061d80 8b12 mov edx,dword ptr [edx]
NTGLOBALFLAG: 2000100
APPLICATION_VERIFIER_FLAGS: 80000001
BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ_SHUTDOWN
PRIMARY_PROBLEM_CLASS: INVALID_POINTER_READ_SHUTDOWN
DEFAULT_BUCKET_ID: INVALID_POINTER_READ_SHUTDOWN
LAST_CONTROL_TRANSFER: from 732520f5 to 05061d80
STACK_TEXT:
0012fc00 732520f5 00000000 7325c88c 0012fc48 alilog!CLoggerImpl::Logva+0x90 [f:\src\alibrowser\camel\webim\source\log\alilog\source\logger.cpp @ 288]
0012fc1c 732520ae 00000000 7325c88c 0012fc48 imentry!CLogShell::Logva+0x35 [f:\src\alibrowser\camel\webim\source\log\alilog\include\logger_shell.h @ 38]
0012fc34 732519db 7325f278 00000000 7325c88c imentry!CLogShell::Log+0x1e [f:\src\alibrowser\camel\webim\source\log\alilog\include\logger_shell.h @ 34]
0012fc6c 7325170b 561dea26 085c4fe0 0012fcb4 imentry!ImAppProxy::UnInit+0x11b [f:\src\alibrowser\camel\webim\source\imentry\imappproxy.cpp @ 155]
0012fc88 7325214f 085c4fe0 0012fcc0 732534f6 imentry!ImAppProxy::~ImAppProxy+0x4b [f:\src\alibrowser\camel\webim\source\imentry\imappproxy.cpp @ 63]
0012fc94 732534f6 00000001 561dea6e 0012fcc8 imentry!ImAppProxy::`scalar deleting destructor'+0xf
0012fcc0 73253316 0012fd0c 732565ec 73250000 imentry!CSingleton::DestroyInstance+0x66 [f:\src\alibrowser\camel\webim\include\singleton.h @ 50]
0012fcc8 732565ec 73250000 00000000 00000001 imentry!DllMain+0x16 [f:\src\alibrowser\camel\webim\source\imentry\imentry.cpp @ 31]
0012fd0c 732566a6 73250000 0012fd6c 6546c66d imentry!__DllMainCRTStartup+0x7a [f:\dd\vctools\crt_bld\self_x86\crt\src\crtdll.c @ 546]
0012fd18 6546c66d 73250000 00000000 00000001 imentry!_DllMainCRTStartup+0x1e [f:\dd\vctools\crt_bld\self_x86\crt\src\crtdll.c @ 510]
0012fd6c 67e2898c 73250000 00000000 00000001 verifier!AVrfpStandardDllEntryPointRoutine+0x99
0012fdb4 773289d8 73250000 00000000 00000001 vrfcore!VfCoreStandardDllEntryPointRoutine+0x128
0012fdd4 7732e104 67e28864 73250000 00000000 ntdll!LdrpCallInitRoutine+0x14
0012fe78 7732e19f 050f4c00 050f4bfc 00000001 ntdll!LdrShutdownProcess+0x1aa
0012fe8c 7717bbf6 00000000 77e8f3b0 ffffffff ntdll!RtlExitUserProcess+0x74
0012fea0 00fe7e9c 00000000 0012fef0 00fe8101 kernel32!ExitProcessStub+0x12
0012feac 00fe8100 00000000 564d3d0a fffffffe Alibrowser!__crtExitProcess+0x17 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 731]
0012fef0 00fe812a 00000000 00000000 00000000 Alibrowser!doexit+0x113 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 644]
0012ff04 00fe36f2 00000000 564d3c72 00000000 Alibrowser!exit+0x11 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0dat.c @ 412]
0012ff88 7716ed6c 7ffd7000 0012ffd4 773337f5 Alibrowser!__tmainCRTStartup+0x120 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 272]
0012ff94 773337f5 7ffd7000 6af3f144 00000000 kernel32!BaseThreadInitThunk+0xe
0012ffd4 773337c8 00fe374f 7ffd7000 00000000 ntdll!__RtlUserThreadStart+0x70
0012ffec 00000000 00fe374f 7ffd7000 00000000 ntdll!_RtlUserThreadStart+0x1b
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: alilog!CLoggerImpl::Logva+90
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: alilog
IMAGE_NAME: alilog.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 4e6eb3ab
STACK_COMMAND: ~0s ; kb
FAILURE_BUCKET_ID: INVALID_POINTER_READ_SHUTDOWN_c0000005_alilog.dll!CLoggerImpl::Logva
BUCKET_ID: APPLICATION_FAULT_INVALID_POINTER_READ_SHUTDOWN_alilog!CLoggerImpl::Logva+90
WATSON_STAGEONE_URL:
Followup: MachineOwner
---------
这次crash显示的具体场景不太一样了。
alilog!CLoggerImpl::Logva+0x90 [f:\src\alibrowser\camel\webim\source\log\alilog\source\logger.cpp @ 288]
上图高亮的那行,由于开启了了appverify 触发的是第一现场,所以直接怀疑m_instance 有问题。
0:000> dt this
Local var @ 0x12fbc8 Type CLoggerImpl*
0x085a6ff0
+0x000 __VFN_table : 0x050652b4
+0x004 m_instance : 0x085b2f78 log4cpp::Category
+0x008 m_async : 1
+0x00c m_priority : 700
0:000> !address 0x085b2f78
ProcessParametrs 0016a130 in range 0016a000 0016b000
Environment 00167880 in range 00167000 00169000
08550000 : 085b1000 - 00003000
Type 00020000 MEM_PRIVATE
State 00002000 MEM_RESERVE
Usage RegionUsageIsVAD
非常清晰的看到m_instance 确实已经被回收了。
再来看看直接导致crash的asm代码
0:000> u eip
alilog!CLoggerImpl::Logva+0x90 [f:\src\alibrowser\camel\webim\source\log\alilog\source\logger.cpp @ 288]:
05061d80 8b12 mov edx,dword ptr [edx]
edx=085b2f78
这下看到了,呵呵,确实是想对 这个m_instance deref导致了access违规。
而且两下验证确实也是证明了 edx=085b2f78 就是那个m_instance.
呵呵,其实m_instance 咋一个source code它还是一个引用,其实从asm直观的看到它其实仅仅是一个指针。
这下原因找到并且坐实了。
到了这儿,其实还有一个深层次问题,即如果这类问题无法很好的重复,且也没有实现打开appverify,那么如何诊断?
1, 从!analyze -v 来判断是否是raw ptr 这类问题。 最近这类问题很多,主要原因是错误的地方往往不是立刻crash。
2. 就事发点周围的相关模块查看他们的一些关键内存成员,这个就要具体问题具体看,但是至少你可以逐步排除缩小。