Debugowanie Xcode 4.2 nie symbolizuje wywołania stosu

140

Mam problem z debugowaniem Xcode 4.2 w symulatorze / urządzeniu iOS 5. Następujący kod ulega awarii zgodnie z oczekiwaniami:

NSArray *arr=[NSArray array];
[arr objectAtIndex:100];

W iOS 4 otrzymuję przydatne ślady stosu liczb szesnastkowych. Ale w iOS 5 daje mi tylko:

*** First throw call stack:
(0x16b4052 0x1845d0a 0x16a0674 0x294c 0x6f89d6 0x6f98a6 0x708743 0x7091f8 0x7fcaa9 0x2257fa9 0x16881c5 0x15ed022 0x15eb90a 0x15eadb4 0x15eaccb 0x6f02a7 0x6faa93 0x2889 0x2805)

Dzięki.

cekisakurek
źródło

Odpowiedzi:

256

Nic, co próbowałem, nie naprawiłoby tego (wypróbowałem oba kompilatory, oba debuggery itp.) Po uaktualnieniu XCode do aktualizacji iOS 5, żadne ślady stosu wydawały się nie działać.

Jednak znalazłem skuteczne obejście - utworzenie własnego modułu obsługi wyjątków (który jest również przydatny z innych powodów). Najpierw utwórz funkcję, która obsłuży błąd i wyśle ​​ją do konsoli (a także wszystkiego, co chcesz z nią zrobić):

void uncaughtExceptionHandler(NSException *exception) {
    NSLog(@"CRASH: %@", exception);
    NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    // Internal error reporting
}

Następnie dodaj procedurę obsługi wyjątków do delegata aplikacji:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{   
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
    // Normal launch stuff
}

Otóż ​​to!

Jeśli to nie zadziała, są tylko dwa możliwe powody :

  1. Coś nadpisuje twoje NSSetUncaughtExceptionHandlerwywołanie (może być tylko jeden program obsługi dla całej aplikacji). Na przykład niektóre biblioteki innych firm ustawiają własne uncaughtExceptionHandler. Spróbuj więc ustawić go na końcu swojej didFinishLaunchingWithOptionsfunkcji (lub selektywnie wyłącz biblioteki innych firm). Lub jeszcze lepiej, ustaw symboliczny punkt przerwania, NSSetUncaughtExceptionHandleraby szybko zobaczyć, kto do niego dzwoni. To, co możesz chcieć zrobić, to zmodyfikować bieżącą, zamiast dodawać kolejną.
  2. Nie jesteś rzeczywiście napotyka wyjątek (na przykład, EXC_BAD_ACCESSto nie wyjątek, kredyt komentarze @Erik B, poniżej)
Zane Claes
źródło
1
Miło to słyszeć :) Uważam, że warto zapisać dziennik awarii do pliku i poprosić użytkownika o przesłanie go przy następnym uruchomieniu (tylko w trybie wydania, aby nie przeszkadzać w debugowaniu). Dzięki temu otrzymuję świetne raporty o błędach ... a użytkownicy wiedzą, że ich problem jest rozwiązywany :)
Zane Claes,
2
To nie wydaje się działać - uncaughtExceptionHandlerprocedura nigdy nie jest wywoływana.
Hot Licks
1
Czy możesz bardziej szczegółowo określić, jak go używać? Wydaje mi się, że to nie działa.
Danut Pralea
1
Dość smutne xCode nie wyświetla tego dla nas.
Authman Apatira
1
Bardzo cenione! Zaskakujące jest to, że Apple nie implementuje tego rodzaju podstawowych funkcji w IDE.
devios1
110

Istnieje przydatna opcja dodania punktu przerwania wyjątku (za pomocą + u dołu nawigatora punktów przerwania). Spowoduje to przerwanie w przypadku każdego wyjątku (lub możesz ustawić warunki). Nie wiem, czy ten wybór jest nowy w 4.2, czy dopiero w końcu zauważyłem, że próbuję obejść problem z brakującymi symbolami.

Po osiągnięciu tego punktu przerwania możesz użyć nawigatora debugowania, aby nawigować po stosie wywołań, badać zmienne itp. Jak zwykle.

Jeśli potrzebujesz symbolicznego stosu wywołań odpowiedniego do kopiowania / wklejania itp., GDB backtrace będzie działał dobrze:

(gdb) bt
#0  0x01f84cf0 in objc_exception_throw ()
#1  0x019efced in -[NSObject doesNotRecognizeSelector:] ()

(itp)

WiseOldDuck
źródło
3
Jak dotąd działa to idealnie dla mnie. Debuger zatrzymuje się teraz na linii powodującej awarię, bez potrzeby śledzenia wstecznego.
Tim
1
U mnie to też działa doskonale. Dziękuję bardzo, oszalałem bez tego punktu przerwania ...
William Denniss,
+1 za to zadziałało. Nie daje tak miłego komunikatu o błędzie wyjaśniającego przyczynę wyjątku, ale to początek ...
Nicu Surdu Kwietnia
jesteś moim HotD @WiseOldDuck.
Maverick 1
To przywraca mi oczekiwane zachowanie. UWAGA: Pamiętaj również, aby ponownie dodać ten punkt przerwania w nowych projektach!
MechEthan
46

Debugger ma nową funkcję. Możesz ustawić punkt przerwania za każdym razem, gdy zostanie zgłoszony wyjątek i zatrzymać wykonywanie w tym miejscu, tak jak miało to miejsce w 4.0.

W „Nawigatorze punktów przerwania” dodaj „Punkt przerwania wyjątku” i po prostu naciśnij „Gotowe” w wyskakującym okienku opcji.

To wszystko!

PS: W niektórych przypadkach lepiej byłoby przerwać tylko dla wyjątków Cel-C.

Pedro
źródło
Zdecydowanie jest to rozwiązanie dla mnie.
bradgonesurfing
To był problem dla mnie. Współpracownik i ja dzielimy ten sam projekt Xcode i zapytałem go, czy ma problem, a nie był. Różnica polegała na tym, że jego projekt łamał się przy wyjątkach dotyczących celu-c (objc_exception_throw)
horseshoe7
Właśnie pomogłeś znaleźć niewykrywalny błąd. Dziękuję bardzo. Szukałem wszędzie czegoś takiego.
rjgonzo,
1
To działa jak urok! To było dokładnie to, czego szukałem, jest to lepsze niż dodanie programu obsługi wyjątków, ponieważ dodanie punktu przerwania może zabrać Cię dokładnie tam, gdzie został zgłoszony wyjątek, program obsługi wyjątków działa, ale po prostu daje pomysł.
im8bit
21

Oto jeszcze jedno rozwiązanie, nie tak eleganckie jak poprzednie, ale jeśli nie dodałeś punktów przerwania lub obsługi wyjątków, może to być tylko jedna droga.
Gdy aplikacja ulegnie awarii i otrzymasz surowy stos wywołań pierwszego rzutu (w liczbach szesnastkowych), wpisz w konsoli Xcode info line *hex(nie zapomnij o 0xspecyfikatorze gwiazdy i szesnastkowym), na przykład:

(gdb) info line *0x2658
Line 15 of "path/to/file/main.m" starts at address 0x25f2 <main+50>
and ends at 0x267e <main+190>.

Jeśli używasz lldb , możesz wpisać image lookup -a hex(w tej sytuacji bez gwiazdki), a otrzymasz podobny wynik.

Dzięki tej metodzie możesz przejść od góry stosu rzutów (będzie około 5-7 propagatorów wyjątków systemowych) do funkcji, która spowodowała awarię i określić dokładny plik i wiersz kodu.

Aby uzyskać podobny efekt, możesz użyć narzędzia atos w terminalu, po prostu wpisz:

atos -o path/to/AplicationBundle.app/Executable 0xAdress1 0xAdress2 0xAdress3 ...

i otrzymujesz symboliczny ślad stosu (przynajmniej dla funkcji masz symbole debugowania). Ta metoda jest bardziej preferowana, ponieważ nie masz dla każdego wywołania adresu info line, po prostu skopiuj adresy z wyjścia konsoli i wklej je do terminala.

Alexander
źródło
9

Możesz dodać punkt przerwania wyjątku (za pomocą + u dołu nawigatora punktów przerwania) i dodać do niego akcję bt (kliknij przycisk Dodaj akcję, wybierz polecenie Debugera, wpisz „bt” w polu tekstowym). Spowoduje to wyświetlenie śladu stosu, gdy tylko zostanie zgłoszony wyjątek.

MonsieurDart
źródło
6

Jest to częsty problem, który nie pozwala na uzyskanie śladów stosu w wersji 4.2. Możesz spróbować zamienić się między LLDB i GDB, aby sprawdzić, czy uzyskasz lepsze wyniki.

Zgłoś błąd tutaj.

http://developer.apple.com/bugreporter/

EDYTOWAĆ:

Wierzę, że jeśli przełączysz się z powrotem na LLVM GCC 4.2, nie zobaczysz tego. Możesz jednak stracić funkcje, których potrzebujesz.

logancautrell
źródło
tak, próbowałem przełączyć kompilatory, jednak problem pozostaje. ale i tak dzięki :)
cekisakurek
Zasugerował zmianę debuggerów, a nie kompilatorów.
bames53
1
FYI: W tym przypadku nie ma to nic wspólnego z wersją kompilatora lub debugera, którego używasz. Jest to zmiana w danych wyjściowych konsoli z systemu iOS.
clarkcox3
Ciekawe, jak różne są doświadczenia z tym związane - myślę, że jest kilka problemów. Nie udało mi się sprawić, aby debugger zatrzymał się w punkcie przerwania wyjątku. Przejście z GDB na LLDB rozwiązało problem.
Matt
6

Użyj tego kodu w swojej głównej funkcji:

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    int retVal;
    @try {
        retVal = UIApplicationMain(argc, argv, nil, nil);
    }
    @catch (NSException *exception) {
        NSLog(@"CRASH: %@", exception);
        NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
    }
    @finally {
        [pool release];
    }
    return retVal;
}
wilson lin
źródło
Wydaje się, że nie działa to w przypadku scenorysów. 2012-06-04 20: 34: 52.211 Problemy [1944: 207] Delegat aplikacji musi zaimplementować właściwość okna, jeśli chce użyć głównego pliku scenorysu. 2012-06-04 20: 34: 52.213 Problemy [1944: 207] Oczekuje się, że aplikacje będą miały kontroler widoku root pod koniec uruchamiania aplikacji
macasas
6

W konsoli debugowania Xcode wpisz:

image lookup -a 0x1234

I pokaże ci coś takiego:

  Address: MyApp[0x00018eb0] (MyApp.__TEXT.__text + 91088)
  Summary: MyApp`-[MyViewController viewDidAppear:] + 192 at MyViewController.m:202
Matt Connolly
źródło
Dzięki, naprawdę tego szukałem. Zaskakujące jest to, że nie ma skrótu do wyświetlania całego "stosu wywołań pierwszego rzutu" jako stosu wywołań, ponieważ myślę, że skrypt lldb w Pythonie można łatwo napisać.
Ilya
1

Włączenie „Kompiluj dla Thumb” (konfiguracja debugowania) działało dla mnie.

Bradweiser86
źródło