como_ inyectar_depurar_codigo_en_xcode

Cómo depurar e inyectar código en Xcode

Un proceso necesario y habitual mientras se desarrollan aplicaciones es la depuración e inyección de código, es fácil de utilizar pero muchas veces nos quedamos en la superficie y no profundizamos en las herramientas que dispone nuestro depurador de código.

Habiendo conocido las bases de cómo depurar con lldb en mac, a continuación seguiremos aprendiendo sobre este depurador, pero esta vez sobre un proyecto en Xcode, para ello dispondremos de un proyecto base el cual tiene una serie de problemas, de los cuales vamos a aprender a depurar y corregirlos.

El siguiente artículo forma parte de la colección de artículos acerca de lldb, podrás ver el resto aquí.

Descargando el proyecto

Vamos a simular que el equipo de QA de nuestra empresa, nos ha solicitado la corrección de dos errores que encontraron en nuestra aplicación.

El listado de errores que nos reportaron es el siguiente:

  • BUG-001: El rectángulo coloreado no se está mostrando centrado.
  • BUG-002: Al pulsar varias veces sobre el rectángulo coloreado, la aplicación se cierra.

Primero descargamos el proyecto desde GitHub. Este proyecto contiene los bugs que nos reportó nuestro equipo de QA, vamos a encontrarlos y solucionarlos, todo esto sin tener que volver a compilar el proyecto una y otra vez.

Importante

Antes de ejecutar la aplicación, vamos a modificar el esquema de la aplicación. Dentro de la pestaña run > diagnostics activamos Malloc Stack Logging por consiguiente en el selector que se activa a continuación, seleccionamos All Allocation And Free History. El uso que tiene esta configuración se explicará más adelante.

Identificación de los errores

Tras ejecutar la aplicación se muestra un rectángulo azul sobre un fondo blanco, este parece estar desplazado hacia la derecha (BUG-001), tras pulsar varias veces el rectángulo este va cambiando de color, hasta que la aplicación termina abruptamente su ejecución (BUG-002).

Al parecer QA tenía razón, ahora vamos a compilar de nuevo y corregir estos errores de una vez por todas!

Corrigiendo los errores junto a lldb

Para corregir estos errores, utilizaremos lldb como herramienta, no sólo de depuración sino, como herramienta de inyección de código «en caliente«.

BUG-001: Inspeccionando constraints

Para poder solucionar este error, vamos a utilizar el inspector de jerarquías, esta herramienta nos permite tomar una foto de la interfaz actual de la aplicación, permitiéndonos conocer qué está componiendo la pantalla actual y cómo están posicionados los componentes de esta, todo ello de una forma muy intuitiva.

Tras activar el inspector de jerarquías, se muestran varias opciones:

  • Show/Hide Clipped Content: Muestra u oculta los elementos/secciones de los elementos de la interfaz que están ocultos por su contenedor (recortados).
  • Show/Hide Constraints: Muestra u oculta una representación gráfica de las constraints presentes en los elementos de la interfaz.
  • Adjust View Mode: Cambia el modo de visualización del visor de jerarquías, permitiendo ver elementos solapados a simple vista.
  • Canvas Background: Cambia el color de fondo del visor.
blank

Pulsamos el botón Show Clipped Content, entonces se nos muestra que parte de nuestro rectángulo coloreado se sale por el lado derecho de la pantalla.

blank

¿Qué nos quiere decir esto? Hay dos posibles explicaciones; el rectángulo tiene un ancho demasiado grande para entrar en pantalla, o la constraint derecha tiene algún problema.

Para descartar la primera opción hacemos click sobre el rectángulo coloreado y comprobaremos que NO tiene un ancho fijado mediante constraints.

blank

Otra opción para ver las constraints activas en la vista es pulsar el botón Show Constraints, muestra todas aquellas constraints que tenga activas la vista seleccionada.

Con este método podemos ver, que tanto la constraint derecha como la inferior parecen tener algún problema.

Vamos a solucionar los problemas que tienen ambas constraints, para ello SIN DETENER la ejecución de la aplicación, seleccionamos la constraint derecha del rectángulo coloreado y vamos a Edit > Copy, a continuación en la consola lldb de depuración (situada en la parte inferior de Xcode), escribiremos el comando po (Print Object), seguido del contenido que hemos copiado previamente, finalmente pulsamos intro para ejecutar el comando y se mostraría algo similar a esto:

(lldb) po ((NSLayoutConstraint *)0x600000d990e0) 
<NSLayoutConstraint:0x600000d990e0 UIView:0x154b4ab10.trailing == UILayoutGuide:0x60000178f9c0'UIViewSafeAreaLayoutGuide'.trailing + 20 (active)>

Lo que podemos concluir de aquí es, que tenemos una constraint trailing (por la derecha) + 20, lo que encaja con el trozo de rectángulo fuera de pantalla que vimos antes.

Para modificar este valor sin detener la ejecución (en caliente), vamos a ejecutar dentro de la misma consola el commando e (expression) más el valor que copiamos antes, y modificamos su propiedad constant, de la siguiente forma:

(lldb) e ((NSLayoutConstraint *)0x600000d990e0).constant = -20

Una vez ejecutada esta orden, nuestra contraint tiene el valor -20.

Para poder visualizar en pantalla el resultado de nuestra constante con su nuevo valor, ejecutamos:

e -l objc -O -- [CATransaction flush]

-l objc indica que vamos necesitamos cargar la librería de objetos de Objective-C.

-O parámetro de optimización de expresiones antes de su ejecución.

-- indica el final de los parámetros de lldb y el comienzo de los parámetros del comando de expresión.

[CATransaction flush] expresión en Objective-C que fuerza a Core Animation a dibujar de nuevo toda la pantalla.

Dentro del inspector de jerarquías nada ha cambiado, pero si vemos en el simulador, la posición del rectángulo habrá cambiado.

Ahora ya tenemos corregido (en caliente) el bug, ya sólo nos falta encontrar el origen, para ello utilizando el panel lateral (derecho) mientras tenemos seleccionada la constraint que hemos corregido, vamos a la pestaña Object Inspector.

Aquí entra en juego la opción del esquema que modificamos al inicio Malloc Stack Logging, mostrándonos en la parte inferior una sección llamada Backtrace, la cual nos indica el ciclo de vida que ha tenido el objeto que tenemos seleccionado (la constraint en nuestro caso). Haciendo click en ViewController.setupView() podemos encontrar donde se creó la constraint y modificar el valor constante de 20 a -20, dando así por solucionado el error para siempre.

Para volver a la ejecución, pulsamos el botón Continue program execution situado en la parte inferior de Xcode, con un símbolo de ▯▶

Os invito a seguir los mismos pasos para corregir la contraint inferior.

BUG-002: Inyección de expresiones

Este es el mensaje que encontramos al reproducir el error Fatal error: Index out of range, en el método updateCenteredViewBackgroundColor().

Para solucionarlo vamos a utilizar los puntos de ruptura y algunas expresiones de lldb.

Una vez la aplicación se esté ejecutando de nuevo, añadimos un punto de ruptura en la línea 63 y con botón secundario sobre el item creado, lo editamos.

Dentro del diálogo de edición añadimos una acción del tipo Debugger Command, lo que quiere decir que inyectamos/ejecutamos código mediante lldb cuándo la aplicación llegue al punto de ruptura.

Queremos evitar que se ejecute la línea actual, para ello escribimos el comando

thread jump --by 1

lo que hará que la ejecución de un salto por valor de una línea de código, evitando así ejecutar la llamada al método updateCenteredViewBackgroundColor().

Antes de terminar marcamos la casilla Automatically continue after evaluating actions, para que el punto de ruptura no detenga la ejecución y continue tras inyectar el comando.

blank

Tras esto podemos interactuar con el rectángulo y veremos que no se cierra la aplicación, confirmamos que el problema se encuentra en ese punto aunque ahora tenemos otro bug nuevo, el color de fondo no cambia…

Ya sabemos cuál es el problema y de dónde viene, así que creamos un nuevo punto de ruptura en la línea 68 y al editarlo de la misma forma que el anterior, introducimos el siguiente comando

e if currentColorIndex >= colors.count { currentColorIndex = 0 }

(recuerda marcar la casilla Automatically continue after evaluating actions).

blank

Finalmente ya no necesitamos el punto de ruptura de la línea 63 así que lo desactivamos y volvemos a interactuar con nuestra aplicación, descubriendo así que esta, ya funciona bien y no tenemos ningún error fatal que termine con la ejecución.

Una vez comprobamos que ya funciona bien, podemos añadir el código de nuestro punto de ruptura situado en la línea 68 a la aplicación (justo antes de la línea afectada por el punto de ruptura) y lanzamos de nuevo la aplicación desactivando antes los puntos de ruptura.

Conclusión

LLDB es una herramienta muy útil para depurar e inyectar código en Xcode. Nos permite ya no solo «vomitar información» acerca de un objeto, sino que podemos interactuar con nuestra app para cambiar su funcionamiento y forma durante su ejecución.

RECUERDA!

ESTE ARTÍCULO FORMA PARTE DE LA COLECCIÓN DE ARTÍCULOS ACERCA DE LLDB, PODRÁS VER EL RESTO AQUÍ.