A depuração do Xcode 4.2 não simboliza a chamada de pilha

140

Estou com um problema com a depuração do Xcode 4.2 em um simulador / dispositivo iOS 5. O código a seguir trava, conforme o esperado:

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

No iOS 4, recebo um rastreamento útil da pilha de números hexadecimais. Mas no iOS 5, isso me dá:

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

Obrigado.

cekisakurek
fonte

Respostas:

256

Nada que eu tentasse consertava isso (tentei os dois compiladores, os depuradores etc.) Após atualizar o XCode para a atualização do iOS 5, nenhum rastreamento de pilha parecia funcionar.

No entanto, eu encontrei uma solução alternativa eficaz - criando meu próprio manipulador de exceções (que também é útil por outros motivos). Primeiro, crie uma função que lida com o erro e a envie para o console (assim como qualquer outra coisa que você queira fazer com ele):

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

Em seguida, adicione o manipulador de exceções ao delegado do aplicativo:

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

É isso aí!

Se isso não funcionar, existem apenas dois motivos possíveis :

  1. Algo está sobrescrevendo sua NSSetUncaughtExceptionHandlerchamada (pode haver apenas um manipulador para todo o aplicativo). Por exemplo, algumas bibliotecas de terceiros definem seu próprio uncaughtExceptionHandler. Portanto, tente configurá-lo no final da sua didFinishLaunchingWithOptionsfunção (ou desativar seletivamente as bibliotecas de terceiros). Ou, melhor ainda, defina um ponto de interrupção simbólico NSSetUncaughtExceptionHandlerpara ver rapidamente quem está ligando. O que você pode querer fazer é modificar o atual, em vez de adicionar outro.
  2. Você não está realmente encontrando uma exceção (por exemplo, nãoEXC_BAD_ACCESS é uma exceção; crédito aos comentários de @Erik B, abaixo)
Zane Claes
fonte
1
Fico feliz em ouvi-lo :) Acho útil gravar o log de falha em um arquivo e solicitar ao usuário que o envie no próximo lançamento (apenas no modo de lançamento, para não atrapalhar a depuração). Isto deixa-me obter grandes relatórios de erros ... e os usuários sabem que o seu problema está sendo tratado :)
Zane Claes
2
Isso não parece funcionar - a uncaughtExceptionHandlerrotina nunca é invocada.
Hot Licks
1
Você pode ser mais específico sobre como usá-lo? Não parece funcionar para mim.
Danut Pralea
1
Muito triste xCode não exibe isso para nós.
Authman Apatira
1
Muito apreciado! É desconcertante que a Apple não implemente esse tipo de funcionalidade rudimentar no IDE.
devios1
110

Existe uma opção útil para adicionar um ponto de interrupção de exceção (usando o + na parte inferior do navegador de ponto de interrupção). Isso será interrompido em qualquer exceção (ou você pode definir condições). Não sei se essa opção é nova na versão 4.2 ou se finalmente a notei tentando solucionar o problema dos símbolos ausentes.

Depois de atingir esse ponto de interrupção, você pode usar o Navegador de Depuração para navegar na pilha de chamadas, examinar variáveis ​​etc., como de costume.

Se você deseja uma pilha de chamadas simbolizada adequada para copiar / colar ou algo semelhante, o backdrace gdb funcionará bem a partir daí:

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

(etc)

WiseOldDuck
fonte
3
Isso funciona perfeitamente para mim até agora. O depurador pára na linha de colisão agora, sem a necessidade do backtrace.
Tim
1
Isso funciona perfeitamente para mim também. Muito obrigado, eu estava enlouquecendo sem esse ponto de interrupção ...
William Denniss
+1 para isso funcionou. Ele não lhe dá essa mensagem de erro agradável explicando o motivo da exceção, mas é um começo ...
Nicu Surdu
você é meu HotD @WiseOldDuck.
Maverick1st
Isso restaura o comportamento esperado para mim. NOTA: Lembre-se também de adicionar novamente este ponto de interrupção em novos projetos!
MechEthan
46

Há um novo recurso no depurador. Você pode definir um ponto de interrupção sempre que uma exceção for lançada e interromper a execução ali, exatamente como costumava acontecer no 4.0.

No "Navegador do ponto de interrupção", adicione um "Ponto de interrupção de exceção" e pressione "Concluído" na janela pop-up de opções.

Isso é tudo!

PS: Em alguns casos, seria melhor interromper apenas as exceções de Objective-C.

Pedro
fonte
Definitivamente, esta é a solução para mim.
Bradgonesurfing 3/11
Este foi o problema para mim. Um colega e eu compartilhamos o mesmo projeto do Xcode e perguntei se ele estava tendo o problema, e ele não estava. A diferença foi que seu projeto estava quebrando em Objective-C exceções (objc_exception_throw)
horseshoe7
Você acabou de ajudar a encontrar um bug não rastreável. Muito obrigado. Eu tenho procurado em todos os lugares por algo assim.
Rjgonzo
1
Isso funciona como um encanto! Era exatamente isso que eu estava procurando, isso é melhor do que adicionar o manipulador de exceções, pois adicionar o ponto de interrupção pode levá-lo exatamente onde a exceção foi lançada; o manipulador de exceções funciona, mas é só dar uma idéia.
Im8bit 8/08/12
21

Aqui está mais uma solução, não tão elegante quanto a anterior, mas se você não adicionou pontos de interrupção ou manipuladores de exceção, pode ser apenas um caminho a percorrer.
Quando o aplicativo falha, e você obtém sua pilha bruta de primeira chamada (em números hexadecimais), digite no console do Xcode info line *hex(não se esqueça do 0xespecificador de estrela e hexadecimal), por exemplo:

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

Se você estiver usando lldb , poderá digitar image lookup -a hex(sem estrela nesta situação) e obter uma saída semelhante.

Com esse método, você pode passar da parte superior da pilha de lançamento (haverá cerca de 5-7 propagadores de exceção do sistema) para a sua função que causou uma falha e determinar o arquivo e a linha de código exatos.

Além disso, para um efeito semelhante, você pode usar o utilitário atos no terminal, apenas digite:

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

e você obtém o rastreamento de pilha simbolizado (pelo menos para funções com símbolos de depuração). Esse método é mais preferível, porque você não tem para cada chamada de endereço info line, apenas copie os endereços da saída do console e cole-os no terminal.

Alexander
fonte
9

Você pode adicionar um ponto de interrupção de exceção (usando o + na parte inferior do navegador de ponto de interrupção) e adicionar a ação bt a ele (clique no botão Adicionar ação, selecione Comando do depurador, insira "bt" no campo de texto). Isso exibirá o rastreamento da pilha assim que uma exceção for lançada.

MonsieurDart
fonte
6

Este é um problema comum, não obtendo rastreamentos de pilha no 4.2. Você pode tentar trocar entre LLDB e GDB para ver se obtém melhores resultados.

Arquive um relatório de bug aqui.

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

EDITAR:

Acredito que se você voltar ao LLVM GCC 4.2, não verá isso acontecer. Você pode perder os recursos necessários.

logancautrell
fonte
Sim, tentei mudar os compiladores, mas o problema permanece. mas graças qualquer maneira :)
cekisakurek
Ele sugeriu trocar debuggers, não compiladores.
bames53
1
FYI: Nesse caso, não tem nada a ver com a versão do compilador ou depurador que você está usando. Esta é uma alteração na saída do console do iOS.
Clarkcox3
Interessante o quanto as experiências disso variam - acho que existem alguns problemas. Não foi possível fazer com que o depurador parasse no ponto de interrupção da exceção. Mudar de GDB para LLDB resolveu o problema.
Matt
6

Use este código em sua função principal:

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
fonte
Isso não parece funcionar com storyboards. 2012-06-04 20: 34: 52.211 Problemas [1944: 207] O representante do aplicativo deve implementar a propriedade window se desejar usar um arquivo principal do storyboard. 2012-06-04 20: 34: 52,213 Problemas [1944: 207] são esperados Applications ter um controlador de vista raiz no final da inicialização do aplicativo
macasas
6

No prompt do console de depuração do Xcode, digite:

image lookup -a 0x1234

E isso mostrará algo como:

  Address: MyApp[0x00018eb0] (MyApp.__TEXT.__text + 91088)
  Summary: MyApp`-[MyViewController viewDidAppear:] + 192 at MyViewController.m:202
Matt Connolly
fonte
Obrigado, eu estava realmente procurando por isso. Surpreendentemente, não há atalho para exibir toda a "pilha de chamadas de primeiro lance" como uma pilha de chamadas, pois acho que um script lldb do Python pode ser facilmente escrito.
Ilya
1

Ativar novamente 'Compilar para o Thumb' (configuração de depuração) funcionou para mim.

Bradweiser86
fonte