Як я можу зловити SIGSEGV (помилка сегментації) і отримати трасування стека під JNI на Android?


92

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

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

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


Якщо ви можете запустити віртуальну машину Java за допомогою сценарію-обгортки, ви можете перевірити, чи додаток не працював аномально, та зробити звіт про помилки. Це дозволить вам чітко вловлювати всі види ненормальних виходів, будь то SIGSEGV, SIGKILL або що завгодно. Однак, я не думаю, що це можливо з акційними програмами для Android, тому розміщуючи це як коментар (перетворено з відповіді).
sleske

Також дивіться: Не вдається запустити програму Java для Android із Valgrind про те, як запустити програму для Android зі сценарієм обгортки (у оболонці adb).
sleske

1
Відповідь потрібно оновити. Вихідний код, наданий у прийнятій відповіді, призведе до невизначеної поведінки через виклик функцій, що не захищають асинхронний сигнал. Будь ласка , дивіться тут: stackoverflow.com/questions/34547199 / ...
user1506104

Відповіді:


82

Редагувати: Починаючи з Jelly Bean, ви не можете отримати трасування стека, тому що він READ_LOGSпішов . :-(

Я насправді змусив обробник сигналів працювати, не роблячи нічого надто екзотичного, і випустив код, використовуючи його, який ви можете побачити на github (редагувати: посилання на історичний випуск; з тих пір я видалив обробник збоїв). Ось як:

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

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


2
В ідеалі ви перевірите, чи не стався збій у вашій бібліотеці. Якщо це сталося десь в іншому місці (скажімо, у віртуальній машині), ваші дзвінки JNI з обробника сигналу можуть досить сильно заплутати речі. Це ще не кінець світу, оскільки в будь-якому випадку ви в середині аварії, але це може ускладнити діагностику аварії ВМ (або спричинити химерний збій ВМ, який потрапляє у звіт про помилки Android і збиває з пантелику всіх).
fadden

Ви чудовий @Chris, що поділився своїм дослідницьким проектом з цього приводу!
olafure

Дякую, це було корисно для того, щоб дізнатись, де мій JNI зводився з розуму. Крім того, привіт випускнику DCS!
Нік

3
Початок активності в новому процесі із Служби також вимагає наступного коду:newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Graeme

1
Чи все ще це рішення діє під Jelly Bean? Чи не буде крок 6 зафіксувати будь-які debuggerdвихідні дані?
Джош

14

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

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


6

FWIW, Google Breakpad чудово працює на Android. Я зробив роботу з перенесення, і ми доставляємо її як частину Firefox Mobile. Це вимагає невеликого налаштування, оскільки воно не дає вам слідів стеку на стороні клієнта, а надсилає вам необроблену пам’ять стека та робить стек, що ходить на стороні сервера (так що вам не доведеться доставляти символи налагодження разом із вашим додатком ).


1
Налаштувати Breakpad практично неможливо, враховуючи абсолютно відсутні документи
шейдер

Це насправді не так складно, і на вікі проекту є багато документації. Насправді для Android зараз існує NDK Makefile Makefile, і він повинен бути надзвичайно простим у використанні: code.google.com/p/google-breakpad/source/browse/trunk/…
Тед Мілчарек,

Вам також потрібно скомпілювати модуль, який попередньо обробляє файли символів налагодження для Android, і ви можете скомпілювати його лише в Linux. Коли ви компілюєте на Mac - він створює лише препроцесор dsym для Mac / iOS.
шейдер

5

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

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


Ну, поведінка невизначена після "ігнорування [сигналу] SIGFPE, SIGILL або SIGSEGV, який не був сформований вбивством (2) або підвищенням (3)", але не обов'язково під час лову такого сигналу. Поточний план полягає в тому, щоб спробувати обробник сигналу C, який передзвонює Java і, якимось чином, завершує потік без завершення процесу. Це може бути, а може і неможливо. :-)
Кріс Бойл

1
Інструкції щодо зворотного відстеження: stackoverflow.com/questions/76822/…
Кріс Бойл

1
... крім того, що я не можу використовувати зворотну трасування (), оскільки Android не використовує glibc, він використовує Bionic. :-( Що - то з участю _Unwind_Backtraceвід unwind.hбуде необхідно замість цього.
Chris Boyle
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.