jvm crash的崩溃日志详细分析及注意点
生成
1.生成error文件的路径:你可以通过参数设置-XX:ErrorFile=/path/hs_error%p.log,默认是在Java运行的当前目录[default:./hs_err_pid%p.log]
2.参数-XX:OnError 可以在crash退出的时候执行命令,格式是-XX:OnError=“string”,
例如:
//-XX:OnError="pmap%p"//showmemorymap //-XX:OnError="gcore%p;dbx-%p"//dumpcoreandlaunchdebugger
在Linux中系统会fork出一个子进程去执行shell的命令,因为是用fork可能会内存不够的情况,注意修改你的/proc/sys/vm/overcommit_memory参数,不清楚为什么这里不使用vfork
3.-XX:+ShowMessageBoxOnError参数,当jvmcrash的时候在linux里会启动gdb去分析和调式,适合在测试环境中使用。
什么情况下不会生成error文件
linux内核在发生OOM的时候会强制kill一些进程,可以在/var/logs/messages中查找
Errorcrash文件的几个重要部分
a. 错误信息概要
#AfatalerrorhasbeendetectedbytheJavaRuntimeEnvironment: # #SIGSEGV(0xb)atpc=0x0000000000043566,pid=32046,tid=1121192256 # #JREversion:6.0_17-b04 #JavaVM:JavaHotSpot(TM)64-BitServerVM(14.3-b01mixedmodelinux-amd64) #Problematicframe: #C0x0000000000043566 # #Ifyouwouldliketosubmitabugreport,pleasevisit: #http://java.sun.com/webapps/bugreport/crash.jsp #ThecrashhappenedoutsidetheJavaVirtualMachineinnativecode. #Seeproblematicframeforwheretoreportthebug.
SIGSEGV错误的信号类型
pc就是IP/PC寄存器值也就是执行指令的代码地址
pid就是进程id
#Problematicframe:
#V [libjvm.so+0x593045]
就是导致问题的动态链接库函数的地址
pc和+0x593045指的是同一个地址,只是一个是动态的偏移地址,一个是运行的虚拟地址
b.信号信息
Java中在linux中注册的信号处理函数,中间有2个参数info,ucvoid
staticvoidcrash_handler(intsig,siginfo_t*info,void*ucVoid){ //unmaskcurrentsignal sigset_tnewset; sigemptyset(&newset); sigaddset(&newset,sig); sigprocmask(SIG_UNBLOCK,&newset,NULL); VMErrorerr(NULL,sig,NULL,info,ucVoid); err.report_and_die(); }
在crashreport中的信号错误提示
siginfo:si_signo=SIGSEGV:si_errno=0,si_code=1(SEGV_MAPERR),si_addr=0x0000000000043566
信号的详细信息和si_addr出错误的内存,都保存在siginfo_t的结构体中,也就是信号注册函数crash_handler里的参数info,内核会保存导致错误的内存地址在用户空间的信号结构体中siginfo_t,这样在进程在注册的信号处理函数中可以取得导致错误的地址。
c.寄存器信息
Registers: RAX=0x00002aacb5ae5de2,RBX=0x00002aaaaf46aa48,RCX=0x0000000000000219,RDX=0x00002aaaaf46b920 RSP=0x0000000042d3f968,RBP=0x0000000042d3f9c8,RSI=0x0000000042d3f9e8,RDI=0x0000000045aef9b8 R8=0x0000000000000f80,R9=0x00002aaab3d30ce8,R10=0x00002aaaab138ea1,R11=0x00002b017ae65110 R12=0x0000000042d3f6f0,R13=0x00002aaaaf46aa48,R14=0x0000000042d3f9e8,R15=0x0000000045aef800 RIP=0x0000000000043566,EFL=0x0000000000010202,CSGSFS=0x0000000000000033,ERR=0x0000000000000014 TRAPNO=0x000000000000000e
寄存器的信息就保存在b部分的信号处理函数参数(ucontext_t*)usVoid中
在X86架构下:
voidos::print_context(outputStream*st,void*context){ if(context==NULL)return; ucontext_t*uc=(ucontext_t*)context; st->print_cr("Registers:"); #ifdefAMD64 st->print("RAX="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_RAX]); st->print(",RBX="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_RBX]); st->print(",RCX="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_RCX]); st->print(",RDX="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_RDX]); st->cr(); st->print("RSP="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_RSP]); st->print(",RBP="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_RBP]); st->print(",RSI="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_RSI]); st->print(",RDI="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_RDI]); st->cr(); st->print("R8="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_R8]); st->print(",R9="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_R9]); st->print(",R10="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_R10]); st->print(",R11="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_R11]); st->cr(); st->print("R12="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_R12]); st->print(",R13="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_R13]); st->print(",R14="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_R14]); st->print(",R15="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_R15]); st->cr(); st->print("RIP="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_RIP]); st->print(",EFL="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_EFL]); st->print(",CSGSFS="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_CSGSFS]); st->print(",ERR="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_ERR]); st->cr(); st->print("TRAPNO="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_TRAPNO]); #else st->print("EAX="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_EAX]); st->print(",EBX="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_EBX]); st->print(",ECX="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_ECX]); st->print(",EDX="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_EDX]); st->cr(); st->print("ESP="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_UESP]); st->print(",EBP="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_EBP]); st->print(",ESI="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_ESI]); st->print(",EDI="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_EDI]); st->cr(); st->print("EIP="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_EIP]); st->print(",CR2="INTPTR_FORMAT,uc->uc_mcontext.cr2); st->print(",EFLAGS="INTPTR_FORMAT,uc->uc_mcontext.gregs[REG_EFL]); #endif//AMD64 st->cr(); st->cr(); intptr_t*sp=(intptr_t*)os::Linux::ucontext_get_sp(uc); st->print_cr("TopofStack:(sp="PTR_FORMAT")",sp); print_hex_dump(st,(address)sp,(address)(sp+8*sizeof(intptr_t)),sizeof(intptr_t)); st->cr(); //Note:itmaybeunsafetoinspectmemorynearpc.Forexample,pcmay //pointtogarbageifentrypointinannmethodiscorrupted.Leave //thisattheend,andhopeforthebest. addresspc=os::Linux::ucontext_get_pc(uc); st->print_cr("Instructions:(pc="PTR_FORMAT")",pc); print_hex_dump(st,pc-16,pc+16,sizeof(char)); }
寄存器的信息在分析出错的时候是非常重要的
打印出执行附近的部分机器码
Instructions:(pc=0x00007f48f14ef51a) 0x00007f48f14ef4fa:9090554889e54881ec989f00004889bd 0x00007f48f14ef50a:f85fffff4889b5f05fffffb800000000 0x00007f48f14ef51a:c70001000000c6850060ffffffc9c390 0x00007f48f14ef52a:909090909090554889e553488d1d9400
在instruction部分中会打印出部分的机器码
格式是
地址:机器码
第一种使用udis库里带的udcli工具来反汇编
命令:
echo'9090554889e54881ec989f00004889bd'|udcli-intel-x-64-o0x00007f48f14ef4fa
显示出对应的汇编
第二种可以用
objectdump-d-Clibjvm.so>>jvmsodisass.dump
查找偏移地址 0x593045,就是当时的执行的汇编,然后结合上下文,源码推测出问题的语句。
d.寄存器对应的内存的值
RAX=0x0000000000000000isanunknownvalue RBX=0x000000041a07d1e8isanoop {method} -klass:{otherclass} RCX=0x0000000000000000isanunknownvalue RDX=0x0000000040111800isathread RSP=0x0000000041261b88ispointingintothestackforthread:0x0000000040111800 RBP=0x000000004126bb20ispointingintothestackforthread:0x0000000040111800 RSI=0x000000004126bb80ispointingintothestackforthread:0x0000000040111800 RDI=0x00000000401119d0isanunknownvalue R8=0x0000000040111c40isanunknownvalue R9=0x00007f48fcc8b550:in/usr/java/jdk1.6.0_30/jre/lib/amd64/server/libjvm.soat0x00007f48fc206000 R10=0x00007f48f8ca7d41isanInterpretercodelet methodentrypoint(kind=native)[0x00007f48f8ca7ae0,0x00007f48f8ca8320]2112bytes R11=0x00007f48fc98f270: in/usr/java/jdk1.6.0_30/jre/lib/amd64/server/libjvm.soat0x00007f48fc206000 R12=0x0000000000000000isanunknownvalue R13=0x000000041a07d1e8isanoop {method} -klass:{otherclass} R14=0x000000004126bb88ispointingintothestackforthread:0x0000000040111800 R15=0x0000000040111800isathread
jvm会通过寄存器的值对找对应的对象,也是一个比较好的参考
e.其他的信息
error里面还有一些线程信息,还有当时内存映像信息,这些都可以作为分析的部分参考
crash报告可以大概的反应出一个当时的情况,特别是在没有coredump的时候,是比较有助于帮助分析的,但如果有coredump的话,最终还是coredump能快速准确的发现问题原因。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持毛票票!