Xcode / LLDB: Como obter informações sobre uma exceção que acabou de ser lançada?

83

OK, imagine que meu ponto de interrupção objc_exception_throwacabou de ser acionado. Estou sentado no prompt do depurador e quero obter mais informações sobre o objeto de exceção. Onde posso encontrar?

Karoy Lorentey
fonte
2
Lembre-se, a exceção acaba de ser levantada, sua descrição ainda não foi impressa no console.
Karoy Lorentey
Confira esta pergunta: stackoverflow.com/questions/711650
Nikolai Fetissov

Respostas:

162

O objeto de exceção é passado como o primeiro argumento para objc_exception_throw. O LLDB fornece $arg1.. $argnvariáveis ​​para se referir a argumentos na convenção de chamada correta, tornando simples imprimir os detalhes da exceção:

(lldb) po $arg1
(lldb) po [$arg1 name]
(lldb) po [$arg1 reason]

Certifique-se de selecionar o objc_exception_throwquadro na pilha de chamadas antes de executar esses comandos. Veja "Depuração Avançada e Sanitizador de Endereços" nos vídeos da sessão WWDC15 para ver como isso é realizado no palco.

Informação Desatualizada

Se você estiver no GDB, a sintaxe para se referir ao primeiro argumento depende das convenções de chamada da arquitetura em que você está executando. Se você estiver depurando em um dispositivo iOS real, o ponteiro para o objeto está registrado r0. Para imprimi-lo ou enviar mensagens para ele, use a seguinte sintaxe simples:

(gdb) po $r0
(gdb) po [$r0 name]
(gdb) po [$r0 reason]

No iPhone Simulator, todos os argumentos de função são passados ​​na pilha, portanto, a sintaxe é consideravelmente mais horrível. A expressão mais curta que eu poderia construir é *(id *)($ebp + 8). Para tornar as coisas menos dolorosas, sugiro o uso de uma variável de conveniência:

(gdb) set $exception = *(id *)($ebp + 8)
(gdb) po $exception
(gdb) po [$exception name]
(gdb) po [$exception reason]

Você também pode definir $exceptionautomaticamente sempre que o ponto de interrupção é acionado, adicionando uma lista de comandos aoobjc_exception_throw ponto de interrupção.

(Observe que em todos os casos que testei, o objeto de exceção também estava presente no eaxe se edxregistra no momento em que o ponto de interrupção atingiu. Não tenho certeza se esse será sempre o caso.)

Adicionado a partir do comentário abaixo:

No lldb , selecione o frame da pilha objc_exception_throwe insira este comando:

(lldb) po *(id *)($esp + 4)
Karoy Lorentey
fonte
6
Como alguém faria isso no lldb? Recebo um erro "erro: referência a 'id' é ambígua"
offex de
2
você pode fornecer a fonte desta informação? gostaria de ler mais sobre isso
João Nunes
3
Atualmente, os seguintes trabalhos para me antes do prólogo quando breaing em objc_exception_throw em LLDB : po *(id *)($esp + 4).
wbyoung
7
Isso funcionou! No entanto, não funcionou até que selecionei o frame de pilha 0 . ( objc_exception_throw).
funroll de
7
po $eaxfunciona para mim no simulador como pendente para o $r0dispositivo quando.
monkeydom de
10

em novos simuladores (iOS 8, 64 bits) xcode 6 im usando no quadro de exceção: objc_exception_throw

po $rax

em 32 bits:

po $eax

O que é rax?

Rax é um registrador de 64 bits que substitui o antigo eax

Como encontrar todos os registros?

register read

Wikipedia fonte

João Nunes
fonte
Hmm ... No Xcode 6.1, estou recebendo: (lldb) erro po $ rax: Não foi possível materializar: não foi possível ler o valor do registrador rax Errou em Executar, não foi possível PrepareToExecuteJITExpression
bradheintz
Simulador ou dispositivo @bradheintz? tentei fazer isso com 6.0.1
João Nunes
Você pode apontar e fornecer um link para sua fonte para isso? Obrigado!
Chris Conover,
Acabei de escrever em lldb: registrar leitura. Então, com essa informação, sabemos que o primeiro registro no quadro de exceção contém a mensagem de exceção.
João Nunes
Ok, encontrei alguns documentos: rax é um registrador de 64 bits: No modo longo de 64 bits você pode usar registradores de 64 bits (por exemplo, rax em vez de eax, rbx em vez de ebx, etc.)
João Nunes
6

No momento em que este artigo foi escrito, este post é meu principal hit no Google para: exceção de impressão lldb . Portanto, estou adicionando esta resposta para explicar o lldb e o x86_64.

Minhas tentativas de encontrar a exceção usando po $eaxfalharam com error: Couldn't materialize struct: Couldn't read eax (materialize). Outras tentativas descritas em documentos vinculados de respostas anteriores também falharam.

O segredo era que eu precisava primeiro clicar no objc_exception_throwquadro do meu tópico principal. O lldb não começa nesse quadro.

Em todos os meus exemplos de pesquisa e seguimento, esta entrada do blog foi a primeira a explicar as coisas de uma maneira que funcionou para mim. É mais moderno, tendo sido postado em agosto de 2012.

Jeff
fonte
1

Se você tiver uma instrução catch, coloque um ponto de interrupção lá e você poderá inspecionar o objeto de exceção nesse ponto.

Se você não tiver uma instrução catch, continue.

Você receberá uma mensagem em seu terminal como esta:

Encerrando aplicativo devido à exceção não capturada 'NSInvalidArgumentException', motivo: ' * - [__ NSPlaceholderDictionary initWithObjects: forKeys: count:]: tentativa de inserir objeto nulo de objetos [0]'

Contudo , você provavelmente está procurando uma maneira de inspecioná-lo sem continuar, já que perderá seu bom rastreamento de pilha quando o aplicativo for encerrado.

Para isso, parece que a resposta do Fnord é a melhor, mas não consegui fazê-la funcionar no LLDB.

funroll
fonte