android jni signals android-ndk sigsegv

¿Cómo puedo detectar SIGSEGV(falla de segmentación) y obtener un seguimiento de pila bajo JNI en Android?



signals android-ndk (4)

Moveré un proyecto al nuevo Kit de desarrollo nativo de Android (es decir, JNI) y me gustaría capturar SIGSEGV, en caso de que ocurra (posiblemente también SIGILL, SIGABRT, SIGFPE) para presentar un buen cuadro de diálogo de informe de bloqueo, en lugar de (o antes) lo que ocurre actualmente: la muerte inmediata sin ceremonias del proceso y posiblemente algún intento por parte del SO de reiniciarlo. ( Editar: La JVM / Dalvik VM capta la señal y registra un seguimiento de pila y otra información útil; solo quiero ofrecerle al usuario la opción de enviarme esa información por correo electrónico).

La situación es la siguiente: un gran cuerpo de código C que no escribí hace la mayor parte del trabajo en esta aplicación (toda la lógica del juego) y aunque está bien probado en muchas otras plataformas, es muy posible que yo, en mi Android port, lo alimentarán con basura y provocarán un bloqueo en el código nativo, por lo que quiero los volcados de bloqueo (tanto nativos como de Java) que aparecen actualmente en el registro de Android (supongo que sería stderr en una situación que no sea de Android). Soy libre de modificar tanto el código C como el código Java de forma arbitraria, aunque las devoluciones de llamada (que entran y salen de JNI) suman aproximadamente 40 y, obviamente, puntos de bonificación para pequeñas diffs.

He oído hablar de la biblioteca de encadenamiento de señal en J2SE, libjsig.so, y si pudiera instalar con seguridad un manejador de señal como ese en Android, eso resolvería la parte atrapante de mi pregunta, pero no veo esa biblioteca para Android / Dalvik .


En mi experiencia limitada (que no es Android), SIGSEGV en código JNI generalmente bloqueará la JVM antes de que se devuelva el control a su código Java. Recuerdo vagamente haber escuchado acerca de una JVM ajena a Sun que le permite capturar SIGSEGV, pero AFAICR no puede esperar poder hacerlo.

Puede tratar de capturarlos en C (vea sigaction (2)), aunque puede hacer muy poco después de un controlador SIGSEGV (o SIGFPE o SIGILL) ya que el comportamiento continuo de un proceso no está definido oficialmente.


Estoy un poco tarde, pero tenía exactamente la misma necesidad, y he desarrollado una pequeña biblioteca para abordarlo, SEGV fallas comunes ( SEGV , SIBGUS , etc.) dentro del código JNI , y las reemplazo por java.lang.Error normal. java.lang.Error excepciones . Además, si el cliente se ejecuta en Android> = 4.1.1 , el seguimiento de la pila incorpora la traza inversa resuelta del bloqueo (una pseudo-traza que contiene el seguimiento completo de la pila nativa). No se recuperará de caídas viciosas (es decir, si corrompe el asignador, por ejemplo), pero al menos debería permitirle recuperarse de la mayoría de ellos. (por favor informe de éxitos y fracasos, el código es completamente nuevo)

Más información en https://github.com/xroche/coffeecatch (el código es una licencia BSD de 2 cláusulas )


FWIW, Google Breakpad funciona bien en Android. Hice el trabajo de portación y lo enviamos como parte de Firefox Mobile. Requiere una pequeña configuración, ya que no le da rastros de pila en el lado del cliente, pero le envía la memoria de pila sin procesar y hace que la pila pase al lado del servidor (para que no tenga que enviar símbolos de depuración con su aplicación )


Editar: a partir de Jelly Bean en adelante no se puede obtener el seguimiento de la pila, porque READ_LOGS desapareció . :-(

De hecho, obtuve un manejador de señal que funciona sin hacer nada demasiado exótico, y he lanzado un código para usarlo, que se puede ver en github (editar: vincular a la versión histórica; eliminé el controlador de bloqueo desde entonces). Así es cómo:

  1. Use sigaction() para capturar las señales y almacenar los manejadores antiguos. ( android.c:570 )
  2. El tiempo pasa, ocurre un segfault.
  3. En el controlador de señal, llame a JNI por última vez y luego llame al controlador anterior. ( android.c:528 )
  4. En esa llamada JNI, registre cualquier información de depuración útil y llame a startActivity() en una actividad que está marcada como que necesita estar en su propio proceso. ( SGTPuzzles.java:962 , AndroidManifest.xml:28 )
  5. Cuando regrese de Java y llame a ese antiguo manejador, el marco de Android se conectará a debuggerd para registrar un buen trazo nativo para usted, y luego el proceso morirá. ( debugger.c , debuggerd.c )
  6. Mientras tanto, su actividad de manejo de choques está comenzando. Realmente debería pasarle el PID para que pueda esperar a que se complete el paso 5; Yo no hago esto Aquí se disculpa con el usuario y le pregunta si puede enviar un registro. Si es así, recopile el resultado de logcat -d -v threadtime y ejecute un ACTION_SEND con el destinatario, el asunto y el cuerpo rellenado. El usuario tendrá que presionar Enviar. ( CrashHandler.java , SGTPuzzles.java:462 , strings.xml:41
  7. Tenga cuidado con el logcat falla o que toma más de unos pocos segundos. Me encontré con un dispositivo, el T-Mobile Pulse / Huawei U8220, donde el logcat entra inmediatamente en el estado T (trazado) y se cuelga. ( CrashHandler.java:70 , strings.xml:51 )

En una situación que no es Android, algo de esto sería diferente. Tendría que recopilar su propia traza nativa, vea esta otra pregunta , dependiendo del tipo de libc que tenga. Tendría que manejar deshacerse de esa traza, iniciar su proceso separado de administración de bloqueos y enviar el correo electrónico de forma apropiada para su plataforma, pero me imagino que el enfoque general debería funcionar.