2 puntos por GN⁺ 2025-09-24 | 2 comentarios | Compartir por WhatsApp
  • El lenguaje Go agregó oficialmente compatibilidad con Valgrind
  • Este cambio refuerza las capacidades de detección de errores de memoria y depuración
  • Los desarrolladores ahora pueden detectar más fácilmente fugas de memoria y errores de acceso
  • La mejora en la compatibilidad con Valgrind permite realizar tareas de portabilidad y mantenimiento de forma más eficiente
  • Se facilita la evaluación de la estabilidad del código Go en diversas plataformas

La importancia de incorporar soporte para Valgrind en Go

  • Se añadió compatibilidad con Valgrind en Go, lo que permite a los desarrolladores usar oficialmente una herramienta de detección de errores de memoria
  • Con este cambio, ahora es posible identificar en código Go problemas como use-after-free, fugas de memoria y accesos inválidos a memoria
  • Valgrind se usa ampliamente para detectar problemas de memoria en distintos lenguajes, y para la comunidad de Go representa un cambio importante para reforzar la confiabilidad y robustez
  • La función añadida facilita en varias plataformas tareas como depuración, verificación de calidad y evaluación de estabilidad sobre programas escritos en Go
  • Esta actualización es especialmente relevante porque incorpora código de instrumentación para Valgrind principalmente en la capa de runtime de Go

¿Qué es Valgrind?

  • Valgrind es una herramienta de desarrollo de código abierto que analiza errores de memoria, errores de hilos, fugas de memoria y más
  • Se usa ampliamente en lenguajes de programación de sistemas como C y C++, y ofrece una detección precisa de problemas relacionados con la administración de memoria

Resumen de esta incorporación

  • La instrumentación de código (instrumentation) introducida con este cambio permite que Valgrind rastree con precisión los eventos relacionados con la memoria asignada dinámicamente en el runtime de Go
  • Los desarrolladores pueden ejecutar programas Go con Valgrind para diagnosticar de forma efectiva posibles problemas de memoria o accesos incorrectos a punteros
  • Como resultado, esto aporta beneficios como el mantenimiento de código de alta calidad y la prevención anticipada de problemas en infraestructura o servicios basados en Go

Efectos esperados del cambio

  • Se espera que los procesos de detección de errores de memoria y mejora de la calidad del código en proyectos Go sean más precisos
  • También se prevé que será más fácil garantizar la compatibilidad y confiabilidad del código Go distribuido en diversas plataformas

2 comentarios

 
taptaps 2025-09-25

Cuando veo publicaciones sobre el lenguaje Go, siento que en los comentarios siempre aparece algo como
Rust no hace eso o Rust no necesita eso jajaja

 
GN⁺ 2025-09-24
Comentarios en Hacker News
  • Soy el autor de ese CL; con esta mejora quiero intentar aprovechar el seguimiento de inicialización de memoria para comprobar si el código criptográfico se ejecuta dentro de un tiempo fijo (pruebas de tiempo constante), de una forma similar a la que propuso agl hace unos 15 años y se hizo en BoringSSL enlace relacionado, porque esta propiedad es realmente difícil de probar; también espero otros efectos interesantes, como explorar si al habilitar el uso de Valgrind en Go se puede rastrear correctamente el manejo de memoria del runtime; eso sí, quiero enfatizar que este soporte sigue siendo experimental, no puedo asegurar al 100% que todo funcione bien en todas las configuraciones y podrían aparecer más advertencias difíciles de entender
    • Me pregunto si hay alguna forma en la que la comunidad pueda ayudar con el desarrollo
    • Me parece una función realmente genial, y ojalá también ayude a encontrar otros problemas en Go; pero me pregunto si, en vez de un rastreo tan complejo, no bastaría con meter distintos valores de entrada en una función criptográfica, medir directamente el tiempo de ejecución y, si todos salen iguales, verificar así si se garantiza el tiempo constante; por ejemplo, incluso con recolección de basura o ruido del SO, podría ser suficiente probar varias entradas y comprobar que los tiempos caen dentro de un margen de error epsilon; además, algunos CPU tienen contadores de ramas y el depurador rr los usa, así que también se podría comparar el número de bifurcaciones antes y después del descifrado con ese valor (relacionado con la afirmación del artículo de agl de que para tener tiempo constante las ramas deben ser iguales); y también sería posible tomar el tiempo máximo de las primeras 10 operaciones de descifrado, agregarle un pequeño margen, y luego aplicar padding temporal en cada descifrado posterior (por ejemplo ejecutando noop) para forzar que todos tarden lo mismo; incluso se podría hacer un assert para que el programa falle si supera ese límite, aunque el método se complica si el SO desplanifica el proceso y altera la temporización
  • Me gusta mucho que, en lugar de agregar los headers de Valgrind al árbol y usar cgo para invocar varios macros de solicitudes de cliente de Valgrind, se haya elegido activar esas solicitudes emitiendo directamente las instrucciones necesarias desde una sola función en ensamblador; ese enfoque es justo el deseable en una toolchain de bootstrap: construir los bloques mínimos y manejar todo lo demás a nivel del lenguaje
    • Si no se hubiera elegido este método y se hubiera evitado el enfoque existente u otras alternativas, me pregunto cómo se habría logrado algo con procesos tan simples como los de Go y con un rendimiento casi equivalente; creo que ese tipo de preguntas seguirán siendo una tarea pendiente
  • Da gusto ver que rsc sigue contribuyendo activamente, y me impresiona especialmente que incluso agregue comentarios en los mensajes de commit; mientras más años pasan, más importante me parecen los mensajes de commit; dejar solo algo como "agregar valgrind" no ayuda mucho cuando toca revisar el archivo histórico más adelante
    • rsc es de verdad un desarrollador de nivel rockstar; ahora además está probando cosas nuevas, como usar IA para manejar issues y PR, y espero que logre bastantes resultados
  • Esta función solo es efectiva si las pruebas se aplican a todos los paquetes; de lo contrario, queda enterrada entre advertencias no relacionadas; por eso es difícil usar Valgrind con código Python
    • Si eso es cierto, entonces debería aplicar igual a C y C++; en mi caso usé Valgrind con un programa híbrido Python + Boost C++ y, tras dedicar una hora al archivo de suppressions, pude usarlo sin problemas
    • Un LLM local probablemente sería útil para ordenar y resumir cantidades excesivas de información; por eso es tan importante el papel de una toolchain con baterías incluidas como Valgrind
    • Me pregunto qué significa exactamente que en el entorno Go "todos los paquetes estén bajo prueba"
  • Valgrind es un superpoder oculto; en la mayor parte del software que escribo, ejecuto los casos de prueba con make check y luego vuelvo a correr las mismas pruebas en entorno Valgrind con make check-valgrind; esto último solo lo uso en las PC de los desarrolladores; así suelo encontrar fugas de memoria o bugs sutiles con bastante frecuencia
    • Estoy parcialmente de acuerdo, pero cuando entra el multihilo (algo muy usado en Go), la capa de abstracción de Valgrind no funciona tan bien; lo digo por la última vez que profundicé bastante con código C++, pero al usar su propio scheduler no se reflejan bien en Valgrind problemas de concurrencia reales ni condiciones de carrera; y en general la pérdida de rendimiento es bastante severa; aun así, es cierto que me ha ayudado muchísimo varias veces y agradezco que siga existiendo
  • Este soporte está muy bueno, y creo que gracias a él saldrán a la luz algunos bugs más; lo que me pregunto es por qué se eligió Valgrind; Clang AddressSanitizer (asan) y MemorySanitizer (msan) detectan más tipos de errores (por ejemplo, use-after-return) y creo que además son mucho más rápidos
    • Go no usa clang/llvm, así que esas herramientas no se pueden aplicar
    • Go ya tiene soporte propio para msan/asan desde hace varios años
    • Valgrind es mucho más rápido, y además se puede adjuntar a programas que ya están corriendo
    • Valgrind también ofrece varias funciones como rastreo y perfilado de memoria, así que desde el punto de vista del análisis de rendimiento también es excelente
  • Muy impresionante; uno de los mayores problemas en Go es el perfilado y los frecuentes casos de fugas/presión de memoria, y ahora mismo no conozco una herramienta alternativa clara para resolverlo
    • Me gustaría escuchar más detalles: ¿qué problemas concretos de perfilado estás teniendo? Si el perfil de memoria inuse no es suficiente para rastrear fugas de memoria (me pregunto si ya usaste gorefs), cuéntame qué tipo de presión de memoria te está dando problemas y eso podría ayudar repo de goref; por cierto, en Datadog están trabajando en continuous profiling y siguen contribuyendo al perfilado del runtime de Go
    • Me pregunto cómo puede darse una "fuga de memoria persistente" en un lenguaje con GC
    • Idealmente, creo que deberían haber permitido controlar explícitamente qué se pone en la pila, como en otros lenguajes (en lugar de depender solo del escape analysis); por ahora es incómodo tener que ajustarlo con opciones como -gcflags -m=3 o con configuraciones del plugin de Go en VSCode como ui.codelenses y ui.diagnostic.annotations
    • Sobre las "fugas/presión de memoria persistentes", en Go no deberías crear goroutines sin tener claro cómo limpiarlas después
    • pprof también suele funcionar bastante bien; me pregunto qué funciones extra te gustaría tener
  • Este intento se ve bien; hay cierto riesgo si cambia el mecanismo de solicitudes de cliente, pero los headers casi no cambian (sobre todo al agregar nuevas plataformas), y como Go solo trata con amd64 y arm64, hay menos de qué preocuparse; la verdadera ventaja de esta mejora no está tanto en prevenir fugas de memoria como en identificar con precisión la memoria no inicializada, porque cuando la memoria se reutiliza y no se la "envenena" para impedir su uso, el análisis se vuelve difícil; esta función también es bastante útil para las demás herramientas, excepto cachegrind y callgrind
  • Más que una ganancia, esta mejora me da la sensación de ser un pequeño fracaso para el ecosistema Go; me gusta muchísimo Valgrind y lo usé mucho cuando era desarrollador en C, pero el hecho de que Go necesite Valgrind me hace pensar que hay algo insuficiente en el lenguaje o en el ecosistema; llevo unos 6 años usando Rust y nunca he sentido que necesite Valgrind (salvo una vez que lo usó un compañero del equipo); sé que esta sensación probablemente viene de cgo, pero no puedo evitar sentirlo como un retroceso
    • Me cuesta entender por qué en los comentarios destacados sobre Go siempre aparece alguien mencionando Rust con tono burlón; cada vez suena más defensivo y con un aire de superioridad
    • El objetivo principal de esta función no es tanto el rastreo correcto de memoria, sino su uso para probar código de tiempo constante, y todavía queda un poco de explicación relacionada
    • También he usado Valgrind un poco con Rust; no hace falta muy seguido, pero la necesidad sí existe; Rust es un lenguaje que se usa en entornos muy diversos, desde código funcional de alto nivel hasta código de microcontroladores de bajo nivel al estilo C; hay lugares donde no se usa unsafe en absoluto y otros donde es indispensable; además, usar C por FFI también es bastante común, así que tarde o temprano surge la necesidad; hace tiempo, cuando creaba un módulo Rust para nginx (cuando todavía no había bindings oficiales o no oficiales), cometía errores con frecuencia y Valgrind me ayudó
    • La frecuencia de uso depende de cuánto código unsafe escribas y de cuánto conectes crates unsafe o bibliotecas C/C++; incluso en Java, .NET o Node puede volverse necesario por dependencias externas