使用.NET应用程序时出现本地访问冲突

3
我们遇到了一个与.NET应用程序相关的问题。它在关闭时随机产生本机访问冲突(不是.NET异常)。
  • Windows Server 2012 x64(在刀片系统上运行的虚拟机)
  • .NET 4.0框架(已安装4.5)
  • 我们的应用程序是.NET x86应用程序(不是AnyCPU;不是ASP.NET)
  • 第三方本地模块使用SQL CE 3.5等

来自Windows事件日志的错误报告看起来像这样(省略了应用程序和模块名称,我将条目从德语翻译成英语):

Exceptioncode: 0xc0000005
Offset: 0x00006a9e
Process ID: 0xfe8
...

我发现0xc0000005是一种访问冲突错误,也可能由.NET NullReferenceException引起。在Windows错误报告对话框打开时,我们已经使用ma标志创建了完整的内存转储文件,并且已经找到了ProcDump工具。
当我打开这个转储文件时,除了.exe文件本身之外,我并没有找到任何.NET模块被加载。我不是一个本地编程专家,只知道一些WinDbg的基础知识。
因此,我所做的是:
  1. Open the dump with WinDbg
  2. Setup the symbol path with

    .sympath SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols;C:\PathToMatchingPDBs\ForTheProgram
    
  3. Reload all symbols with

    .reload /d /f
    
    Reloading current modules
    ...*** ERROR: Symbol file could not be found.  Defaulted to export symbols for sysfer.dll - 
    ...
    
  4. ~* k outputs

    .  0  Id: 1560.19a4 Suspend: 0 Teb: ff20e000 Unfrozen
    ChildEBP RetAddr  
    0058f0e8 7744c752 ntdll!ZwWaitForMultipleObjects+0xc
    0058f26c 76d256c0 KERNELBASE!WaitForMultipleObjectsEx+0x10b
    0058f2e0 76d2586a kernel32!WerpReportFaultInternal+0x1c4
    0058f2f4 76cf7828 kernel32!WerpReportFault+0x6d
    0058f300 774d07c4 kernel32!BasepReportFault+0x19
    0058f39c 7762c11c KERNELBASE!UnhandledExceptionFilter+0x1f1
    0058f3a4 775f3334 ntdll!__RtlUserThreadStart+0x57
    0058f3b8 77691fd7 ntdll!_EH4_CallFilterFunc+0x12
    0058f3e0 77693612 ntdll!_except_handler4_common+0x8e
    0058f400 775f30f1 ntdll!_except_handler4+0x20
    0058f424 775f30c3 ntdll!ExecuteHandler2+0x26
    0058f4ec 775f2f2b ntdll!ExecuteHandler+0x24
    0058f4ec 00406a9e ntdll!KiUserExceptionDispatcher+0xf
    0058f844 7760ac69 MyProgram!COM+_Entry_Point <PERF> (MyProgram+0x6a9e)
    0058f888 7760ac3c ntdll!__RtlUserThreadStart+0x72
    0058f8a0 00000000 ntdll!_RtlUserThreadStart+0x1
    
  5. The next part I am pretty unsure if it is correct what I did. I read somewhere that you can take the address of the column RetAddr and pass it to uf to see what the function at this point is doing. Is this correct? So I am expecting the following command to display the disassembly from the function at MyProgram+0x6a9e, see call stack above.

    >uf 0x00406a9e
    MyProgram!COM+_Entry_Point <PERF> (MyProgram +0x6a9e):
    00406a9e ff2500204000    jmp     dword ptr [MyProgram!COM+_Entry_Point <PERF> (MyProgram+0x2000) (00402000)]
    
    mscoree!_CorExeMain_Exported:
    707f4ddb 8bff            mov     edi,edi
    707f4ddd 56              push    esi
    707f4dde e80c2f0000      call    mscoree!ShellShim__CorExeMain (707f7cef)
    707f4de3 6a00            push    0
    707f4de5 8bf0            mov     esi,eax
    707f4de7 e84bc4ffff      call    mscoree!GetShimImpl (707f1237)
    707f4dec e93a800000      jmp     mscoree!_CorExeMain_Exported+0x11 (707fce2b)
    
    mscoree!_CorExeMain_Exported+0x11:
    707fce2b 83f801          cmp     eax,1
    707fce2e 750a            jne     mscoree!_CorExeMain_Exported+0x20 (707fce3a)
    
    mscoree!_CorExeMain_Exported+0x16:
    707fce30 6858378370      push    offset mscoree!g_wszShimImplDllPath (70833758)
    707fce35 e826350100      call    mscoree!DisplayMessageBoxForNoShimImpl (70810360)
    
    mscoree!_CorExeMain_Exported+0x20:
    707fce3a 8bc6            mov     eax,esi
    707fce3c 5e              pop     esi
    707fce3d c3              ret
    

    I am not sure how to interpret this. I looks like this is really the entry point of the application because of the CoreExeMain Exports? So not so much useful informations here, or? Only that the problem occurs on native code level; as far as I interpreted it.

仅出于完整性考虑:
.loadby sos clr
Unable to find module ‘clr’
!pe
No export pe found.

当我使用Visual Studio打开转储文件时,也写道没有发现异常。
这就是我目前的情况。也许有人可以帮我解释这些数据,或给我一些提示,还能尝试什么?
我的问题:
1. Reflection.Emit生成的IL代码会导致这种错误吗? 2. 我可以做什么来进一步分析问题/找到原因? 3. COM+_Entry_Point是什么意思?这是我的应用程序的主入口点还是与某些外部组件有关?
编辑: @Jochen Kalmbach
.ecxr;kP

Minidump doesn't have an exception context
Unable to get exception context, HRESULT 0x80004002
ChildEBP RetAddr  
0058f0e8 7744c752 ntdll!ZwWaitForMultipleObjects+0xc
0058f26c 76d256c0 KERNELBASE!WaitForMultipleObjectsEx+0x10b
0058f2e0 76d2586a kernel32!WerpReportFaultInternal+0x1c4
0058f2f4 76cf7828 kernel32!WerpReportFault+0x6d
0058f300 774d07c4 kernel32!BasepReportFault+0x19
0058f39c 7762c11c KERNELBASE!UnhandledExceptionFilter+0x1f1
0058f3a4 775f3334 ntdll!__RtlUserThreadStart+0x57
0058f3b8 77691fd7 ntdll!_EH4_CallFilterFunc+0x12
0058f3e0 77693612 ntdll!_except_handler4_common+0x8e
0058f400 775f30f1 ntdll!_except_handler4+0x20
0058f424 775f30c3 ntdll!ExecuteHandler2+0x26
0058f4ec 775f2f2b ntdll!ExecuteHandler+0x24
0058f4ec 00406a9e ntdll!KiUserExceptionDispatcher+0xf
0058f844 7760ac69 MyProgram!COM+_Entry_Point <PERF> 
0058f888 7760ac3c ntdll!__RtlUserThreadStart+0x72
0058f8a0 00000000 ntdll!_RtlUserThreadStart+0x1b

为什么要使用minidump?
@Thomas W.
lm

start    end        module name
00400000 0040c000   MyProgram   (private pdb symbols)  c:\...\release\MyProgram.pdb
707f0000 7083a000   mscoree    (pdb symbols)          c:\symbols\mscoree.pdb\7608F9FF3C954E429A27D833164E4BEE2\mscoree.pdb
74b70000 74bdd000   sysfer     (export symbols)       sysfer.dll
76cc0000 76df0000   kernel32   (pdb symbols)          c:\symbols\wkernel32.pdb\CFACA818EC334A5EAD707CD86C847B4A1\wkernel32.pdb
77440000 774e6000   KERNELBASE   (pdb symbols)          c:\symbols\wkernelbase.pdb\CAE0056433064B78937D9022B20FA1102\wkernelbase.pdb
775b0000 77707000   ntdll      (private pdb symbols)  c:\symbols\wntdll.pdb\EC83D8DF555946E0B630133EBD9792662\wntdll.pdb

编辑2

额外信息:

!heap
Index   Address  Name      Debugging options enabled
  1:   009c0000                
  2:   006b0000                
  3:   00770000                
  4:   00d00000


!heap -a
Index   Address  Name      Debugging options enabled
  1:   009c0000 
    Segment at 009c0000 to 00abf000 (0000b000 bytes committed)
  2:   006b0000 
    Segment at 006b0000 to 006bf000 (00003000 bytes committed)
    Segment at 00810000 to 0090f000 (00042000 bytes committed)
  3:   00770000 
    Segment at 00770000 to 0077f000 (00003000 bytes committed)
  4:   00d00000 
    Segment at 00d00000 to 00d3f000 (00001000 bytes committed)


!heap -l
Searching the memory for potential unreachable busy blocks.
Heap 009c0000
Heap 006b0000
Heap 00770000
Heap 00d00000
Scanning VM ...
Scanning references from 204 busy blocks (0 MBytes) ...
Entry     User      Heap      Segment       Size  PrevSize  Unused    Flags
-----------------------------------------------------------------------------
006b07c0  006b07c8  006b0000  006b0000        88       220         8  busy 
007707c0  007707c8  00770000  00770000        88       220         8  busy 
2 potential unreachable blocks were detected.

那么这意味着什么?

如果你遇到异常,你应该执行.ecxr;kP和/或!analyze -v - Jochen Kalmbach
1
你现在只是盲目地摸索。请注意,MyProgram+0x6a9e注释标明了崩溃发生的位置。+0x6a9e偏移量与程序中任何已知位置都相距甚远。对于操作系统函数,你可以得到很小的偏移量,并且可以从Microsoft符号服务器获得良好的PDB文件。但是你自己程序的PDB文件却没有,调试器无法提供有用的信息。重要的是要组织好它,你的构建服务器需要保留PDB文件,以便在稍后获取minidump时使用。 - Hans Passant
我正在使用自己的PDB文件。在不加载它们的情况下查看调用堆栈"MyProgram!COM+_Entry_Point",其中写有MyProgram!+0x6a9e。 - Daniel Bişar
你是否能够使用我建议的设置或者 WER LocalDumps 生成新的转储文件?我认为我们在这里看错了东西。 - Thomas Weller
直到现在,这个错误再也没有发生过... - Daniel Bişar
1
前段时间,问题出在 sysfer.dll 上,它属于赛门铁克防火墙/杀毒软件。禁用该程序,一切都会好起来。 - Daniel Bişar
2个回答

5

这不算完整的答案,但是太长了不能放在评论里。按照出现的顺序:

"when the windows error reporting dialog was open."

当 Windows 错误报告对话框已经打开时,采集崩溃转储并不起作用。 相反,应直接使用 ProcDump 运行应用程序:

procdump -e -ma -x c:\MyDump.dmp C:\PathTo\MyApplication.exe Arguments

ProcDump会在异常发生时生成崩溃转储文件。

如果这对您不起作用,可以让Windows错误报告将转储文件保存到磁盘上。请参见收集用户模式转储文件以获取相应的注册表设置。确保将DumpType设置为2(针对.NET)。

"我找不到任何已加载的.NET模块"

您是如何检查.NET模块的?您可以使用lm列出所有模块,并查看是否存在您所知道的一些DLL文件。

"使用...设置符号路径"

没问题。如果您不想输入所有HTTP内容,也可以使用以下命令:

.symfix C:\Symbols
.sympath+ C:\PathToMatchingPDBs\ForTheProgram

"~* k outputs KERNELBASE!UnhandledExceptionFilter+0x1f1"

可能这就是为什么你没有得到关于原始异常的更多信息的原因:异常只是被Windows错误报告处理了。

"Unable to find module ‘clr’"

您确定这是一个.NET 4应用程序吗?尝试使用.loadby sos mscorwks查看它是否为.NET 2。如果这也无法帮助,请检查lm m ms*以获取其他类似.NET的程序集。


WER能够允许本地崩溃转储(正如您在第二个主题中所说)。 您只需要在注册表中进行配置即可。 这是我长期以来看到的最好的功能,可以在没有特殊进程运行的情况下进行崩溃转储! 它适用于Vista SP1及更高版本...这是我推荐的获取崩溃转储的方法:http://msdn.microsoft.com/en-us/library/windows/desktop/bb787181 - Jochen Kalmbach
lm m * 列出了 c:\symbols\mscoree.pdb\7608F9FF3C954E429A27D833164E4BEE2\mscoree.pdb。是的,我确定这是一个 .NET 4 应用程序。我真的很想知道转储中似乎没有与 .NET 相关的信息。似乎我不能使用用户转储,因为 MSDN 表示:“进行自定义崩溃报告的应用程序(包括 .NET 应用程序)不受此功能的支持。” - Daniel Bişar
自从 .NET 4,似乎你也可以使用注册表键。我会尝试一下。 - Daniel Bişar

0

在windbg中加载转储文件并执行!analyze -v,如果符号正确,则应该得到一个准确的结果,包括源代码行。

将返回地址提供给uf是没有用的。

uf执行控制流程,如果返回地址指向随机dll中的某个随机函数的jmpuf将简单地转储随机函数

return address is pushed into the stack by the callee

如果你执行 ub <返回地址>,你可以找到 callee

应该是类似于 call blah <0x.......>

现在对 callee 执行 uf <0x........>,以查看调用的操作
很可能看起来像是执行未处理的异常


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接