Resolver símbolos en un fichero de errores de iOS (Symbolicate)

Una situación muy común que enfrenta un desarrollador de aplicaciones móviles, quizá la más común fuera del desarrollo estrictamente hablando, es el crash debug o análisis de errores de las aplicaciones ya desplegadas a un entorno de producción, ya sea en los markets de uso general u otros para distribución interna.

En iOS concretamente, cuando se produce un cierre inesperado de la aplicación, todos hemos visto alguna vez la alerta que permite enviar información al respecto y que nos permitirá como desarrolladores indagar más profundamente en el error.

¿Qué debemos hacer?

Lo primero de todo es importante remarcar que, antes de distribuir el .ipa, deberíamos haber guardado el fichero .dSYM asociado a él. Este paso es muy importante ya que no es posible generarlo más adelante. No es posible incluso aunque nos pudiéramos mover a la misma versión de código con el que la app fue generada y, por tanto, tengamos exactamente el mismo código. En este caso, el fichero .dSYM generado tampoco te serviría.

Esto es lo que dice la documentación de Apple al respecto:

The Debug Symbol file and application binary are tied together on a per-build-basis by the build UUID. A new UUID is generated for each build of your application and uniquely identifies that build. Even if a functionally-identical executable is rebuilt from the same source code, with the same compiler settings, it will have a different build UUID. Debug Symbol files from subsequent builds, even from the same source files, will not interoperate with binaries from other builds.

Qué significa: El fichero .dSYM y el binario están unidos por el UDID de compilación. Un nuevo UDID se genera en cada compilación para identificar de manera única esa compilación. Incluso dos ejecutables generados con el mismo código exacto tendrán un UDID distinto, por lo que el dSYM de una compilación concreta no funcionará con el binarios de otra compilación.

¿Qué debe hacer el cliente cuando la aplicación tiene un cierre inesperado?

Necesitaremos que el cliente obtenga el fichero de errores del dispositivo, cuya extensión es .ips.

Dónde está localizado este fichero varía dependiendo de la versión. En dispositivos que tengan la versión 10 o superior podemos encontrarlo en Ajustes → Privacidad → Análisis → Datos del análisis.

El nombre del fichero contiene el nombre de nuestra app y la fecha.

Si habías guardado el fichero .dSYM, puedes seguir leyendo esta sección. Si no, puedes ir a la siguiente sección del artículo un poco más abajo.

¿Cuál es el siguiente paso?

Una vez que el cliente nos ha mandado el fichero .ips, debemos modificar la extensión a .crash.

Si lo abrimos, podemos ver que contiene direcciones de memoria.

Ejemplo:

Last Exception Backtrace:

(0x1fd1c7ea0 0x1fc399a40 0x1fd0ce674 0x1fdb46c64 0x1fdb46bcc 0x10227193c 0x10227257c 0x1fcc02484 0x1fcba46d8 0x1fdf7fa14 0x1fd1581cc 0x1fd15814c 0x1fd157a84 0x1fd1528fc 0x1fd1521cc 0x1ff3c9584 0x22a24d054 0x1022847cc 0x1fcc12bb4)

Ahora debemos resolver los símbolos de este fichero (symbolicate). A saber, transformar las direcciones de memoria en métodos legibles del código fuente. 

Este es el comando que debemos usar: 

symbolicatecrash MyApp_2019-05-09_Device.crash MyApp.app.dSYM

Si funciona, nos devolverá el contenido del fichero “traducido”.

Ejemplo:

Last Exception Backtrace:

0   CoreFoundation                0x1fd1c7ea0 __exceptionPreprocess + 228

1   libobjc.A.dylib               0x1fc399a40 objc_exception_throw + 55

2   CoreFoundation                0x1fd0ce674 +[NSException raise:format:] + 115

3   Foundation                    0x1fdb46c64 -[NSPlaceholderString initWithFormat:locale:arguments:] + 123

4   Foundation                    0x1fdb46bcc +[NSString stringWithFormat:] + 67

5   MyApp                   0x10227193c +[ABNotifier showNoticeAlertForNoticesWithPaths:] + 39228 (ABNotifier.m:693)

6   MyApp                   0x10227257c __ABNotifierReachabilityDidChange_block_invoke + 42364 (ABNotifier.m:836)

7   libdispatch.dylib             0x1fcc02484 _dispatch_client_callout + 15

8   libdispatch.dylib             0x1fcba46d8 _dispatch_once_callout + 27

9   SystemConfiguration           0x1fdf7fa14 reachPerformAndUnlock + 539

10  CoreFoundation                0x1fd1581cc __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 23

11  CoreFoundation                0x1fd15814c __CFRunLoopDoSource0 + 87

12  CoreFoundation                0x1fd157a84 __CFRunLoopDoSources0 + 259

13  CoreFoundation                0x1fd1528fc __CFRunLoopRun + 1039

14  CoreFoundation                0x1fd1521cc CFRunLoopRunSpecific + 435

15  GraphicsServices              0x1ff3c9584 GSEventRunModal + 99

16  UIKitCore                     0x22a24d054 UIApplicationMain + 211

17 MyApp                   0x1022847cc main + 116684 (main.m:15)

18  libdyld.dylib                 0x1fcc12bb4 start + 3

Entonces siguiendo esta traza, puede que encontremos la solución al problema que causó que la aplicación se colgase.

Pero, ¿qué pasa si no has guardado el fichero .dSYM?

Si no habíamos guardado el fichero .dSYM, la primera cosa que deberíamos hacer es empezar a guardarlo.

Ahora, veremos algunos trucos que pueden ayudar a “traducir” el fichero.

Aunque para ello necesitaremos usar el mismo ordenador con que se genero el binario, ya que si no, no va a funcionar.

Importar el fichero a xCode

Para ello, necesitaremos hacer un par de cosas primero: Modificar la extensión a .crash y conectar un dispositivo de iOS al ordenador. 

Entonces, con xCode abierto, pincharemos en la pestaña Window del menú y seleccionaremos Devices.

Ahora seleccionaremos el dispositivo conectado para ver el panel informativo. Haz click en el botón “View Device Logs” .

En esta sección podemos seleccionar la pestaña de “All logs” tab y arrastrar el fichero al panel izquierdo. Pulsar sobre el botón derecho y seleccionar “Re-symbolicate log”.

Si esto funciona, podemos intentar buscar la solución al problema que generó el crash.

Si no, puedes intentar otra opción.

Usar el comando symbolicatecrash sin archivo .dSYM

Normalmente el comando se usa asi:

symbolicatecrash MyApp_2019-05-09_Device.crash MyApp.app.dSYM

Pero podemos omitir el último argumento, el que se refiere al fichero .dSYM:

symbolicatecrash app.crash

Como en el consejo anterior, esto puede funcionar o no. Si funciona, ahora con el fichero «traducido» puedes comprobar dónde falló la aplicación.

Error al ejecutar symbolicatecrash

Si al ejecutar el comando symbolicatecrash, recibes un mensaje de error de que el commando no se encuentra.

Debes ejecutar el siguiente comando, para encontrar la ruta absoluta: 

find /Applications/Xcode.app name symbolicatecrash type f

y seleccionar la ruta completa al comando.

Si entonces, recibes el mensaje de error de que DEVELOPER_DIR no se encuentra, debes ejecutar:

export DEVELOPER_DIR=‘/Applications/Xcode.app/Contents/Developer’

 

¿Has tenido suerte? ¿te han servido de algo estos trucos?

De todas maneras, recuerda archivar el archivo .dSYM a partir de ahora.

Además, puede pensar en integrar un servicio de monitorización de errores. De eso podemos hablar en otro artículo.

 

Articulos relacionados

Firmado iOS Parte 1

Creando una librería compartida para iOS de forma sencilla

Distribución continua Ad-Hoc de una aplicación iOS con Bitrise

 

Deja un comentario

¿Necesitas una estimación?

Calcula ahora