2012-12-08 5 views
11

Wenn ein nicht behandeltes NSException geworfen wird, hat der Stack-Trace einen Abschnitt wie folgt aus:iOS: Wie bekomme ich Stack-Trace von einer unbehandelten std :: exception?

Last Exception Backtrace: 
0 CoreFoundation     0x32bd688f __exceptionPreprocess + 163 
1 libobjc.A.dylib     0x34b7b259 objc_exception_throw + 33 
2 CoreFoundation     0x32bd65c5 -[NSException init] + 1 
3 Foundation      0x37296bd7 -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 263 
... 

Aber wenn std :: Ausnahme ausgelöst wird, erhalte ich nur das:

Thread 0 Crashed: 
0 libsystem_kernel.dylib   0x34f2632c __pthread_kill + 8 
1 libsystem_c.dylib    0x31e4c208 pthread_kill + 48 
2 libsystem_c.dylib    0x31e45298 abort + 88 
3 libc++abi.dylib     0x33bcaf64 abort_message + 40 
4 libc++abi.dylib     0x33bc8346 default_terminate() + 18 
5 libobjc.A.dylib     0x349f4368 _objc_terminate + 164 
6 libc++abi.dylib     0x33bc83be safe_handler_caller(void (*)()) + 70 
7 libc++abi.dylib     0x33bc844a std::terminate() + 14 
8 libc++abi.dylib     0x33bc981e __cxa_rethrow + 82 
9 libobjc.A.dylib     0x349f42a2 objc_exception_rethrow + 6 
10 CoreFoundation     0x329a5506 CFRunLoopRunSpecific + 398 
11 CoreFoundation     0x329a5366 CFRunLoopRunInMode + 98 
12 GraphicsServices    0x32af2432 GSEventRunModal + 130 
13 UIKit       0x34f84cce UIApplicationMain + 1074 
14 APP_NAME       0x00086b10 main (main.m:68) 
15 APP_NAME      0x00071b98 start + 32 

Wie komme ich die genauen Absturzinformationen aus diesem Absturzprotokoll?

Update -

Ich habe gegeben HockeyApp einen Schuss, aber es hat die gleiche Einschränkung wie iTunes Crash-Protokolle - es mir nicht sagen, den Stapel für eine nicht behandelte C++ Ausnahme.

+0

Verwenden Sie ein Drittanbieter-Tool wie crittercism oder testflightapp, um die gemeldeten Abstürze zu erhalten. Sie müssen eine DSM-Datei hochladen und dann wird es die Abstürze symbolisieren und es wird Ihnen die genaue Linie zeigen, wo der Absturz aufgetreten ist. Um die dsym-Datei zu finden, klicken Sie mit der rechten Maustaste auf die .ipa-Datei im Organizer-Fenster oder wo auch immer Sie sie gespeichert haben, klicken Sie mit der rechten Maustaste und zeigen Sie Inhalt an. – Srikanth

+0

Ich verwende kein Drittanbieter-Tool. Kannst du eins empfehlen? Außerdem vermute ich, dass diese Tools nicht mit C++ - Ausnahmen umgehen können - kennen Sie jemanden, der das tut? – Mar0ux

+0

Bin mir nicht sicher. Aber hier gehst du, dort ist eine andere Frage, die deinem ähnlich ist: http://stackoverflow.com/questions/11883069/how-to-decode-a-dsym-file-crash-log – Srikanth

Antwort

21

Was Sie sehen, ist eine unglückliche Eigenart von AppKit und UIKit. Sowohl iOS als auch OS X haben einen Ausnahme-Handler in CFRunLoop, der alle nicht abgefangenen Ausnahmen abfängt. Unter OS X zeigt der Handler die Ausnahme für den Benutzer mit einem Dialogfeld an. Unter iOS wiederholt der Handler die Ausnahme jedoch einfach erneut.

Objective-C-Ausnahmen, wie sie von NSException implementiert werden, speichern ihre Rückverfolgungen innerhalb des Ausnahmeobjekts direkt vor dem eigentlichen "throw" als Teil von [NSException init] (oder einer ähnlichen Initialisierungsmethode). Leider machen C++ - Ausnahmen nicht dasselbe. Normalerweise hat eine C++ Ausnahme ein Backtrace, weil die Laufzeitbibliothek erkennt, dass kein catch ist und ruft sofort std::terminate, was wiederum abort() fordert, den gesamten Call-Stack intakt bleibt, etwa so:

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 
0 libsystem_kernel.dylib   0x00007fff93ef8d46 __kill + 10 
1 libsystem_c.dylib    0x00007fff89968df0 abort + 177 
2 libc++abi.dylib     0x00007fff8beb5a17 abort_message + 257 
3 libc++abi.dylib     0x00007fff8beb33c6 default_terminate() + 28 
4 libobjc.A.dylib     0x00007fff8a196887 _objc_terminate() + 111 
5 libc++abi.dylib     0x00007fff8beb33f5 safe_handler_caller(void (*)()) + 8 
6 libc++abi.dylib     0x00007fff8beb3450 std::terminate() + 16 
7 libc++abi.dylib     0x00007fff8beb45b7 __cxa_throw + 111 
8 test       0x0000000102999f3b main + 75 
9 libdyld.dylib     0x00007fff8e4ab7e1 start + 1 

Wenn jedoch der Lauf Der Schleifenausnahmebehandler fängt die Ausnahme ab, die Ausführung wird normalerweise innerhalb des Blocks catch fortgesetzt. Das ursprüngliche Backtrace ist daher verloren. Der erneute Aufruf ruft anschließend std::terminate auf, aber zu diesem Zeitpunkt gibt der Aufrufstapel den Ausnahmehandler für die Ausführungsschleife wieder. Um ein Backtrace von einer C++ - Ausnahme in diesem Fall zu erhalten, müssen Sie ein Ausnahmeobjekt werfen, das NSException imitiert und den Aufruf-Stack als Teil seines Konstruktors liest, und sicherstellen, dass alle in Ihrem Code geworfenen Ausnahmen dasselbe tun . std::exception tut dies nicht und es ist auch nicht wahrscheinlich, dass der von Ihnen verwendete Code von Drittanbietern verwendet wird.

Einen Fehler mit Apple einreichen, der sie bittet, den Ausnahmehandler CFRunLoop zu entfernen, oder mindestens eine API zur Verfügung zu stellen, um sie abzuschalten. Es gibt keine API, nicht einmal einen SPI, um dies jetzt zu tun.

+1

Gibt es irgendeine Weise, grundlegende Ausnahmeinformation zu erhalten (Wie die Info in 'exception :: what()'), ohne die Ausnahme tatsächlich zu fangen, in einem Signal-Handler für SIGABRT? – MechEthan

+1

Hat jemand einen Fehler bei Apple eingereicht? Wie würde man eine C++ Ausnahme werfen, die NSException nachahmt? – Zammbi

+0

Ich habe einen Fehler mit Apple eingereicht. Offenes Radaräquivalent: http://www.openradar.me/radar?id=4943586562932736 – John

-2

sieht aus wie die Ausnahme von innen UIApplicationMain geschieht() ... Sie könnten versuchen, Ihre main.m-Datei in eine main.mm-Datei konvertieren, und dies zu tun:

int main(...) 
{ 
    try 
    { 
     return UIApplicationMain(...); 
    } 
    catch(exception e) 
    { 
     cerr < e.what() ; 
    } 
} 

Ich habe nicht wirklich versuchen es aber ...

0

von Gwynne Raskind beantworten Im Anschluss an Sie die CFRunLoop Exception-Handler GCD vermeiden können, indem Sie synchron einen Anruf zu einem Block von C++ Code versenden:

namespace 
{ 
    void MyThrowingCode() 
    { 
     throw std::runtime_error("My Exception"); 
    } 

} 

- (void)objcHandlerCalledFromARunLoop 
{ 
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
     MyThrowingCode(); 
     }); 
} 

Möglicherweise müssen Sie eigene Warteschlangen erstellen, anstatt eine einzige gemeinsam genutzte Warteschlange zu verwenden.

Der Crash-Protokoll sieht wie folgt aus:

0 libsystem_kernel.dylib   0x3aa39350 __pthread_kill + 8 
1 libsystem_c.dylib    0x3a9affb2 pthread_kill + 54 
2 libsystem_c.dylib    0x3a9ec366 abort + 90 
3 libc++abi.dylib     0x39f94dda abort_message + 70 
4 libc++abi.dylib     0x39f92094 default_terminate() + 20 
5 libobjc.A.dylib     0x3a545a70 _objc_terminate() + 168 
6 libc++abi.dylib     0x39f92118 safe_handler_caller(void (*)()) + 76 
7 libc++abi.dylib     0x39f921b0 std::terminate() + 16 
8 libc++abi.dylib     0x39f9359a __cxa_throw + 118 
9 Test       0x000fddfc MyThrowingCode() (MainViewController.mm:177) 
10 Test       0x000fe38c __35-[MainViewController toolsClick:]_block_invoke (MainViewController.mm:184) 
11 libdispatch.dylib    0x3a95f5d8 _dispatch_client_callout + 20 
12 libdispatch.dylib    0x3a962776 _dispatch_sync_f_invoke + 22 
13 Test       0x000fde90 -[MainViewController toolsClick:] (MainViewController.mm:183) 
14 UIKit       0x34744082 -[UIApplication sendAction:to:from:forEvent:] + 66 
15 UIKit       0x3474410c -[UIBarButtonItem(UIInternal) _sendAction:withEvent:] + 116 
16 UIKit       0x34744082 -[UIApplication sendAction:to:from:forEvent:] + 66 
17 UIKit       0x34744036 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 26 
18 UIKit       0x34744010 -[UIControl sendAction:to:forEvent:] + 40 
19 UIKit       0x347438c6 -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 498 
20 UIKit       0x34743db4 -[UIControl touchesEnded:withEvent:] + 484 
21 UIKit       0x3466c5f4 -[UIWindow _sendTouchesForEvent:] + 520 
22 UIKit       0x346598dc -[UIApplication sendEvent:] + 376 
23 UIKit       0x346591ea _UIApplicationHandleEvent + 6194 
24 GraphicsServices    0x363715f4 _PurpleEventCallback + 588 
25 GraphicsServices    0x36371222 PurpleEventCallback + 30 
26 CoreFoundation     0x3281f3e4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32 
27 CoreFoundation     0x3281f386 __CFRunLoopDoSource1 + 134 
28 CoreFoundation     0x3281e20a __CFRunLoopRun + 1378 
29 CoreFoundation     0x32791238 CFRunLoopRunSpecific + 352 
30 CoreFoundation     0x327910c4 CFRunLoopRunInMode + 100 
31 GraphicsServices    0x36370336 GSEventRunModal + 70 
32 UIKit       0x346ad2b4 UIApplicationMain + 1116 
33 Test       0x00120462 main (main.mm:55) 
34 libdyld.dylib     0x3a972b1c start + 0 
2

ich Glück gehabt habe mit mpipe3 Antwort zu verbessern.

In meiner Hauptmethode verwende ich eine try-catch, um die C++ - Ausnahme zu erhalten, und rufen Sie dann dispatch_sync auf dispatch_get_global_queue. Jetzt wird die vollständige Stapelverfolgung mit Zeilennummern auf Crashlytics (Crash-Manager) angezeigt.

Dies funktioniert, da es den CFRunLoop-Ausnahmehandler vermeidet, indem GCD verwendet wird, um die Ausnahme zu werfen.

+0

Scheint, dass diese Lösung nicht immer funktioniert. Aber es ist das Beste, was ich mir ausgedacht habe. – Zammbi