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

Estoy moviendo un proyecto al nuevo Android Native Development Kit (es decir, JNI) y me gustaría capturar SIGSEGV, en caso de que ocurra (posiblemente también SIGILL, SIGABRT, SIGFPE) con el fin de presentar un cuadro de diálogo de error agradable, en lugar de (O antes) de lo que sucede actualmente: la muerte inmediata sin ceremonia del proceso y posiblemente algún intento por parte del sistema operativo para reiniciarlo. ( Editar: La VM de JVM / Dalvik captura la señal y registra un rastreo de la pila y otra información útil, solo quiero ofrecer al usuario la opción de enviarme esa información por correo electrónico.)

La situación es: 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 numerosas otras plataformas, es totalmente posible que yo, en mi Android Puerto, lo alimentará basura y causar un accidente en el código nativo, así que quiero los volcados de desplome (nativos y Java) que aparecen actualmente en el registro de Android (supongo que sería stderr en una situación no-Android). Soy libre de modificar C y código Java de forma arbitraria, aunque los callbacks (tanto entrando y saliendo de JNI), el número de alrededor de 40 y, obviamente, los puntos de bonificación para los diffs pequeños.

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

Editar: De Jelly Bean en adelante no se puede obtener la pila de seguimiento, porque READ_LOGS fue . Unesdoc.unesco.org

En realidad tengo un manejador de señales trabajando sin hacer nada demasiado exótico, y he publicado el código que lo usa, que se puede ver en github (editar: enlazando a la versión histórica, he eliminado el manejador de fallos desde entonces). Así es cómo:

  1. Utilice sigaction() para capturar las señales y almacenar los manejadores antiguos. ( Android.c: 570 )
  2. El tiempo pasa, sucede un segfault.
  3. En el controlador de señal, llama a JNI una última vez y luego llama al manejador antiguo. ( Android.c: 528 )
  4. En esa llamada JNI, registre cualquier información de depuración útil y llame a startActivity() en una actividad que se señale como necesitando estar en su propio proceso. ( SGTPuzzles.java:962 , AndroidManifest.xml: 28 )
  5. Cuando regreses de Java y llames a ese manejador antiguo, el framework de Android se conectará a debuggerd para registrar un buen rastreo nativo para ti, y entonces el proceso morirá. ( Debugger.c , debuggerd.c )
  6. Mientras tanto, su actividad de manejo de accidentes se está iniciando. Realmente debe pasar el PID por lo que puede esperar a que el paso 5 para completar; Yo no hago esto. Aquí le pedimos disculpas al usuario y le preguntamos si puede enviar un registro. Si es así, recopile la salida de logcat -d -v threadtime y ejecute un ACTION_SEND con el destinatario, el sujeto y el cuerpo rellenados. El usuario tendrá que pulsar Enviar. ( CrashHandler.java , SGTPuzzles.java:462 , strings.xml: 41
  7. Tenga cuidado con logcat fallando o tomando más de unos segundos. He encontrado un dispositivo, el T-Mobile Pulse / Huawei U8220, donde logcat entra inmediatamente en el estado T (trazado) y se cuelga. ( CrashHandler.java:70 , strings.xml: 51 )

En una situación no-Android, algo de esto sería diferente. Tendrías que recopilar tu propio rastro nativo, ver esta otra pregunta , dependiendo de qué tipo de libc tienes. Tendrías que manejar el dumping de ese rastreo, el lanzamiento de tu proceso separado del manejador de fallos y el envío del correo electrónico en algunas formas apropiadas para tu plataforma, pero me imagino que el enfoque general todavía debería funcionar.

Estoy un poco tarde, pero tuve exactamente la misma necesidad, y he desarrollado una pequeña biblioteca para abordarla, atrapando bloqueos comunes ( SEGV , SIBGUS , etc.) dentro del código JNI y reemplazándolos por java.lang.Error regular java.lang.Error excepciones . Si el cliente se está ejecutando en Android> = 4.1.1 , el seguimiento de pila incorpora el backtrace resuelto del bloqueo (una pseudo-traza que contiene el rastreo de pila nativo completo). Usted no se recuperará de accidentes viciosos (es decir, si usted daña el asignador, por ejemplo), pero al menos debería permitirle recuperarse de la mayoría de ellos. (Por favor informe los éxitos y los fracasos, el código es nuevo)

Más información en https://github.com/xroche/coffeecatch (el código es la licencia BSD 2-Clauses )

FWIW, Google Breakpad funciona bien en Android. Hice el trabajo de portar y lo enviamos como parte de Firefox Mobile. Requiere un poco de configuración, ya que no le da pistas de pila en el lado del cliente, pero le envía la memoria de la pila sin procesar y hace que la pila caminar lado del servidor (por lo que no tiene que enviar símbolos de depuración con su aplicación ).

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

Puede intentar capturarlos en C (ver sigaction (2)), aunque puede hacer muy poco después de un manejador SIGSEGV (o SIGFPE o SIGILL) ya que el comportamiento continuo de un proceso está oficialmente indefinido.

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.