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?

Karoy Lorentey
fuente
2
Recuerde, se acaba de plantear la excepción, su descripción aún no se ha impreso en la consola.
Karoy Lorentey
Echa un vistazo a esta pregunta: stackoverflow.com/questions/711650
Nikolai Fetissov

Respuestas:

162

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:

(lldb) po *(id *)($esp + 4)
Karoy Lorentey
fuente
6
¿Cómo se haría esto en lldb? Recibo un error "error: la referencia a 'id' es ambigua"
offex
2
¿Puede proporcionar la fuente de esta información? Me gustaría leer más al respecto
João Nunes
3
Actualmente las siguientes obras por mí ante el prólogo cuando breaing en objc_exception_throw en LLDB : po *(id *)($esp + 4).
wbyoung
7
¡Esto funcionó! Sin embargo, no funcionó hasta que seleccioné el marco de pila 0 . ( objc_exception_throw).
funroll
7
po $eaxfunciona para mí en el simulador como colgante para $r0cuando esté en el dispositivo.
monkeydom
10

en nuevos simuladores (iOS 8, 64 bits) xcode 6 estoy usando en el marco de excepción: objc_exception_throw

po $rax

en 32 bits:

po $eax

¿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

João Nunes
fuente
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.

Jeff
fuente
1

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.

funroll
fuente