Вопрос по android – Как я могу поймать SIGSEGV (ошибка сегментации) и получить трассировку стека под JNI на Android?

87

Я двигаюсьпроект в новый комплект разработки для Android (например, JNI), и я хотел бы поймать SIGSEGV, если это произойдет (возможно, также SIGILL, SIGABRT, SIGFPE), чтобы представить приятный диалог отчетности о сбоях вместо (или до) того, что в настоящее время случается: немедленная бесцеремонная смерть процесса и, возможно, попытка ОС перезапустить его. (Edit: JVM / Dalvik VM ловит сигнал и регистрирует трассировку стека и другую полезную информацию; Я просто хочу предложить пользователю возможность отправить эту информацию мне по-настоящему.)

Ситуация такова: большая часть кода на C, которую я не написал, выполняет большую часть работы в этом приложении (всю игровую логику), и, хотя она хорошо протестирована на многих других платформах, вполне возможно, что я в моем порте Android будет загружать мусор и вызывать сбой в нативном коде, поэтому я хочу, чтобы аварийные дампы (как нативные, так и Java) отображались в журнале Android (я думаю, это будет stderr в не Android) ситуация). Я свободен изменять код C и Java произвольно, хотя количество обратных вызовов (входящих и исходящих из JNI) составляет около 40, и, очевидно, бонусные баллы за небольшие различия.

Я слышал о библиотеке цепочки сигналов в J2SE, libjsig.so, и если бы я мог безопасно установить такой обработчик сигналов на Android, это решило бы важную часть моего вопроса, но я не вижу такой библиотеки для Android / Dalvik ,

Также см:Can't run a Java Android program with Valgrind о том, как запустить приложение Android с помощью скрипта-оболочки (в оболочке adb). sleske
Ответ должен быть обновлен. Исходный код, предоставленный в принятом ответе, приведет к неопределенному поведению из-за вызова не асинхронно-безопасных функций. Пожалуйста, смотрите здесь:stackoverflow.com/questions/34547199/… user1506104
Если вы можете запустить Java VM через скрипт-обертку, вы можете проверить, не было ли приложение ненормальным, и создать отчет об ошибках. Это позволит вам ловко ловить все виды аномальных выходов, будь то SIGSEGV, SIGKILL или что-то еще. Тем не менее, я не думаю, что это возможно для стандартных Android-приложений, поэтому публикую их как комментарий (конвертированный из ответа). sleske

Ваш Ответ

4   ответа
5

По моему ограниченному опыту (не Android), SIGSEGV в коде JNI, как правило, приводит к сбою JVM, прежде чем управление возвращается в ваш код Java. Я смутно припоминаю, что слышал о какой-то не-Sun JVM, которая позволяет вам ловить SIGSEGV, но AFAICR вы не можете ожидать, что сможете это сделать.

Вы можете попытаться перехватить их в C (см. Sigaction (2)), хотя вы можете сделать очень мало после обработчика SIGSEGV (или SIGFPE или SIGILL), поскольку текущее поведение процесса официально не определено.

Что ж, поведение не определено после "игнорирования" [S] сигнала SIGFPE, SIGILL или SIGSEGV, который не был сгенерирован kill (2) или Повышать (3) ", но не обязательно во время перехвата такого сигнала. Текущий план состоит в том, чтобы попытаться обработчик сигнала C, который обращается к Java и, так или иначе, завершает поток, не завершая процесс. Это может или не может быть возможно. :-) Chris Boyle
Обратные инструкции C:stackoverflow.com/questions/76822/… Chris Boyle
... за исключением того, что я не могу использовать backtrace (), потому что Android не использует glibc, он использует Bionic. :-( Что-то вовлекающее_Unwind_Backtrace отunwind.h будет необходимо вместо этого. Chris Boyle
14

Я немного опоздал, но у меня была точно такая же необходимость, и я разработал небольшую библиотеку для решения этой проблемы, обнаруживая общие сбои (SEGV, SIBGUSи т. д.) внутриJNI codeи заменить их обычнымиjava.lang.Error exceptions, Бонус, если клиент работает на Android & gt; =4.1.1трассировка стека встраивает разрешенныйbacktrace сбоя (псевдо-трассировка, содержащая полную трассировку собственного стека). Вы не восстановитесь после злобных сбоев (например, если вы испортили распределитель, например), но, по крайней мере, это должно позволить вам восстановиться послеmost из них. (пожалуйста, сообщайте об успехах и неудачах, код совершенно новый)

Больше информации наhttps://github.com/xroche/coffeecatch (кодBSD 2-Clauses license)

79

Edit: От Jelly Bean и далее вы не можете получить трассировку стека, потому чтоREAD_LOGS went away. :-(

Я на самом деле получил обработчик сигнала, не делая ничего слишком экзотического, и выпустил код с его использованием, который вы можете увидетьна GitHub (редактировать: ссылка на историческую версию; с тех пор я удалил обработчик сбоя). Вот как:

  1. Use sigaction() to catch the signals and store the old handlers. (android.c:570)
  2. Time passes, a segfault happens.
  3. In the signal handler, call up to JNI one last time and then call the old handler. (android.c:528)
  4. In that JNI call, log any useful debugging info, and call startActivity() on an activity that is flagged as needing to be in its own process. (SGTPuzzles.java:962, AndroidManifest.xml:28)
  5. When you come back from Java and call that old handler, the Android framework will connect to debuggerd to log a nice native trace for you, and then the process will die. (debugger.c, debuggerd.c)
  6. Meanwhile, your crash-handling activity is starting up. Really you should pass it the PID so it can wait for step 5 to complete; I don't do this. Here you apologise to the user and ask if you can send a log. If so, gather the output of logcat -d -v threadtime and launch an ACTION_SEND with recipient, subject and body filled in. The user will have to press Send. (CrashHandler.java, SGTPuzzles.java:462, strings.xml:41
  7. Watch out for logcat failing or taking more than a few seconds. I have encountered one device, the T-Mobile Pulse / Huawei U8220, where logcat immediately goes into the T (traced) state and hangs. (CrashHandler.java:70, strings.xml:51)

В ситуации, не связанной с Android, некоторые из них будут другими. Вам нужно собрать свой собственный родной след, см.этот другой вопросв зависимости от того, какой у вас libc. Вам необходимо обработать дамп этой трассировки, запустить отдельный процесс обработчика сбоев и отправить электронное письмо некоторыми подходящими способами для вашей платформы, но я полагаю, что общий подход все еще должен работать.

Для запуска действия в новом процессе из службы также требуется следующий код:newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Спасибо, это было полезно в поиске, где мой JNI сходил с ума. Кроме того, привет от выпускника DCS!
Это решение до сих пор действует под Jelly Bean? На шаге 6 не будет ничего записаноdebuggerd выходы?
Вы замечательный @Chris, что поделились своим исследовательским проектом на эту тему!
В идеале вы должны проверить, произошел ли сбой в вашей библиотеке. Если это произошло где-то еще (скажем, внутри виртуальной машины), ваши JNI-вызовы из обработчика сигнала могут очень сильно запутать ситуацию. Это не конец света, так как вы все равно находитесь в середине сбоя, но это может затруднить диагностику сбоя виртуальной машины (или вызвать странный сбой виртуальной машины, который заканчивается в отчете об ошибках Android и сбивает с толку всех).
6

FWIW,Google Breakpad отлично работает на Android. Я выполнил работу по переносу, и мы отправляем ее как часть Firefox Mobile. Он требует небольшой настройки, поскольку он не дает вам трассировки стека на стороне клиента, но отправляет вам необработанную память стека и выполняет обработку стека на стороне сервера (поэтому вам не нужно отправлять символы отладки вместе с вашим приложением ).

Настроить Breakpad практически невозможно, учитывая абсолютно отсутствующую документацию
Это действительно не так сложно, и есть много документации по вики проекта. Фактически, для Android теперь есть Makefile для сборки NDK, и он должен быть очень простым в использовании:code.google.com/p/google-breakpad/source/browse/trunk/…

Похожие вопросы