jvmcrash의 붕괴 로그 상세 분석 및 주의점

9568 단어 jvmcrash
생성
1. error 파일 생성 경로: 매개 변수 설정 - XX:ErrorFile=/path/hs_error%p.log, 기본적으로 Java에서 실행 중인 현재 디렉토리 [default:./hs_err_pid%p.log]
2. 매개 변수-XX:OnError는crash가 종료될 때 명령을 실행할 수 있습니다. 형식은 -XX:OnError="string"입니다. 은 명령의 집합일 수 있습니다. 세미콜론으로 구분자를 만들고'%p'로 현재 프로세스의 ID를 가져올 수 있습니다.
예:

// -XX:OnError="pmap %p"  // show memory map
// -XX:OnError="gcore %p; dbx - %p" // dump core and launch debugger
Linux에서 시스템은 fork에서 셸 명령을 실행하기 위해 하위 프로세스를 내보냅니다. fork로 메모리가 부족할 수 있기 때문에 /proc/sys/vm/overcommit_memory 파라미터를 수정하십시오. 왜 여기에서 vfork를 사용하지 않는지 잘 모르겠습니다.
3. -XX:+ShowMessageBoxOnError 매개 변수, jvmcrash 때 linux에서 gdb를 시작하여 분석 및 변조를 합니다. 테스트 환경에서 사용하기에 적합합니다.
오류 파일이 생성되지 않는 경우
linux 커널은 OOM이 발생할 때 프로세스를 강제로 kill합니다./var/logs/messages에서 찾을 수 있습니다.
Error crash 파일의 몇 가지 중요한 부분
a. 오류 정보 개요

# A fatal error has been detected by the Java Runtime Environment: 
# 
# SIGSEGV (0xb) at pc=0x0000000000043566, pid=32046, tid=1121192256 
# 
# JRE version: 6.0_17-b04 
# Java VM: Java HotSpot(TM) 64-Bit Server VM (14.3-b01 mixed mode linux-amd64 ) 
# Problematic frame: 
# C 0x0000000000043566 
# 
# 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. 
SIGSEGV 잘못된 신호 유형
pc는 IP/PC 레지스터 값, 즉 명령을 실행하는 코드 주소입니다.
pid가 프로세스 id입니다.
# Problematic frame:
# V  [libjvm.so+0x593045]
문제를 일으키는 동적 링크 라이브러리 함수의 주소입니다.
pc와 +0x593045는 같은 주소를 가리키며, 단지 동적 편이 주소일 뿐이며, 하나는 실행 중인 가상 주소이다
b. 신호 정보
자바에 linux에 등록된 신호 처리 함수, 중간에 2개의 매개 변수 info,ucvoid

static void crash_handler(int sig, siginfo_t* info, void* ucVoid) { 
 // unmask current signal 
 sigset_t newset; 
 sigemptyset(&newset); 
 sigaddset(&newset, sig); 
 sigprocmask(SIG_UNBLOCK, &newset, NULL); 
 
 VMError err(NULL, sig, NULL, info, ucVoid); 
 err.report_and_die(); 
} 
crash 보고서에서의 신호 오류 알림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 아키텍처:

void os::print_context(outputStream *st, void *context) { 
 if (context == NULL) return; 
 
 ucontext_t *uc = (ucontext_t*)context; 
 st->print_cr("Registers:"); 
#ifdef AMD64 
 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("Top of Stack: (sp=" PTR_FORMAT ")", sp); 
 print_hex_dump(st, (address)sp, (address)(sp + 8*sizeof(intptr_t)), sizeof(intptr_t)); 
 st->cr(); 
 
 // Note: it may be unsafe to inspect memory near pc. For example, pc may 
 // point to garbage if entry point in an nmethod is corrupted. Leave 
 // this at the end, and hope for the best. 
 address pc = 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: 90 90 55 48 89 e5 48 81 ec 98 9f 00 00 48 89 bd 
0x00007f48f14ef50a: f8 5f ff ff 48 89 b5 f0 5f ff ff b8 00 00 00 00 
0x00007f48f14ef51a: c7 00 01 00 00 00 c6 85 00 60 ff ff ff c9 c3 90 
0x00007f48f14ef52a: 90 90 90 90 90 90 55 48 89 e5 53 48 8d 1d 94 00 
instruction 부분에서 기계 코드가 출력됩니다.
형식 :   첫 번째는udis 라이브러리에 있는udcli 도구를 사용하여 어셈블리
명령:echo '90 90 55 48 89 e5 48 81 ec 98 9f 00 00 48 89 bd' | udcli -intel -x -64 -o 0x00007f48f14ef4fa  해당하는 어셈블리 표시
두 번째는 쓸 수 있어요.objectdump -d -C libjvm.so >> jvmsodisass.dump   오프셋 주소 0x593045를 찾습니다. 바로 당시 실행된 어셈블리입니다. 그리고 상하문과 결합하여 원본 코드로 문제의 문장을 추측합니다.
d. 레지스터에 대응하는 메모리의 값

RAX=0x0000000000000000 is an unknown value 
RBX=0x000000041a07d1e8 is an oop 
{method} 
 - klass: {other class} 
RCX=0x0000000000000000 is an unknown value 
RDX=0x0000000040111800 is a thread 
RSP=0x0000000041261b88 is pointing into the stack for thread: 0x0000000040111800 
RBP=0x000000004126bb20 is pointing into the stack for thread: 0x0000000040111800 
RSI=0x000000004126bb80 is pointing into the stack for thread: 0x0000000040111800 
RDI=0x00000000401119d0 is an unknown value 
R8 =0x0000000040111c40 is an unknown value 
R9 =0x00007f48fcc8b550: <offset 0xa85550> in /usr/java/jdk1.6.0_30/jre/lib/amd64/server/libjvm.so at 0x00007f48fc206000 
R10=0x00007f48f8ca7d41 is an Interpreter codelet 
method entry point (kind = native) [0x00007f48f8ca7ae0, 0x00007f48f8ca8320] 2112 bytes 
R11=0x00007f48fc98f270: <offset 0x789270> in /usr/java/jdk1.6.0_30/jre/lib/amd64/server/libjvm.so at 0x00007f48fc206000 
R12=0x0000000000000000 is an unknown value 
R13=0x000000041a07d1e8 is an oop 
{method} 
 - klass: {other class} 
R14=0x000000004126bb88 is pointing into the stack for thread: 0x0000000040111800 
R15=0x0000000040111800 is a thread 
jvm는 레지스터의 값을 통해 대응하는 대상을 찾는 데 좋은 참고가 될 수 있습니다
e. 기타 정보
error 안에 일부 라인 정보, 그리고 당시 메모리 이미지 정보도 있는데 이것들은 모두 분석의 일부분으로 참고할 수 있다
crash 보고서는 당시의 상황을 대략적으로 반영할 수 있다. 특히 코어덤프가 없을 때 분석에 도움이 되지만 코어덤프가 있다면 최종적으로 코어덤프는 문제의 원인을 신속하고 정확하게 발견할 수 있다.
이상은 본문의 전체 내용입니다. 본고의 내용이 여러분의 학습이나 업무에 일정한 도움을 줄 수 있는 동시에 저희를 많이 지지해 주시기 바랍니다!

좋은 웹페이지 즐겨찾기