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?
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:
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.
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.
Respostas:
O objeto de exceção é passado como o primeiro argumento para
objc_exception_throw
. O LLDB fornece$arg1
..$argn
variáveis para se referir a argumentos na convenção de chamada correta, tornando simples imprimir os detalhes da exceção:Certifique-se de selecionar o
objc_exception_throw
quadro 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: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
$exception
automaticamente 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
eax
e seedx
registra 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_throw
e insira este comando:(lldb) po *(id *)($esp + 4)
fonte
objc_exception_throw
em LLDB :po *(id *)($esp + 4)
.objc_exception_throw
).po $eax
funciona para mim no simulador como pendente para o$r0
dispositivo quando.em novos simuladores (iOS 8, 64 bits) xcode 6 im usando no quadro de exceção:
objc_exception_throw
em 32 bits:
O que é rax?
Rax é um registrador de 64 bits que substitui o antigo eax
Como encontrar todos os registros?
register read
Wikipedia fonte
fonte
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 $eax
falharam comerror: 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_throw
quadro 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.
fonte
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.
fonte