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.

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)

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)


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.


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.


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.


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.


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;
}

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

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.