Depurar netcore ejecutándose en WSL con VS2017

Hoy he tenido que afrontar la dolorosa situación de un código en netcore que funcionaba correctamente en mi máquina (Windows 10) pero que al ejecutarse en Docker fallaba miserablemente.

Dado que el error que imprimía por consola era que no encontraba un fichero, lo primero fue comprobar que nuestro código era cross-platform (p. ej. que usábamos siempre Path.Combine y no concatenábamos el caráter \ para separar directorios) ya que la imagen Docker era de Linux. Luego hicimos todas las verificaciones habidas y por haber (permisos, etc) y nada. Todo parecía correcto. Al final, nos preguntamos si era un problema de Docker o de cualquier Linux en general y ahí acudimos a WSL.

Ejecutamos el programa bajo WSL y obtuvimos el mismo error. Eso era bueno por dos motivos: No era Docker la fuente del error y además nos permitía depurar el proceso.

Había pero un problema:  teníamos clarísismo que el error NO estaba directamente en nuestro código, si no que era lanzado por una librería externa. Afortunadamente la librería era de Microsoft (se trataba de ML.NET 0.7) y eso significa que podíamos usar la potencia de «Source link» de VS2017 para depurar el código fuente… sin necesidad de tenerlo.

1. Habilitar Source Link

Así, que esto fue lo primero: habilitar Source Link. Resumiendo Source Link permite a VS2017 descargarse el código fuente (de las librerías que lo soportan, claro) automáticamente. No es nada «nuevo», versiones anteriores de VS ya lo permitían (bajo una opción llamada «Source Server»), pero Source Link solventa muchas de las limitaciones que Source Server tenia. Una de las ventajas de Source Link es que una de sus fuentes puede ser Github.

Para habilitar Source Link hay que modificar tres settings de VS2017. El primero es permitir depurar código que no sea propio. Para ello en Options->Debugging->General debes deshabilitar la opción «Enable Just My Code»:

Pantalla de opciones con MyCode deshabilitada

Con esta opción habilitada, VS2017 no intenta depurar código que no sea el de nuestro proyecto (que es lo habitual, no deseas generalmente depurar un framework o librería externa).

La segunda opción a modificar se trata de habilitar «source link». De nuevo está en Options->Debugging->General:

Opción source link habilitada

Habilitar Source Link puede ser suficiente en el caso de nugets que tengan los PDBs incrustados, pero en muchos casos no es así. Para estos otros casos necesitamos dar de alta un servidor de símbolos, del cual VS2017 se pueda descargar los símbolos (PDBs) de las librerías que se van cargando. Microsoft ofrece un servidor de símbolos. Para habilitarlo solo debes ir a «Options->Debugging->Symbols» y marcar la casilla «Microsoft Symbol Servers»:

Añadiendo el servidor de símbolos de MS

Con esto tenemos VS2017 configurado para depurar código externo, descargándose los PDBs del servidor de símbolos y el código fuente. Estamos listos.

2. Depurar contra WSL

La depuración contra WSL se realiza a través de SSH. Lo único que debes tener presente es que no tengas un posible conflicto de puertos. Dado que WSL comparte espacio de puertos con el propio Windows 10, si tienes un servidor de SSH escuchando en Windows, el puerto 22 ya lo tendrás ocupado, así que entonces debes modificar el puerto del servidor SSH del WSL. Yo para que me funcionase he tenido que hacer lo siguiente:

  1. Desinstalar el servidor de SSH de WSL (sudo apt-get remove openssh-server)
  2. Reinstalarlo (sudo apt-get install openssh-server).
  3. Editar el fichero /etc/ssh/sshd_config y poner las entradas (si alguna no existe, la creas):
    1. Port a 2200 (o el valor que prefieras)
    2. UsePrivilegeSeperation a «no» (sin las comillas)
    3. PasswordAuthentication a «yes» (sin las comillas)
  4. Reiniciar el servidor de ssh (sudo service ssh –full-restart)

Esos han sido mis pasos hasta que me ha funcionado (sin ellos VS2017 me daba error al conectarme via SSH).

El siguiente punto fue ejecutar el proyecto en WSL (con dotnet run). Mi proyecto era una Web Api y el error lo daba cuando recibía un POST, así que podía ponerlo en marcha y luego adjuntarme al proceso con VS2017. Para ello (con la sln cargada en VS), en Debug->Attach To Process, seleccionas SSH y en «Connection Target» entras un nombre descriptivo tipo «WSL». Eso te lanzará el diálogo de conexión SSH:

Diálogo "connect to remote system"

Entras los datos correctos (en Host name es el nombre de tu máquina, Port el puerto SSH de WSL y por supuesto «User name» y «Password» es tu usuario y password de WSL).

Hecho esto, VS2017 se connectará con WSL (via SSH) y te mostrará los procesos. En mi caso he tenido que marcar «show process from all users» , ya que los procesos aparecían bajo un usuario llamado «eiximen+» (mi usuario de WSL es eiximenis). No sé a que se debe eso, pero no es problema alguno. Te adjuntas a un proceso y ya podrás empezar a depurar.

La sesión de depuración puede ser muy lenta, ya que VS2017 se debe descargar los símbolos de todas las librerías (así que ten paciencia). En la ventana de Output irás viendo mensajes del tipo:

Loaded '/home/eiximenis/dotnet/shared/Microsoft.AspNetCore.App/2.2.0-preview3-35497/Microsoft.AspNetCore.Diagnostics.Abstractions.dll'. Symbols loaded.
* Searching for 'System.ComponentModel.Annotations.pdb' on the configured symbol servers.
.

Ahora debes tener un breakpoint colocado en tu código fuente, y una vez se para, puedes ir usando F11 para entrar en los métodos. Si entras en un método que no es tuyo (si no del framework o de una librería), VS2017 se descargará el código fuente y podrás seguir depurando.

En mi caso, al final encontré la causa del error (observa que el código fuente cargado es ImageLoaderTransform.cs que forma parte de ML.NET (y que VS2017 se ha descargado):

Sesión de depuración remota con el error

Vaya, se trata de que no se encuentra una librería «libgdiplus». ¡Hemos encontrado el error!

Por cierto que con esa info, encontré una issue ya entrada referente a este problema, pero no la hubiera encontrado si no hubiese sabido qué problema era 🙂

Eso me confirma dos cosas:

  1. A pesar de que cada vez uso menos VS2017, en algunos casos se convierte en un amigo irremplazable.
  2. Tragarse todas las excepciones para terminar lanzando otra excepción sin información es una muy mala práctica.

Saludos!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *