Cuando trabajas con .NET habrás observado que, al compilar una aplicación, aparte del .exe
o .dll
correspondientes se generan también unos archivos con la extensión .pdb
. Estos tienen el mismo nombre que el ensamblado principal, pero no parecen hacer nada.
PDB es el acrónimo de Program Database. Es un formato propietario de Microsoft que almacena información de depuración de las aplicaciones ejecutables en Windows (.exe
y .dll
). Se crea a partir del código fuente para obtener una relación exhaustiva de los símbolos (variables, objetos, métodos...) del programa y su ubicación en el código y relacionarlos con el código compilado resultante. De esta manera, si necesitamos depurar la aplicación, las interfaces de depuración pueden "casar" las instrucciones de bajo nivel que se están ejecutando con las líneas de código concretas con las que se corresponden.
Aunque es un formato propietario de Microsoft, en realidad está documentado de forma abierta en GitHub. Si bien existe una API y SDK específicos para trabajar con estos archivos .pdb en Windows y, además, con .NET Core se introdujo una versión "portable" del formato que sirve para cualquier sistema, en otros sistemas debemos usar herramientas específicas de terceros, como PDBParse u otras más avanzadas como Radare2, que es mucho más potente y no sólo sirve para este tipo de archivos, sino que hace ingeniería inversa casi de cualquier cosa.
Con un PDB puedes depurar el código fuente a partir de volcados de memoria (y/o el ejecutable) con Visual Studio y herramientas como WinDBG.
Archivos PDB en release
Pero, si los archivos .pdb
son para depuración, ¿por qué cuando genero una aplicación en modo release, para producción, también se me generan archivos .pdb
? ¿Para qué me sirve eso?
El motivo parece obvio, pero no lo es tanto. Y es que, si no tuviésemos este tipo de archivos, no podríamos depurar la aplicación en producción.
¿Cómo? ¿Depurar en producción? Parece un contrasentido. Al fin y al cabo, ya depuramos la aplicación mientras estamos desarrollándola ¿no?
En efecto. Durante las pruebas y el desarrollo ya depuramos la aplicación, por lo que la podemos desplegar a producción con ciertas garantías. Sin embargo, debemos tener en cuenta varias cosas:
- Hay muchas cosas que se nos van a escapar durante el desarrollo, que pueden producir errores una vez la aplicación está en funcionamiento.
- En ciertos tipos de aplicaciones, al estar en producción y tener muchos usuarios simultáneos, se dan situaciones muy difíciles de reproducir en desarrollo o pruebas, especialmente las que tienen que ver con código multisubproceso/asincronía.
- Si la aplicación se distribuye a muchos usuarios finales es muy probable que ciertos entornos personalizados (configuración, sistema operativo, nivel de parche, drivers...) den problemas que no hayamos podido tener en cuenta durante el desarrollo. En ese caso necesitaremos el
.pdb
para depurar el ejecutable concreto que presente los problemas.
- Cuando compilamos para release se le aplican multitud de optimizaciones al código final que no están al compilar en debug (no tanto por el switch
/debug
como el /optimize
que se aplica en este caso). Generalmente esto no tiene impacto alguno sobre el funcionamiento de la aplicación más allá de mejorar su rendimiento, pero en ciertas ocasiones sí que pueden introducir cambios sutiles que afecten al resultado final e introduzcan nuevos bugs.
- Si hacemos profiling y optimización del ejecutable final, necesitaremos el
.pdb
para localizar las líneas que se pueden optimizar.
Regenerar archivos .pdb
Vale. Me has convencido. Son útiles. Pero, dado que tengo el código fuente de cada versión gracias al control de código fuente con Git, si me hace falta puedo volver a compilarlo y generar el PDB si lo necesito ¿no?
Pues sí, pero no... La lógica es aplastante, y será cierto en la mayoría de los casos. Pero hay un problema: nadie te garantiza que el mismo código exacto, unos meses después, genere el mismo archivo .pdb
que se generó cuando compilaste la primera vez.
El motivo es que si cambia la versión del compilador o incluso de Visual Studio, el ejecutable generado puede ser diferente, y por lo tanto el PDB también. No sólo eso, incluso el mismo compilador no siempre genera el mismo código ejecutable todas las veces, según asegura Eric Lippert, exmiembro del equipo de diseño del lenguaje C# en Microsoft.
Tampoco debemos olvidar que los archivos .pdb
que se generan para debug y para release no son iguales.
¿Debería incluir los .pdb
cuando distribuya mi aplicación a los usuarios?
No es necesario que lo hagas. Ocupan bastante y en la mayoría de los casos no los vas a usar para nada. Por eso, los puedes quitar del paquete final que distribuyas (instalación, ZIP...) y obviar dárselos a los usuarios finales.
Si luego los necesitas siempre puedes facilitárselos y depurar in situ. Por eso es interesante que los archives en algún lado para tenerlos a mano, solo, por si acaso.
Desactivar la generación de archivos PDB en release
De acuerdo otra vez. Siempre me convences... Pero, de todos modos, seamos realistas, yo no voy a depurar mi aplicación en producción nunca. ¿No sería mejor directamente no generarlos?
La recomendación sería que no: no desactives su generación. Genéralos siempre y archívalos por versiones, aunque no los distribuyas.
No obstante, si tienes la seguridad de que no los quieres, desactivar su generación es muy sencillo en Visual Studio:
- Cambias a la configuración "Release".
- En las propiedades del proyecto vas a la pestaña
Debug
.
- En la parte de abajo pulsas el botón
Avanzadas
, para abrir el diálogo de propiedades avanzadas.
- En la lista desplegable para
Información de depuración
escoges la opción None
.
Esto desactivará la generación de símbolos de depuración, por lo que prueba conseguida. Pero te recomiendo que no lo hagas.
Fíjate en que hay varias opciones y por defecto, en .NET Core / .NET 5 la elegida es Portable
. Lo que significan estas opciones es:
None
: no se generan símbolos de depuración.
db-only
: podrás depurar el código a partir de volcados de memoria, pero sin poder usar el ejecutable.
Full
: igual que el anterior pero además podrás adjuntar el ejecutable al depurador ya que incluirá dentro de éste cierta metainformación de depuración. Te permite hacer cosas como "Editar y continuar" en Visual Studio, que están muy bien.
Portable
: un formato nuevo de PDB que surgió con .NET Core y que genera un archivo de depuración compatible con todos los sistemas operativos, lo que pega bien con la naturaleza multiplataforma de .NET Core. Es el equivalente "moderno" a la opción Full
y es lo que está marcado en el caso de aplicaciones .NET Core/.NET 5+.
Embedded
: embebe la información de depuración directamente en el ensamblado final, por lo que no se genera ningún archivo .pdb
. Ahora bien, siempre estarás distribuyendo esta información y el .exe
o .dll
ocuparán mucho más.
Espero que este repaso a los PDB haya arrojado luz sobre qué son, cuál es su utilidad y cómo debes gestionarlos.
Fecha de publicación: