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


Я переезжаю проект С новым 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.

4 84

4 ответа:

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

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

  1. использовать sigaction() чтобы поймать сигналы и сохранить старые обработчики. ( android.c: 570)
  2. проходит время, происходит segfault.
  3. в обработчике сигнала вызовите JNI в последний раз, а затем вызовите старый обработчик. (android.c: 528)
  4. в этом вызове JNI регистрируйте любую полезную отладочную информацию и вызывайте startActivity() на действие, которое помечено как необходимость быть в своем собственном процессе. (SGTPuzzles.java: 962,AndroidManifest.xml: 28)
  5. когда вы возвращаетесь с Java и вызываете этот старый обработчик, платформа Android подключится к debuggerd чтобы войти в хороший родной след для вас, а затем процесс умрет. (отладчик.c,debuggerd.c)
  6. тем временем, ваша деятельность по обработке сбоев запускается. На самом деле вы должны передать ему PID, чтобы он мог дождаться завершения шага 5; я этого не делаю. Здесь вы извиняетесь перед Пользователем и спрашиваете, можете ли вы Отправить журнал. Если да, то соберите вывод logcat -d -v threadtime и запуститеACTION_SEND С получателем, тема и тело заполнены. Пользователь должен будет нажать кнопку Отправить. (CrashHandler.java, SGTPuzzles.java: 462,строки.xml: 41
  7. следи за logcat сбой или занимает более нескольких секунд. Я столкнулся с одним устройством, T-Mobile Pulse / Huawei U8220, где logcat сразу же переходит в T (проследить) и зависает. (CrashHandler.Ява:70, строки.xml: 51)

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

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

дополнительная информация на https://github.com/xroche/coffeecatch (код лицензия BSD 2-Clauses)

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

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

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