Android의 JNI에서 SIGSEGV (세그먼트 오류)를 포착하고 스택 추적을 얻으려면 어떻게해야합니까?


92

나는 이동하고있어 프로젝트 새로운 안드로이드 네이티브 개발 키트 (즉, JNI)에 및 나는 대신, 대화를보고 멋진 충돌을 제시하기 위해 (또한 SIGILL 가능성이 SIGABRT, SIGFPE)를 발생한다 SIGSEGV 잡을 싶습니다 (또는 그 이전에) 현재 일어나는 일 : 프로세스의 즉각적인 비 의식적인 죽음과 OS가 프로세스를 다시 시작하려는 시도. ( 편집 : JVM / Dalvik VM은 신호를 포착하고 스택 추적 및 기타 유용한 정보를 기록합니다. 사용자에게 해당 정보를 실제로 이메일로 보낼 수있는 옵션을 제공하고 싶습니다.)

상황은 다음과 같습니다. 제가 작성하지 않은 큰 C 코드는이 애플리케이션 (모든 게임 로직)에서 대부분의 작업을 수행하며 다른 수많은 플랫폼에서 잘 테스트되었지만 Android에서 전적으로 가능합니다. 포트는 가비지를 공급하고 네이티브 코드에서 크래시를 유발하므로 현재 Android 로그에 표시되는 크래시 덤프 (네이티브 및 Java 모두)를 원합니다 (Android가 아닌 상황에서는 stderr 일 것임). C와 Java 코드를 임의로 수정할 수 있지만, 콜백 (JNI로 들어오고 나가는 것 모두)이 약 40 개이고 분명히 작은 차이에 대한 보너스 포인트입니다.

J2SE, libjsig.so의 신호 체인 라이브러리에 대해 들었고 Android에 이와 같은 신호 핸들러를 안전하게 설치할 수 있다면 내 질문의 포착 부분을 해결할 수 있지만 Android / Dalvik 용 라이브러리는 보이지 않습니다. .


래퍼 스크립트를 통해 Java VM을 시작할 수 있으면 앱이 비정상적으로 종료되었는지 확인하고 오류보고를 수행 할 수 있습니다. 그러면 SIGSEGV, SIGKILL 등 모든 종류의 비정상적인 출구를 깨끗하게 잡을 수 있습니다. 그러나 재고 Android 앱에서는 이것이 가능하지 않다고 생각하므로 이것을 댓글로 게시하십시오 (답변에서 변환 됨).
sleske

래퍼 스크립트 (adb 셸에서)로 Android 앱을 시작하는 방법 은 Valgrind로 Java Android 프로그램을 실행할 수 없음을 참조하세요 .
sleske

1
답변을 업데이트해야합니다. 수락 된 답변에 제공된 소스 코드는 비동기 신호 안전이 아닌 함수 호출로 인해 정의되지 않은 동작을 초래합니다. 여기를 참조하십시오 : stackoverflow.com/questions/34547199/…
user1506104

답변:


82

편집 : 때문에 젤리 빈에서는 이후 당신은 스택 추적을 얻을 수 없다 READ_LOGS멀리 갔다 . :-(

나는 실제로 너무 이국적인 것을하지 않고 작동하는 신호 처리기를 얻었으며 그것을 사용하여 코드를 릴리스 했습니다 .github에서 볼 수 있습니다 (편집 : 기록 릴리스에 연결; 그 이후로 충돌 처리기를 제거했습니다). 방법은 다음과 같습니다.

  1. sigaction()신호를 포착하고 이전 핸들러를 저장하는 데 사용 합니다. ( android.c : 570 )
  2. 시간이 지나면 세그 폴트가 발생합니다.
  3. 신호 핸들러에서 마지막으로 한 번 JNI를 호출 한 다음 이전 핸들러를 호출합니다. ( android.c : 528 )
  4. 해당 JNI 호출에서 유용한 디버깅 정보를 기록 startActivity()하고 자체 프로세스에 있어야하는 것으로 플래그 지정된 활동을 호출 합니다. ( SGTPuzzles.java:962 , AndroidManifest.xml : 28 )
  5. 자바에서 돌아와 이전 핸들러를 호출하면 Android 프레임 워크가 연결되어 debuggerd멋진 네이티브 트레이스를 기록한 다음 프로세스가 종료됩니다. ( debugger.c , debuggerd.c )
  6. 한편, 충돌 처리 활동이 시작됩니다. 실제로 5 단계가 완료 될 때까지 기다릴 수 있도록 PID를 전달해야합니다. 나는 이러지 않는다. 여기에서 사용자에게 사과하고 로그를 보낼 수 있는지 묻습니다. 그렇다면 출력을 수집하고 수신자, 제목 및 본문 을 입력 logcat -d -v threadtime하여 실행합니다 ACTION_SEND. 사용자는 보내기를 눌러야합니다. ( CrashHandler.java , SGTPuzzles.java:462 , strings.xml : 41
  7. 조심 logcat실패 또는 몇 초 이상 복용. T-Mobile Pulse / Huawei U8220이라는 장치를 만났는데, 여기서 logcat은 즉시 T(추적 된) 상태가되고 중단됩니다. ( CrashHandler.java:70 , strings.xml : 51 )

Android가 아닌 상황에서는이 중 일부가 다를 수 있습니다. 자신의 고유 한 추적을 수집해야합니다. 보유한 libc의 종류에 따라 다른 질문을 참조하십시오 . 추적 덤프를 처리하고 별도의 크래시 처리기 프로세스를 시작하고 플랫폼에 적합한 방식으로 이메일을 보내야하지만 일반적인 접근 방식은 여전히 ​​작동 할 것이라고 생각합니다.


2
이상적으로는 라이브러리에서 충돌이 발생했는지 확인하는 것이 좋습니다. 다른 곳 (예 : VM 내부)에서 발생한 경우 신호 처리기의 JNI 호출이 상황을 심하게 혼동 할 수 있습니다. 어쨌든 충돌 중반이기 때문에 세상의 끝은 아니지만 VM 충돌 진단을 더 어렵게 만들 수 있습니다 (또는 Android 버그 보고서로 끝나고 모든 사람을 당황시키는 기괴한 VM 충돌을 유발할 수 있습니다).
fadden

이것에 대한 연구 프로젝트를 공유 해주신 @Chris가 훌륭합니다!
olafure 2011-06-14

고마워, 이것은 내 JNI가 미친 곳을 찾는 데 유용했습니다. 또한 DCS 동문 여러분 안녕하세요!
Nick

3
서비스의 새 프로세스에서 활동을 시작하려면 다음 코드도 필요합니다.newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Graeme

1
이 솔루션은 Jelly Bean에서 여전히 유효합니까? 6 단계가 debuggerd출력 을 기록하는 데 실패하지 않습니까?
Josh

14

나는 늦게 조금 해요,하지만 난 똑같은 필요가 있고, 나는 (공통 충돌을 잡기에 의해, 그것을 해결하기 위해 작은 라이브러리를 개발 한 SEGV, SIBGUS내부 등) JNI 코드 , 정기적으로 교체 java.lang.Error 예외 . 보너스로 클라이언트가 Android> = 4.1.1에서 실행중인 경우 스택 추적은 크래시 의 해결 된 역 추적 (전체 네이티브 스택 추적을 포함하는 의사 추적)을 포함합니다. 악의적 인 충돌 (예 : 할당자를 손상시키는 경우)에서 복구 할 수는 없지만 최소한 대부분 의 경우 복구 할 수 있어야 합니다. (성공 및 실패를보고하십시오. 코드는 새로운 것입니다)

자세한 정보는 https://github.com/xroche/coffeecatch (코드는 BSD 2-Clauses 라이선스입니다 )


6

FWIW, Google Breakpad 는 Android에서 잘 작동합니다. 포팅 작업을했고 Firefox Mobile의 일부로 제공하고 있습니다. 클라이언트 측에서 스택 추적을 제공하지 않지만 원시 스택 메모리를 전송하고 스택 워킹 서버 측을 수행하므로 약간의 설정이 필요하므로 앱과 함께 디버그 기호를 제공 할 필요가 없습니다. ).


1
완전히 누락 된 문서를 고려하여 Breakpad를 구성하는 것은 거의 불가능합니다
shader

그다지 어렵지 않고 프로젝트 위키에 대한 많은 문서가 있습니다. 사실, 안드로이드의 NDK 빌드 메이크 지금이 그리고 그것은 매우 쉽게 사용할 수 있어야한다 : code.google.com/p/google-breakpad/source/browse/trunk/...
테드 Mielczarek

또한 Android 용 디버그 기호 파일을 전처리하는 모듈을 컴파일해야하며 Linux에서만 컴파일 할 수 있습니다. Mac에서 컴파일하면 Mac / iOS dSym 전처리기만 빌드됩니다.
shader

5

내 제한된 경험 (비 Android)에서 JNI 코드의 SIGSEGV는 일반적으로 제어가 Java 코드로 반환되기 전에 JVM을 충돌시킵니다. 나는 당신이 SIGSEGV를 잡을 수있게 해주는 Sun이 아닌 JVM에 대해 들었던 것을 막연하게 회상하지만 AFAICR은 그렇게 할 수 있다고 기대할 수 없습니다.

프로세스의 진행중인 동작이 공식적으로 정의되지 않았기 때문에 SIGSEGV (또는 SIGFPE 또는 SIGILL) 핸들러 후에는 거의 수행 할 수 없지만 C (sigaction (2) 참조)에서이를 포착하려고 시도 할 수 있습니다.


글쎄, 행동은 "kill (2) 또는 raise (3)에 의해 생성되지 않은 SIGFPE, SIGILL 또는 SIGSEGV 신호를 무시 [ing]"한 후에 정의되지 않지만 그러한 신호를 잡는 동안 반드시 그런 것은 아닙니다. 현재 계획은 Java를 다시 호출하고 프로세스를 종료하지 않고 스레드를 종료하는 C 신호 핸들러를 시도하는 것입니다. 이것은 가능할 수도 있고 불가능할 수도 있습니다. :-)
Chris Boyle


1
... Android는 glibc를 사용하지 않고 Bionic을 사용하기 때문에 backtrace ()를 사용할 수 없습니다. :-(과 관련된 뭔가 _Unwind_Backtrace로부터는 unwind.h대신 필요합니다.
크리스 보일
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.