Android'de JNI altında SIGSEGV'yi (segmentasyon hatası) nasıl yakalayabilirim ve bir yığın izlemesi alabilirim?


93

Ben hareket ediyorum bir proje Yeni Android Yerel Geliştirme Kiti (yani JNI) ve bunun yerine, diyalog raporlama güzel kazasında sunmak için (ayrıca SIGILL muhtemelen SIGABRT, SIGFPE) gerçekleşmesi gereken, SIGSEGV yakalamak istiyorum (veya daha önce) şu anda olanlardan: sürecin ani ve belirsiz ölümü ve muhtemelen işletim sisteminin onu yeniden başlatma girişimleri. ( Düzenleme: JVM / Dalvik VM sinyali yakalar ve bir yığın izini ve diğer yararlı bilgileri günlüğe kaydeder; yalnızca kullanıcıya bu bilgiyi bana gerçekten e-posta ile gönderme seçeneği sunmak istiyorum.)

Durum şu: Yazmadığım büyük bir C kodu bu uygulamadaki işin çoğunu yapıyor (tüm oyun mantığı) ve diğer birçok platformda iyi test edilmiş olmasına rağmen, Android'imde tamamen mümkün. bağlantı noktası, çöpü besleyecek ve yerel kodda bir çökmeye neden olacak, bu nedenle şu anda Android günlüğünde görünen kilitlenme dökümlerini (hem yerel hem de Java) istiyorum (Android olmayan bir durumda stderr olacağını tahmin ediyorum). Hem C hem de Java kodunu keyfi olarak değiştirmekte özgürüm, ancak geri aramalar (hem JNI'ye giren hem de çıkan) yaklaşık 40'tır ve tabii ki küçük farklar için bonus puanlar.

J2SE, libjsig.so'daki sinyal zincirleme kitaplığını duydum ve Android'e güvenli bir şekilde böyle bir sinyal işleyici kurabilirsem sorumun dikkat çekici kısmını çözerdi, ancak Android / Dalvik için böyle bir kitaplık göremiyorum .


Java sanal makinesini bir sarmalayıcı komut dosyası aracılığıyla başlatabilirseniz, uygulamanın anormal şekilde çıkıp çıkmadığını kontrol edebilir ve hata raporlaması yapabilirsiniz. Bu, SIGSEGV, SIGKILL veya her neyse, her türlü anormal çıkışı temiz bir şekilde yakalamanıza izin verir. Ancak, bunun stok Android uygulamalarıyla mümkün olduğunu düşünmüyorum, bu yüzden bunu bir yorum olarak yayınlamak (yanıttan dönüştürülmüş).
sleske

Ayrıca bkz: Bir Android uygulamasının bir sarmalayıcı komut dosyasıyla (adb kabuğunda) nasıl başlatılacağını öğrenmek için Valgrind ile bir Java Android programı çalıştıramazsınız .
sleske

1
Cevabın güncellenmesi gerekiyor. Kabul edilen yanıtta sağlanan kaynak kodu, eşzamansız sinyal güvenli işlevlerin çağrılması nedeniyle tanımlanmamış davranışla sonuçlanacaktır. Lütfen buraya bakın: stackoverflow.com/questions/34547199/…
user1506104

Yanıtlar:


82

Düzenleme: Jelly Bean'den itibaren yığın izini alamazsınız çünkü READ_LOGSgitti . :-(

Aslında çok egzotik bir şey yapmadan çalışan bir sinyal işleyicim var ve onu kullanarak github'da görebileceğiniz bir kod yayınladım (düzenleme: tarihsel sürüme bağlanmak; o zamandan beri çökme işleyicisini kaldırdım). Bunu nasıl yapacağınız aşağıda açıklanmıştır:

  1. sigaction()Sinyalleri yakalamak ve eski işleyicileri saklamak için kullanın . ( android.c: 570 )
  2. Zaman geçer, bir segfault olur.
  3. Sinyal işleyicide, JNI'yi son bir kez arayın ve ardından eski işleyiciyi arayın. ( android.c: 528 )
  4. Bu JNI çağrısında, her türlü yararlı hata ayıklama bilgisini günlüğe kaydedin startActivity()ve kendi sürecinde olması gerektiği şeklinde işaretlenen bir etkinliği çağırın . ( SGTPuzzles.java:962 , AndroidManifest.xml: 28 )
  5. Java'dan geri döndüğünüzde ve o eski işleyiciyi aradığınızda, Android çerçevesi debuggerdsizin için güzel bir yerel izleme kaydetmek üzere bağlanacak ve ardından süreç ölecektir. ( debugger.c , debuggerd.c )
  6. Bu arada, çarpışma halletme faaliyetiniz başlıyor. Gerçekten PID'yi iletmelisiniz, böylece adım 5'in tamamlanmasını bekleyebilir; Ben bunu yapmıyorum Burada kullanıcıdan özür diliyor ve bir günlük gönderip gönderemeyeceğinizi soruyorsunuz. Eğer öyleyse, çıktısını toplayın ve alıcı, konu ve gövde dolu olarak logcat -d -v threadtimebaşlatın ACTION_SEND. Kullanıcının Gönder'e basması gerekir. ( CrashHandler.java , SGTPuzzles.java:462 , strings.xml: 41
  7. Dikkat logcatbaşarısız ya da birkaç saniyeden fazla alarak. Logcat'in hemen T(izlenen) duruma geçtiği ve kilitlendiği T-Mobile Pulse / Huawei U8220 adlı bir cihazla karşılaştım . ( CrashHandler.java:70 , strings.xml: 51 )

Android olmayan bir durumda, bunların bir kısmı farklı olacaktır. Ne tür bir libc'ye sahip olduğunuza bağlı olarak , kendi yerel izinizi toplamanız, diğer soruya bakmanız gerekir. Bu izi dökmeyi, ayrı kilitlenme işleyici sürecinizi başlatmanız ve e-postayı platformunuz için uygun bazı yollarla göndermeniz gerekir, ancak genel yaklaşımın yine de işe yarayacağını tahmin ediyorum.


2
İdeal olarak, kütüphanenizde çökmenin olup olmadığını kontrol edersiniz. Başka bir yerde meydana geldiyse (örneğin, VM'nin içinde), sinyal işleyiciden gelen JNI çağrılarınız işleri oldukça kötü bir şekilde karıştırabilir. Zaten kazanın ortasında olduğunuz için dünyanın sonu değil, ancak bir VM çökmesinin teşhisini daha zor hale getirebilir (veya bir Android hata raporuyla sonuçlanan ve herkesi şaşırtan tuhaf bir VM çökmesine neden olabilir).
38'de fadden

Bu konuda araştırma projenizi paylaştığınız için harikasınız @Chris!
olafure

Teşekkürler, bu JNI'mın nerede çıldırdığını bulmamda faydalı oldu. Ayrıca, bir DCS mezunundan merhaba!
Nick

3
Bir Hizmetten yeni bir süreçte bir Faaliyet başlatmak için ayrıca şu kod gereklidir:newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Graeme

1
Bu çözüm Jelly Bean kapsamında hala geçerli mi? 6. adım herhangi bir debuggerdçıktıyı kaydetmede başarısız olmaz mı?
Josh

14

Ben biraz geç değilim, ama aynı ihtiyacı vardı ve ben (ortak çöküyor yakalayarak, bunu gidermek için küçük bir kütüphane geliştirdik SEGV, SIBGUSiç vs.) JNI'yı kodu ve düzenli tarafından bunların yerine java.lang.Error istisnalar . Bonus, istemci Android> = üzerinde çalışıyorsa 4.1.1, yığın izleme çökmenin çözülmüş geri izini (tam yerel yığın izlemesini içeren bir sözde izleme) gömer . Kısır çökmelerden kurtulamazsınız (örneğin, ayırıcıyı bozarsanız), ancak en azından çoğundan kurtulmanıza izin vermelidir . (lütfen başarıları ve başarısızlıkları bildirin, kod yepyeni)

Https://github.com/xroche/coffeecatch adresinde daha fazla bilgi (kod BSD 2-Cümleler lisansıdır )


6

FWIW, Google Breakpad Android'de iyi çalışıyor. Taşıma işini yaptım ve Firefox Mobile'ın bir parçası olarak gönderiyoruz. Küçük bir kurulum gerektirir, çünkü istemci tarafında size yığın izleri vermez, ancak size ham yığın belleğini gönderir ve yığını sunucu tarafında yürütür (böylece uygulamanızla hata ayıklama sembollerini göndermeniz gerekmez. ).


1
Kesinlikle eksik belgeleri göz önünde bulundurarak Breakpad'i yapılandırmak neredeyse imkansız
gölgelendirici

Gerçekten o kadar da zor değil ve proje wiki'sinde bol miktarda belge var. Aslında, Android için artık bir NDK derlemesi Makefile var ve kullanımı çok kolay olmalı: code.google.com/p/google-breakpad/source/browse/trunk/…
Ted Mielczarek

Ayrıca Android için hata ayıklama sembol dosyalarını önceden işleyen modülü derlemeniz gerekir ve bunu yalnızca Linux'ta derleyebilirsiniz. Bir Mac'te derlediğinizde - yalnızca Mac / iOS dSym ön işlemcisi oluşturur.
shader

5

Sınırlı deneyimime göre (Android olmayan), JNI kodundaki SIGSEGV, kontrol Java kodunuza dönmeden önce genellikle JVM'yi çökertecektir. SIGSEGV'i yakalamanıza izin veren Sun-dışı JVM hakkında duyduğumu belli belirsiz hatırlıyorum, ancak AFAICR bunu yapabilmeyi bekleyemezsiniz.

Bir sürecin devam eden davranışı resmi olarak tanımlanmadığı için bir SIGSEGV (veya SIGFPE veya SIGILL) işleyicisinden sonra çok az şey yapmanıza rağmen, bunları C'de yakalamaya çalışabilirsiniz (bkz. Sigaction (2)).


Öyle ki, öldürme (2) veya yükseltme (3) ile üretilmeyen bir SIGFPE, SIGILL veya SIGSEGV sinyalini "yok saydıktan] sonra davranış tanımsızdır, ancak böyle bir sinyali yakalama sırasında gerekli değildir. Mevcut plan, Java'yı geri çağıran ve bir şekilde süreci sonlandırmadan iş parçacığını sonlandıran bir C sinyal işleyicisini denemektir. Bu mümkün olabilir veya olmayabilir. :-)
Chris Boyle

1
C geri izleme talimatları: stackoverflow.com/questions/76822/…
Chris Boyle

1
... dışında backtrace () kullanamıyorum, çünkü Android glibc kullanmıyor, Bionic kullanıyor. :-( karıştığı şey _Unwind_Backtraceden unwind.hyerine ihtiyaç olacaktır.
Chris Boyle
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.