Comment puis-je détecter SIGSEGV (erreur de segmentation) et obtenir une trace de pile sous JNI sur Android ?

StackOverflow https://stackoverflow.com/questions/1083154

Question

Je bouge un projet au nouveau kit de développement natif Android (c'est-à-direJNI) et j'aimerais attraper SIGSEGV, si cela se produit (éventuellement aussi SIGILL, SIGABRT, SIGFPE) afin de présenter une jolie boîte de dialogue de rapport de crash, au lieu (ou avant) de ce qui se passe actuellement :la mort immédiate et sans cérémonie du processus et éventuellement une tentative du système d'exploitation pour le redémarrer.(Modifier: La VM JVM/Dalvik capte le signal et enregistre une trace de pile et d'autres informations utiles ;Je veux juste offrir à l'utilisateur la possibilité de m'envoyer ces informations par e-mail.)

La situation est la suivante :un grand corps de code C que je n'ai pas écrit fait l'essentiel du travail dans cette application (toute la logique du jeu) et bien qu'il soit bien testé sur de nombreuses autres plates-formes, il est tout à fait possible que, dans mon port Android, je nourrisse cela détruit et provoque un crash dans le code natif, donc je veux les vidages sur incident (à la fois natifs et Java) qui apparaissent actuellement dans le journal Android (je suppose que ce serait stderr dans une situation non Android).Je suis libre de modifier arbitrairement le code C et Java, bien que les rappels (entrants et sortants de JNI) soient au nombre d'environ 40 et évidemment, des points bonus pour les petites différences.

J'ai entendu parler de la bibliothèque de chaînage de signaux dans J2SE, libjsig.so, et si je pouvais installer en toute sécurité un gestionnaire de signaux comme celui-ci sur Android, cela résoudrait la partie captivante de ma question, mais je ne vois pas de bibliothèque de ce type pour Android/Dalvik. .

Était-ce utile?

La solution

Modifier: À partir de Jelly Bean, vous ne pouvez pas obtenir la trace de la pile, car READ_LOGS parti. :-(

En fait, j'ai un gestionnaire de signal qui fonctionne sans rien faire de trop exotique, et j'ai publié du code l'utilisant, que vous pouvez voir sur github (modifier:lien vers la version historique ;J'ai supprimé le gestionnaire de crash depuis).Voici comment:

  1. Utiliser sigaction() pour capter les signaux et stocker les anciens gestionnaires.(android.c:570)
  2. Le temps passe, une erreur de segmentation se produit.
  3. Dans le gestionnaire de signaux, appelez JNI une dernière fois, puis appelez l'ancien gestionnaire.(android.c:528)
  4. Dans cet appel JNI, enregistrez toutes les informations de débogage utiles et appelez startActivity() sur une activité signalée comme devant s'inscrire dans son propre processus.(SGTPuzzles.java:962, AndroidManifest.xml:28)
  5. Lorsque vous revenez de Java et appelez cet ancien gestionnaire, le framework Android se connectera à debuggerd pour enregistrer une belle trace native pour vous, puis le processus mourra.(débogueur.c, débogueur.c)
  6. Pendant ce temps, votre activité de gestion des crashs démarre.Vraiment, vous devriez lui transmettre le PID afin qu'il puisse attendre la fin de l'étape 5 ;Je ne fais pas ça.Ici, vous vous excusez auprès de l'utilisateur et demandez si vous pouvez envoyer un journal.Si tel est le cas, rassemblez le résultat de logcat -d -v threadtime et lancer un ACTION_SEND avec destinataire, sujet et corps renseignés.L'utilisateur devra appuyer sur Envoyer.(CrashHandler.java, SGTPuzzles.java:462, chaînes.xml:41
  7. Attention à logcat échoue ou prend plus de quelques secondes.J'ai rencontré un appareil, le T-Mobile Pulse / Huawei U8220, sur lequel logcat entre immédiatement dans le T (tracé) état et se bloque.(CrashHandler.java:70, chaînes.xml:51)

Dans une situation non Android, certaines choses seraient différentes.Vous auriez besoin de rassembler votre propre trace native, voir cette autre question, selon le type de libc dont vous disposez.Vous devrez gérer le vidage de cette trace, lancer votre processus de gestion de crash distinct et envoyer l'e-mail de manière appropriée pour votre plate-forme, mais j'imagine que l'approche générale devrait toujours fonctionner.

Autres conseils

Je suis un peu en retard, mais j'avais exactement le même besoin, et j'ai développé une petite bibliothèque pour y répondre, en détectant les crashs courants (SEGV, SIBGUS, etc.) à l'intérieur Code JNI, et remplacez-les par des réguliers java.lang.Error des exceptions.Bonus, si le client fonctionne sous Android >= 4.1.1, la trace de pile intègre le résolu trace du crash (une pseudo-trace contenant la trace complète de la pile native).Vous ne vous remettrez pas des crashs vicieux (c.-à-d.si vous corrompez l'allocateur, par exemple), mais au moins cela devrait vous permettre de récupérer de la plupart d'eux.(veuillez signaler les succès et les échecs, le code est tout nouveau)

Plus d'informations sur https://github.com/xroche/coffeecatch(le code est Licence BSD 2 clauses)

FWIW, Google Breakpad fonctionne bien sur Android.J'ai effectué le travail de portage et nous le livrons dans le cadre de Firefox Mobile.Cela nécessite un peu de configuration, car il ne vous donne pas de traces de pile côté client, mais vous envoie la mémoire brute de la pile et effectue le parcours de la pile côté serveur (vous n'avez donc pas à envoyer de symboles de débogage avec votre application). ).

D'après mon expérience limitée (non Android), SIGSEGV dans le code JNI fera généralement planter la JVM avant que le contrôle ne soit rendu à votre code Java.Je me souviens vaguement avoir entendu parler d'une JVM non Sun qui vous permet d'attraper SIGSEGV, mais AFAICR, vous ne pouvez pas vous attendre à pouvoir le faire.

Vous pouvez essayer de les attraper en C (voir sigaction(2)), bien que vous puissiez faire très peu de choses après un gestionnaire SIGSEGV (ou SIGFPE ou SIGILL) car le comportement en cours d'un processus n'est officiellement pas défini.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top