2012-12-08 19 views
11

Jeśli nieobsługiwany NSException jest wyrzucane, ślad stosu ma przekrój takiego:iOS: Jak uzyskać ślad stosu nieobsługiwanego std :: wyjątek?

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 
... 

Ale jeśli std :: jest wyjątek, mam tylko to:

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 

Jak mogę dostać dokładne informacje o awarii z tego dziennika awarii?

Update -

Dałem HockeyApp strzał, ale ma takie same ograniczenia jak iTunes dzienników zderzeniowych - nie mów mi stos na nieobsługiwany wyjątek C++.

+0

Czy używasz narzędzia innej firmy, takiego jak Crittercism, lub testflightapp, aby uzyskać zgłaszane awarie. Musisz przesłać plik dsym, a następnie oznaczy on awarie i wyświetli dokładną linię, w której nastąpiła awaria. Aby znaleźć plik dsym, możesz kliknąć plik .ipa prawym przyciskiem myszy w oknie organizatora lub gdziekolwiek go zapisałeś, a następnie kliknąć prawym przyciskiem myszy i pokazać zawartość. – Srikanth

+0

Nie używam żadnego narzędzia innej firmy. Czy możesz polecić? Ponadto podejrzewam, że te narzędzia mogą nie obsługiwać wyjątków C++ - czy znasz kogoś, kto to robi? – Mar0ux

+0

Nie jestem tego pewien. Ale tutaj masz inne pytanie podobne do twojego: http://stackoverflow.com/questions/11883069/how-to-decode-a-dsym-file-crash-log – Srikanth

Odpowiedz

21

To, co widzisz, to niefortunne dziwactwo AppKit i UIKit. Zarówno system iOS, jak i OS X, mają funkcję obsługi wyjątków w CFRunLoop, która przechwytuje wszystkie nieprzechwycone wyjątki. W systemie OS X program obsługi wyświetla wyjątek użytkownikowi z oknem dialogowym, ale w systemie iOS program obsługi po prostu ponownie wyrzuca wyjątek.

Cel C wyjątki wykonywanym NSException zapisują śledzenia wstecznego w obiekcie wyjątku tuż przed właściwym „rzut zachodzi”, w ramach [NSException init] (lub podobną metodą inicjatora). Niestety, wyjątki C++ nie robią tego samego. Normalnie, C wyjątek ++ ma ślad ponieważ runtime library wykryje, że nie ma catch i natychmiast wywołuje std::terminate, co z kolei wywołuje abort() pozostawiając stosu wywołań nienaruszone, tak jak poniżej:

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 

Jednak, gdy bieg obsługa wyjątków pętlowych wychwytuje wyjątek, wykonanie wznawia się normalnie w bloku catch. Oryginalny ślad śledzenia jest w związku z tym tracony. Ponowne wywołanie następnie wywołuje std::terminate, ale w tym momencie stos wywołań odzwierciedla procedurę obsługi wyjątku pętli uruchamiania.

Aby uzyskać ślad od C wyjątkiem ++ w tej sytuacji, trzeba wyrzucić przedmiot, który naśladuje NSException wyjątku i odczytuje stos wywołań w ramach jej konstruktora i upewnij się, że wszystkie wyjątki rzucane w kodzie zrobić to samo . std::exception nie robi tego, i wcale nie jest prawdopodobne, że użyje się dowolnego kodu innej firmy.

Złóż błąd w firmie Apple, prosząc o usunięcie obsługi wyjątków CFRunLoop lub przynajmniej udostępnij interfejs API do wyłączenia go. W tej chwili nie ma API, nawet SPI.

+1

Czy istnieje sposób na uzyskanie podstawowych informacji o wyjątkach (jak informacja w 'exception :: what()'), bez faktycznego wychwytywania wyjątku, w procedurze obsługi sygnału dla SIGABRT? – MechEthan

+1

Czy ktoś zgłosił błąd w Apple? W jaki sposób można rzucić wyjątek C++, który naśladuje wyjątek NSException? – Zammbi

+0

Złożyłem błąd w Apple. Otwarty odpowiednik radaru: http://www.openradar.me/radar?id=4943586562932736 – John

-2

wygląda wyjątek dzieje się od wewnątrz UIApplicationMain (...) Można spróbować konwersji pliku main.m do pliku main.mm, i w ten sposób:

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

ja nie faktycznie spróbować go jednak ...

0

Kontynuując Gwynne Raskind odpowiedzieć można uniknąć obsługi wyjątku CFRunLoop za pomocą GCD wysyłką połączenia do bloku C++ kod jednocześnie:

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

} 

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

Może być konieczne utworzenie własnych kolejek zamiast korzystania z pojedynczej kolejki współdzielonej.

Katastrofa log wygląda tak:

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

miałem szczęścia do poprawy odpowiedź mpipe3 użytkownika.

W mojej głównej metodzie używam try-catch, aby uzyskać wyjątek C++, a następnie wywołać dispatch_sync na dispatch_get_global_queue. Teraz pełne śledzenie stosu z numerami linii będzie wyświetlane w Crashlytics (menadżerze awarii).

int main(int argc, char *argv[]) { 

@autoreleasepool { 
    @try{ 
     return UIApplicationMain(argc, argv, nil, @"AppControllerClassName"); 
    } @catch (NSException *exception) {/* Catch any uncaught exceptions and print out a friendly call stack. Instead of an ugly memory addresses. */ 
     NSString * message = [NSString stringWithFormat:@"Uncaught exception %@ : %@\n %@", exception.name, exception.reason, [exception callStackSymbols]]; 
     [LogManager e:@"main.m" Message:message Exception: exception]; 
     @throw; 
    } @catch (...){ 
     //Attempt to get the correct stacktrace for C++ exceptions. 
     dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
      @throw; 
     }); 
    } 
    return 0; 
} 
} 

Działa tak, jak unika obsługi wyjątków CFRunLoop za pomocą GCD, aby wyrzucić wyjątek.

+0

Wygląda na to, że to rozwiązanie nie zawsze działa. Ale to najlepsze, co wymyśliłem. – Zammbi