I am trying to find the cause of a crash in my Java application. It is actually a crash of the JVM, caused by a call to a native library through JNI.
Here is what I can see in the generated 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
So, it shows that there is a division by zero in JCustomOpc.dll
. JCustomOpc.dll
is a third party library, written in Delphi, and compiled by our team with Borland Delphi pro 7.0. What I am trying to do now, is to find where in the Delphi source code this division by zero happens.
I am mainly a Java programmer, and I am not comfortable debugging native code. So, I followed a tutorial on JVM crash analysis.
I followed the instructions :
> 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
...
So, the image base is 400000, and according to the tutorial I expect the division instruction to be at offset 409409.
Let's look at it with the disassembler :
> 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 is indeed a div instruction.
I know very few about assembly, but this code seems to manipulate a floating point value and make an integer division. I searched the Delphi source code, but found nothing that looks like an arithmetic operation.
I am pretty sure there is a reliable way of finding which Delphi function matches this piece of assembly, but I don't know how to do that. I have the whole source code of this dll, and I have full control on how to compile it.
The delphi sources (.pas) generate compiled files (.dcu), maybe these files could help for my problem, but I don't know what to do with them. There is an option of the linker, to create a map file. It does create a JCustomOpc.map file, which is a text file containing a lots of symbols and offsets. But the offsets don't match anything near 9404.
I must add that this crash happens on the machine of our customer, but is not reproductible on our machines. Also, it is not really possible to do some tests on the customer's machine, so I'm kind of stuck with working from the hs_err_pid file
only.
What can I do now in order to find where the problematic source line is ?
Here is the generated detailed map file.
You need to build the DLL and output a detailed map file. The settings in the linker options control map file output. That map file will give you the start address of every function in your DLL and it should be easy enough to work it out from there.
Another option, if you can reproduce the error on a dev machine with Delphi installed, is to simply debug the DLL with the Delphi debugger. This will break when the exception is raised and you will have even more information than just the identity of the troublesome function.
You can load your dll project in delphi, then in the Run/Parameters menu you define your java appli as host. Then you press F9. When the bug trigs then the delphi IDE should highlight the problematic source code line. Dont forget to make this in debug mode and maybe with "Use debug DCU" checked in case of it would happend in the RTL.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With