Dev Life
Presentamos un depurador multiplataforma para Go
En Mailgun utilizamos Go para gran parte de nuestro desarrollo de servidores, y es genial. Sin embargo, al venir de Python, hay una cosa que he echado mucho de menos:
Podía insertar esta pequeña línea siempre que me confundía lo que estaba pasando en el código y ver exactamente qué ocurría. ¿Pero en Go? No tanto. Cuando empecé este proyecto en enero, gdb fallaba en todos los programas en los que lo probaba, delve no funcionaba en OS X y la depuración de declaraciones de impresión era demasiado lenta y limitada. ¿Qué se puede hacer en estos casos?
Crear mi propio depurador, por supuesto.
godebug
Lo único que se interpone en el camino [de un buen depurador de Go] es la escritura de mucho código de bajo nivel no portable que se comunique con interfaces no documentadas y llenas de errores.
godebug es un tipo de depurador diferente. Los depuradores tradicionales para lenguajes compilados utilizan llamadas al sistema de bajo nivel y leen archivos binarios en busca de símbolos de depuración. Son difíciles de perfeccionar y de portar.
godebug adopta un enfoque diferente: tomar el código fuente de un programa de destino, insertar código de depuración entre cada línea y, a continuación, compilar y ejecutar el resultado. El resultado es un depurador totalmente funcional que resulta extremadamente portable. De hecho, gracias a gopherjs, ¡puedes ejecutarlo directamente en tu navegador!
Puedes editar el programa y volver a lanzarlo con el botón “DEBUG IT!” tantas veces como quieras.
¿Cómo funciona esto?
Aquí tienes un diagrama rápido de la demostración anterior:
El código original se transforma dos veces. En primer lugar, godebug insertó la instrumentación de depuración. Luego, gopherjs compiló el resultado a javascript.
Echemos un vistazo al paso de instrumentación. (Para saber más sobre la parte de gopherjs (que es increíble), consulta su sitio web.) Estas son algunas de las llamadas que inserta godebug:
godebug.EnterFuncpermite a la biblioteca de tiempo de ejecución de godebug saber que estamos entrando en una función. Dado que “next” no se detiene dentro de las llamadas a funciones, la biblioteca de tiempo de ejecución toma nota de estas llamadas para saber cuándo omitir líneas.godebug.ExitFuncpermite a la biblioteca de tiempo de ejecución saber que estamos saliendo de una función. Omitido enmain.godebug.Linehace que el programa se pause y espere una entrada si, y solo si, un comando de usuario o un punto de interrupción se lo indican. Al pausarse, solicita una entrada al usuario y responde a cualquier comando.godebug.Declareregistra el mapeo de nombres de variable a sus valores. Este mapeo es utilizado por el comando print.
Esta es una descripción resumida. Hay otras funciones que inserta godebug, y se han omitido muchos detalles de las funciones anteriores. Sin embargo, estas son las piezas básicas de cómo funciona godebug.
Uso de godebug
Todo lo anterior (menos lo relativo al servidor y a javascript) está empaquetado en la herramienta de línea de comandos godebug. Aquí te mostramos cómo usarla:
Paso 1. Instalación
$ go get -u github.com/mailgun/godebug
Paso 2: Establecer puntos de interrupción
Añade este marcador en cualquier lugar donde quieras un punto de interrupción:
_ = "breakpoint"
Esta declaración se convierte en un punto de interrupción al ejecutarse con godebug y, de lo contrario, es una operación inactiva (no-op).
Dado que los puntos de interrupción forman parte del código fuente, puedes envolver tu propia lógica alrededor de ellos. Supongamos que estás ejecutando una prueba guiada por tablas con docenas de casos, y uno de los casos de prueba falla: el que probó la entrada "weird string". Puedes añadir este punto de interrupción a tu prueba:
for _, tt := range myTestCases { if tt.in == "weird string" { _ = "breakpoint" } ... }
godebug test pausará el programa en la declaración del marcador, la cual está convenientemente situada justo antes de que se ejecute el caso de prueba fallido.
Paso 3: Ejecuta tu programa
Utiliza el comando godebug run:
godebug run
O, para las pruebas, utiliza el comando godebug test:
godebug test
Por defecto, godebug solo añadirá instrumentación de depuración al paquete main (para godebug run) o al paquete que se está probando (para godebug test). No instrumentará ningún paquete importado. Esto es para reducir la sobrecarga del depurador: cualquier paquete que no te interese se ejecutará con normalidad, a máxima velocidad.
Esto significa que, por defecto, no puedes entrar en llamadas a funciones de paquetes importados. ¡Pero a veces necesitarás hacerlo! Para depurar otros paquetes, pasa la etiqueta -instrument a godebug run o a godebug test:
godebug test -instrument=pkgA,pkgB,pkgC pkg/under/test
El comando anterior instrumentará (y, por tanto, te permitirá entrar en) pkgA, pkgB, pkgC y pkg/under/test. A continuación, compilará y ejecutará las pruebas para pkg/under/test. Se aplica una semántica similar a godebug run.
¡Pruébalo!
La próxima vez que quieras entender qué ocurre en un programa de Go, prueba godebug. Ten en cuenta que todavía es una herramienta nueva a la que le falta un poco de pulido. En particular, algunas limitaciones conocidas son:
- sobrecarga de rendimiento
- puede causar conflictos de lectura si el programa lee desde stdin
- no se puede adjuntar a un proceso en ejecución
- debe conocer los paquetes que quieres depurar antes de iniciar la sesión
Dicho esto, me entusiasma esta herramienta y espero que aporte mucho valor a la comunidad de Go. ¡Pruébala y cuéntame qué te parece! Si tienes algún problema, me encantaría saberlo. Abre una incidencia en https://github.com/mailgun/godebug/issues o envía un email a: dev@godebug.rocks
¡Espero que lo disfrutes!
https://github.com/mailgun/godebug
¿Quieres saber más sobre godebug? Aquí tienes una charla que di en GoSF, en la que se profundiza en la generación de código y la gestión de la concurrencia: