我试图在Java应用程序中找到崩溃的原因。它实际上是JVM的崩溃,是由通过JNI调用本机库引起的。
以下是我在生成的hs_err_pidxxxx.log
中可以看到的内容:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_INT_DIVIDE_BY_ZERO (0xc0000094) at pc=0x4fa19409, pid=1456, tid=4068
#
# JRE version: 6.0_30-b12
# Java VM: Java HotSpot(TM) Client VM (20.5-b03 mixed mode windows-x86 )
# Problematic frame:
# C [JCustomOpc.dll+0x9409]
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
--------------- T H R E A D ---------------
Current thread (0x4ab1c400): JavaThread "opc_service" daemon [_thread_in_native, id=4068, stack(0x4f200000,0x4f250000)]
siginfo: ExceptionCode=0xc0000094
Registers:
EAX=0x00000000, EBX=0x00000000, ECX=0x4f24f958, EDX=0x80000000
ESP=0x4f24f93c, EBP=0x4f24f940, ESI=0x4f24f9a4, EDI=0x52d396f4
EIP=0x4fa19409, EFLAGS=0x00010286
Top of Stack: (sp=0x4f24f93c)
0x4f24f93c: 4f24f98a 4f24f970 4fa1968a 52220000
0x4f24f94c: 5042f418 4f24f9a4 4b1ba6e8 00000004
0x4f24f95c: 52d396f4 4ab1c528 4f24f9a8 4f24f9a6
0x4f24f96c: 4f24f9a4 4f24f98c 4fa197cc 4f24f98a
0x4f24f97c: 52220000 5042f418 00000036 4f24f9a8
0x4f24f98c: 4ab33d1c 4fa6388f 52220000 5042f418
0x4f24f99c: 4ab1c528 4ab33d24 00000008 6d92f61f
0x4f24f9ac: 4ab1c528 4ab33d1c 00000022 0000000a
Instructions: (pc=0x4fa19409)
0x4fa193e9: db 89 c1 dd 45 08 d8 8b 88 21 a7 4f 83 ec 08 df
0x4fa193f9: 3c 24 9b 58 5a 09 d2 79 11 f7 da f7 d8 83 da 00
0x4fa19409: f7 b3 8c 21 a7 4f f7 d8 eb 06 f7 b3 8c 21 a7 4f
0x4fa19419: 05 5a 95 0a 00 89 11 89 41 04 5b 5d c2 08 00 55
Register to memory mapping:
EAX=0x00000000 is an unknown value
EBX=0x00000000 is an unknown value
ECX=0x4f24f958 is pointing into the stack for thread: 0x4ab1c400
EDX=0x80000000 is an unknown value
ESP=0x4f24f93c is pointing into the stack for thread: 0x4ab1c400
EBP=0x4f24f940 is pointing into the stack for thread: 0x4ab1c400
ESI=0x4f24f9a4 is pointing into the stack for thread: 0x4ab1c400
EDI=0x52d396f4 is an unknown value
Stack: [0x4f200000,0x4f250000], sp=0x4f24f93c, free space=318k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [JCustomOpc.dll+0x9409]
C [JCustomOpc.dll+0x968a]
C [JCustomOpc.dll+0x97cc]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
J javafish.clients.opc.JOpc.getDownloadGroupNative()Ljavafish/clients/opc/component/OpcGroup;
J fr.def.iss.vd2.mod_opc_service.JEasyFacade.getPossiblyChangedGroups()Ljava/util/Collection;
J fr.def.iss.vd2.mod_opc_service.OpcServiceImpl.updateState()V
j fr.def.iss.vd2.mod_opc_service.OpcServiceImpl.access$3000(Lfr/def/iss/vd2/mod_opc_service/OpcServiceImpl;)V+1
j fr.def.iss.vd2.mod_opc_service.OpcServiceImpl$5.handlOpcEvents()V+103
j fr.def.iss.vd2.mod_opc_service.OpcServiceImpl$Worker.run()V+156
j java.lang.Thread.run()V+11
v ~StubRoutines::call_stub
因此,它表明JCustomOpc.dll
中有一个除零。 JCustomOpc.dll
是第三方库,用Delphi编写,由我们的团队用Borland Delphi pro 7.0编译。我现在要做的是找到Delphi源代码中的这个除零的位置。
我主要是一名Java程序员,我不习惯调试本机代码。所以,我跟着tutorial on JVM crash analysis。
我按照说明操作:
> dumpbin /headers JCustomOpc.dll
...
OPTIONAL HEADER VALUES
10B magic #
2.25 linker version
60800 size of code
12400 size of initialized data
0 size of uninitialized data
616A8 RVA of entry point
1000 base of code
62000 base of data
400000 image base
...
因此,图像库是400000,根据教程我希望除法指令位于偏移409409。
让我们用反汇编来看一下:
> dumpbin /exports /disasm JCustomOpc.dll
...
004093E0: C3 ret
004093E1: 8D 40 00 lea eax,[eax]
004093E4: 55 push ebp
004093E5: 8B EC mov ebp,esp
004093E7: 53 push ebx
004093E8: 31 DB xor ebx,ebx
004093EA: 89 C1 mov ecx,eax
004093EC: DD 45 08 fld qword ptr [ebp+8]
004093EF: D8 8B 88 21 46 00 fmul dword ptr [ebx+00462188h]
004093F5: 83 EC 08 sub esp,8
004093F8: DF 3C 24 fistp qword ptr [esp]
004093FB: 9B wait
004093FC: 58 pop eax
004093FD: 5A pop edx
004093FE: 09 D2 or edx,edx
00409400: 79 11 jns 00409413
00409402: F7 DA neg edx
00409404: F7 D8 neg eax
00409406: 83 DA 00 sbb edx,0
00409409: F7 B3 8C 21 46 00 div eax,dword ptr [ebx+0046218Ch]
0040940F: F7 D8 neg eax
00409411: EB 06 jmp 00409419
00409413: F7 B3 8C 21 46 00 div eax,dword ptr [ebx+0046218Ch]
00409419: 05 5A 95 0A 00 add eax,0A955Ah
0040941E: 89 11 mov dword ptr [ecx],edx
00409420: 89 41 04 mov dword ptr [ecx+4],eax
00409423: 5B pop ebx
00409424: 5D pop ebp
00409425: C2 08 00 ret 8
...
409409确实是一个div指令。
我对汇编知之甚少,但这段代码似乎操纵浮点值并进行整数除法。我搜索了Delphi的源代码,但没有发现任何类似于算术运算的内容。
我很确定有一种可靠的方法可以找到哪个Delphi函数匹配这个程序集,但我不知道该怎么做。我有这个dll的完整源代码,我完全可以控制如何编译它。
delphi源代码(.pas)生成编译文件(.dcu),也许这些文件可以帮助解决我的问题,但我不知道如何处理它们。链接器有一个选项可用于创建映射文件。它确实创建了一个JCustomOpc.map文件,该文件是一个包含大量符号和偏移量的文本文件。但偏移量与9404附近的任何东西都不匹配。
我必须补充一点,这次崩溃发生在我们客户的机器上,但不能在我们的机器上重现。此外,实际上不可能在客户的机器上进行一些测试,因此我只能使用hs_err_pid file
工作。
我现在可以做些什么来找到有问题的源代码行?
答案 0 :(得分:8)
您需要构建DLL并输出详细的地图文件。链接器选项中的设置控制映射文件输出。该映射文件将为您提供DLL中每个函数的起始地址,并且应该很容易从那里开始工作。
另一个选择,如果您可以在安装了Delphi的开发机器上重现错误,只需使用Delphi调试器调试DLL。这会在引发异常时中断,并且您将获得的信息甚至不仅仅是麻烦函数的标识。
答案 1 :(得分:4)
您可以在delphi中加载dll项目,然后在 Run / Parameters 菜单中将java appli定义为主机。然后按F9。当bug触发时,delphi IDE应突出显示有问题的源代码行。不要忘记在调试模式下进行此操作,并且可能会检查“使用调试DCU”,以防它在RTL中发生。
答案 2 :(得分:2)
编译器优化会让您在将程序集直接映射到Delphi中的Pascal源时遇到一些麻烦,因为它不太可能存在“真正的”简单关联。但是,如果dll是内部生成的,您可以让源团队创建一个内置调试信息/符号。这应该允许您附加调试器并提供更多人类可接受的诊断。但它仍然可能是一个艰难的障碍。