Xcode / LLDB: ¿Cómo obtener información sobre una excepción que se acaba de lanzar?
83
Bien, imagina que mi punto de interrupción objc_exception_throwacaba de activarse. Estoy sentado en el indicador del depurador y quiero obtener más información sobre el objeto de excepción. ¿Donde lo encuentro?
El objeto de excepción se pasa como primer argumento a objc_exception_throw. LLDB proporciona $arg1... $argnvariables para hacer referencia a argumentos en la convención de llamada correcta, lo que simplifica la impresión de los detalles de la excepción:
(lldb) po $arg1
(lldb) po [$arg1 name]
(lldb) po [$arg1 reason]
Asegúrese de seleccionar el objc_exception_throwmarco en la pila de llamadas antes de ejecutar estos comandos. Vea "Depuración avanzada y desinfección de direcciones" en los videos de la sesión WWDC15 para ver cómo se realiza en el escenario.
Información desactualizada
Si está en GDB, la sintaxis para hacer referencia al primer argumento depende de las convenciones de llamada de la arquitectura en la que está ejecutando. Si está depurando en un dispositivo iOS real, el puntero al objeto está registrado r0. Para imprimirlo o enviarle mensajes, use la siguiente sintaxis simple:
(gdb) po $r0
(gdb) po [$r0 name]
(gdb) po [$r0 reason]
En el iPhone Simulator, todos los argumentos de las funciones se pasan a la pila, por lo que la sintaxis es considerablemente más horrible. La expresión más corta que pude construir para llegar a él es *(id *)($ebp + 8). Para hacer las cosas menos dolorosas, sugiero usar una variable de conveniencia:
(gdb) set $exception = *(id *)($ebp + 8)
(gdb) po $exception
(gdb) po [$exception name]
(gdb) po [$exception reason]
También puede establecer $exceptionautomáticamente cada vez que se activa el punto de interrupción agregando una lista de comandos al objc_exception_throwpunto de interrupción.
(Tenga en cuenta que en todos los casos que probé, el objeto de excepción también estaba presente en los registros eaxy edxen el momento en que se produjo el punto de interrupción. Sin embargo, no estoy seguro de que siempre sea así).
Agregado del comentario a continuación:
En lldb , seleccione el marco de pila para objc_exception_throwy luego ingrese este comando:
Hmm ... En Xcode 6.1, obtengo: (lldb) po $ rax error: No se pudo materializar: no se pudo leer el valor del registro rax Se produjo un error en Execute, no se pudo PrepareToExecuteJITExpression
bradheintz
@bradheintz simulador o dispositivo? Probé esto con 6.0.1
João Nunes
¿Puede proporcionar un enlace a su fuente para eso? ¡Gracias!
Chris Conover
Acabo de escribir en lldb: register read. Luego, con esta información, sabemos que el primer registro en el marco de excepción contiene el mensaje de excepción.
João Nunes
Ok, encontré algunos documentos: rax es un registro de 64 bits: en el modo largo de 64 bits puede usar registros de 64 bits (por ejemplo, rax en lugar de eax, rbx en lugar de ebx, etc.)
João Nunes
6
En el momento de escribir este artículo, esta publicación es mi principal éxito de Google para: excepción de impresión lldb . Por lo tanto, estoy agregando esta respuesta para dar cuenta de lldb y x86_64.
Mis intentos de encontrar la excepción usando po $eaxfallaron con error: Couldn't materialize struct: Couldn't read eax (materialize). Otros intentos descritos en documentos vinculados de respuestas anteriores también fallaron.
La clave era que primero tenía que hacer clic en el objc_exception_throwmarco de mi hilo principal. lldb no comienza en ese marco.
En todas mis búsquedas y siguientes ejemplos, esta entrada de blog fue la primera en explicar las cosas de una manera que funcionó para mí. Es más moderno, se publicó en agosto de 2012.
Si tiene una declaración de captura, coloque un punto de interrupción allí y podrá inspeccionar el objeto de excepción en ese punto.
Si no tiene una declaración de captura, continúe.
Recibirás un mensaje en tu terminal como este:
Finalizando la aplicación debido a una excepción no detectada 'NSInvalidArgumentException', motivo: ' * - [__ NSPlaceholderDictionary initWithObjects: forKeys: count:]: intento de insertar ningún objeto de los objetos [0]'
Sin embargo , probablemente esté buscando una forma de inspeccionarlo sin continuar, ya que perderá el buen seguimiento de la pila cuando finalice la aplicación.
Por eso, parece que la respuesta de Fnord es la mejor, pero no pude hacer que funcionara en LLDB.
Respuestas:
El objeto de excepción se pasa como primer argumento a
objc_exception_throw
. LLDB proporciona$arg1
...$argn
variables para hacer referencia a argumentos en la convención de llamada correcta, lo que simplifica la impresión de los detalles de la excepción:Asegúrese de seleccionar el
objc_exception_throw
marco en la pila de llamadas antes de ejecutar estos comandos. Vea "Depuración avanzada y desinfección de direcciones" en los videos de la sesión WWDC15 para ver cómo se realiza en el escenario.Información desactualizada
Si está en GDB, la sintaxis para hacer referencia al primer argumento depende de las convenciones de llamada de la arquitectura en la que está ejecutando. Si está depurando en un dispositivo iOS real, el puntero al objeto está registrado
r0
. Para imprimirlo o enviarle mensajes, use la siguiente sintaxis simple:En el iPhone Simulator, todos los argumentos de las funciones se pasan a la pila, por lo que la sintaxis es considerablemente más horrible. La expresión más corta que pude construir para llegar a él es
*(id *)($ebp + 8)
. Para hacer las cosas menos dolorosas, sugiero usar una variable de conveniencia:(gdb) set $exception = *(id *)($ebp + 8) (gdb) po $exception (gdb) po [$exception name] (gdb) po [$exception reason]
También puede establecer
$exception
automáticamente cada vez que se activa el punto de interrupción agregando una lista de comandos alobjc_exception_throw
punto de interrupción.(Tenga en cuenta que en todos los casos que probé, el objeto de excepción también estaba presente en los registros
eax
yedx
en el momento en que se produjo el punto de interrupción. Sin embargo, no estoy seguro de que siempre sea así).Agregado del comentario a continuación:
En lldb , seleccione el marco de pila para
objc_exception_throw
y luego ingrese este comando:(lldb) po *(id *)($esp + 4)
fuente
objc_exception_throw
en LLDB :po *(id *)($esp + 4)
.objc_exception_throw
).po $eax
funciona para mí en el simulador como colgante para$r0
cuando esté en el dispositivo.en nuevos simuladores (iOS 8, 64 bits) xcode 6 estoy usando en el marco de excepción:
objc_exception_throw
en 32 bits:
¿Qué es rax?
Rax es un registro de 64 bits que reemplaza al antiguo eax
¿Cómo encontrar todos los registros?
register read
Fuente wikipedia
fuente
En el momento de escribir este artículo, esta publicación es mi principal éxito de Google para: excepción de impresión lldb . Por lo tanto, estoy agregando esta respuesta para dar cuenta de lldb y x86_64.
Mis intentos de encontrar la excepción usando
po $eax
fallaron conerror: Couldn't materialize struct: Couldn't read eax (materialize)
. Otros intentos descritos en documentos vinculados de respuestas anteriores también fallaron.La clave era que primero tenía que hacer clic en el
objc_exception_throw
marco de mi hilo principal. lldb no comienza en ese marco.En todas mis búsquedas y siguientes ejemplos, esta entrada de blog fue la primera en explicar las cosas de una manera que funcionó para mí. Es más moderno, se publicó en agosto de 2012.
fuente
Si tiene una declaración de captura, coloque un punto de interrupción allí y podrá inspeccionar el objeto de excepción en ese punto.
Si no tiene una declaración de captura, continúe.
Recibirás un mensaje en tu terminal como este:
Finalizando la aplicación debido a una excepción no detectada 'NSInvalidArgumentException', motivo: ' * - [__ NSPlaceholderDictionary initWithObjects: forKeys: count:]: intento de insertar ningún objeto de los objetos [0]'
Sin embargo , probablemente esté buscando una forma de inspeccionarlo sin continuar, ya que perderá el buen seguimiento de la pila cuando finalice la aplicación.
Por eso, parece que la respuesta de Fnord es la mejor, pero no pude hacer que funcionara en LLDB.
fuente