2 points par GN⁺ 2025-09-24 | 2 commentaires | Partager sur WhatsApp
  • Le langage Go ajoute officiellement la prise en charge de Valgrind
  • Ce changement renforce les capacités de détection des erreurs mémoire et de débogage
  • Les développeurs peuvent désormais détecter plus facilement les fuites mémoire et les erreurs d’accès
  • L’amélioration de la compatibilité avec Valgrind rend les tâches de portage et de maintenance plus efficaces
  • Il devient plus simple d’évaluer la stabilité du code Go sur différentes plateformes

Importance de l’introduction de la prise en charge de Valgrind dans Go

  • Avec l’ajout de la prise en charge de Valgrind dans Go, les développeurs peuvent désormais utiliser officiellement un outil de détection des erreurs mémoire
  • Ce changement permet d’identifier dans le code Go des problèmes comme le use-after-free, les fuites mémoire et les accès mémoire invalides
  • Valgrind est largement utilisé pour détecter les problèmes de mémoire dans divers langages, et il s’agit d’une évolution importante pour renforcer la fiabilité et la robustesse au sein de la communauté Go
  • La fonctionnalité ajoutée facilite diverses tâches sur plusieurs plateformes pour les programmes Go, notamment le débogage, la validation de la qualité et l’évaluation de la stabilité
  • Cette mise à jour est surtout significative parce qu’elle intègre dans la couche d’exécution de Go du code d’instrumentation pour Valgrind

Qu’est-ce que Valgrind ?

  • Valgrind est un outil de développement open source qui permet d’analyser les erreurs mémoire, les erreurs liées aux threads et les fuites mémoire
  • Il est largement utilisé avec des langages de programmation système comme C et C++, et fournit une détection précise des problèmes de gestion mémoire

Résumé de cette nouveauté

  • L’instrumentation du code introduite par ce changement permet à Valgrind de suivre avec précision les événements liés à la mémoire allouée dynamiquement dans le runtime Go
  • Les développeurs peuvent exécuter des programmes Go avec Valgrind afin de diagnostiquer efficacement les problèmes mémoire potentiels ou les accès incorrects à des pointeurs
  • En conséquence, cela offre l’avantage de maintenir un code de haute qualité et de prévenir les problèmes en amont dans les infrastructures ou services basés sur Go

Effets attendus de ce changement

  • Dans les projets Go, les processus de détection des erreurs mémoire et d’amélioration de la qualité du code devraient devenir plus précis
  • Il devrait être plus facile de garantir la compatibilité et la fiabilité du code Go déployé sur diverses plateformes

2 commentaires

 
taptaps 2025-09-25

Quand on voit des posts sur le langage Go, j’ai l’impression qu’il y a toujours forcément des commentaires du genre
« Rust n’a pas ce problème », « Rust n’en a pas besoin » haha

 
GN⁺ 2025-09-24
Commentaires sur Hacker News
  • Je suis l’auteur de cette CL. Avec cette amélioration, j’aimerais détourner le suivi de l’initialisation mémoire pour vérifier si du code crypto s’exécute en temps constant, d’une manière similaire à ce qu’agl avait proposé il y a environ 15 ans et à ce qui a été fait dans BoringSSL lien connexe, car cette propriété est vraiment difficile à tester. J’espère aussi d’autres effets intéressants, comme explorer s’il est possible, en activant l’usage de Valgrind dans Go, de suivre correctement la gestion mémoire du runtime. Cela dit, j’insiste sur le fait que ce support reste expérimental : je ne peux pas garantir à 100 % que tout fonctionne bien sur toutes les configurations, et il peut y avoir davantage d’avertissements difficiles à interpréter.
    • Je me demande s’il y a des aspects sur lesquels la communauté pourrait aider au développement.
    • Je trouve que c’est une fonctionnalité vraiment géniale, et j’espère qu’elle permettra aussi de découvrir d’autres problèmes dans Go. Mais je me demande s’il ne serait pas possible, sans ce suivi complexe, de simplement injecter différentes valeurs d’entrée dans une fonction de chiffrement puis de mesurer directement son temps d’exécution, et si tout est identique, de vérifier ainsi la propriété de temps constant. Par exemple, même avec le Garbage Collection ou le bruit de l’OS, on pourrait essayer plusieurs entrées, mesurer le temps, et considérer que c’est suffisant si tout reste dans une marge d’erreur epsilon. Certains CPU ont aussi des compteurs de branchement, qu’utilise le débogueur rr ; on pourrait comparer le nombre de branchements avant et après le déchiffrement avec cette valeur (ce qui rejoint l’idée du billet d’agl selon laquelle des branchements identiques impliquent un temps constant). On pourrait aussi ajouter un léger temps de sécurité au temps maximal des 10 premiers déchiffrements, puis appliquer un padding temporel à chaque déchiffrement suivant (par exemple en exécutant des noop) pour forcer une durée identique. On peut même imaginer faire planter le programme avec un assert si la durée dépasse cette borne supérieure. En revanche, dès que l’OS déschedule le processus et perturbe le timing, la méthode devient compliquée.
  • C’est vraiment bien d’avoir choisi, après avoir ajouté les en-têtes Valgrind à l’arborescence, de ne pas passer par cgo pour appeler les macros de requêtes client Valgrind, mais plutôt de déclencher directement les requêtes via une unique fonction d’assembleur qui émet les instructions nécessaires. C’est exactement le genre d’approche souhaitable pour une toolchain bootstrap : construire les briques minimales, puis tout gérer autant que possible au niveau du langage.
    • Si vous n’aviez pas retenu cette approche et aviez évité la méthode existante ou d’autres alternatives, je me demande comment il aurait fallu faire pour obtenir, comme Go, un processus plus simple avec des performances presque équivalentes. J’ai l’impression que c’est le genre de question qu’il faudra encore résoudre à l’avenir.
  • C’est agréable de voir que rsc contribue toujours activement, surtout avec des commentaires jusque dans les messages de commit. Plus je prends de l’âge, plus je mesure l’importance des messages de commit : laisser juste un truc du genre « ajout de valgrind » n’aide pas beaucoup quand on archive ou qu’on revient dessus plus tard.
    • rsc est vraiment un développeur de niveau rockstar. Il tente aussi maintenant beaucoup de choses nouvelles, comme l’usage de l’IA pour la gestion des issues ou des PR, et je m’attends à ce que cela produise pas mal de résultats.
  • Cette fonctionnalité n’est utile que si les tests couvrent tous les paquets ; sinon, les avertissements pertinents se retrouvent noyés dans le bruit. C’est aussi pour cela qu’il est difficile d’utiliser Valgrind avec du code Python.
    • Si c’est vrai, alors cela devrait s’appliquer de la même manière à C et à C++. Dans mon cas, j’ai utilisé Valgrind sur un programme hybride Python + Boost C++, et après une heure de travail sur un fichier de suppressions, cela fonctionnait très bien.
    • Un LLM local pourrait être utile pour trier et résumer une quantité excessive d’informations ; c’est pour cela que le rôle d’une toolchain avec batteries incluses, comme Valgrind, est très important.
    • Je me demande ce que signifie exactement « tous les paquets sont testés » dans l’environnement Go.
  • Valgrind est un super-pouvoir caché. Pour la plupart des logiciels que j’écris, je lance les tests avec make check, puis je relance les mêmes avec make check-valgrind dans un environnement Valgrind. Le second n’est utilisé que sur les machines des développeurs. Cela me permet de trouver souvent des fuites mémoire et des bugs subtils.
    • Je suis partiellement d’accord, mais dès qu’on entre dans le multithreading — un domaine où Go est beaucoup utilisé — la couche d’abstraction de Valgrind fonctionne mal. C’est le constat que j’avais fait la dernière fois que j’ai pas mal creusé avec du code C++, car comme il utilise son propre ordonnanceur, les problèmes de concurrence réelle ou de race condition apparaissent mal sous Valgrind. Et de manière générale, la dégradation des performances est assez sévère. Cela dit, il m’a tout de même beaucoup aidé à plusieurs reprises, donc je suis content qu’il existe toujours.
  • Ce support est vraiment cool, et je pense qu’il fera remonter quelques bugs supplémentaires. Ce qui m’intrigue, c’est pourquoi avoir choisi Valgrind : j’ai l’impression que Clang AddressSanitizer (asan) et MemorySanitizer (msan) détectent davantage d’erreurs, comme les use-after-return, tout en étant bien plus rapides.
    • Go n’utilise pas clang/llvm, donc ces outils ne sont pas applicables.
    • Go dispose déjà de son propre support msan/asan depuis plusieurs années.
    • Valgrind est bien plus rapide, et on peut aussi l’attacher à un programme déjà en cours d’exécution.
    • Valgrind offre aussi diverses fonctions comme le suivi mémoire ou le profilage mémoire, donc il est également excellent du point de vue de l’analyse des performances.
  • C’est très impressionnant. L’un des plus gros problèmes de Go est le profilage ainsi que les phénomènes fréquents de fuite mémoire ou de pression mémoire, et je ne connais pas vraiment d’outil alternatif pour résoudre cela aujourd’hui.
    • J’aimerais en entendre davantage : quels problèmes de profilage rencontres-tu concrètement ? Si les profils mémoire inuse ne suffisent pas pour suivre les fuites mémoire (je me demande si tu as utilisé gorefs), précise quel type de pression mémoire pose problème, cela aiderait. dépôt goref ; pour information, Datadog travaille sur le continuous profiling et contribue régulièrement au profilage du runtime Go.
    • Je me demande comment une « fuite mémoire persistante » peut se produire dans un langage avec GC.
    • Idéalement, j’aurais aimé qu’on puisse, comme dans d’autres langages, contrôler explicitement ce qui va sur la pile au lieu de s’en remettre uniquement à l’escape analysis. Pour l’instant, il faut bricoler avec des options comme -gcflags -m=3 ou des réglages du plugin Go de VSCode tels que ui.codelenses et ui.diagnostic.annotations, ce qui est peu pratique.
    • Concernant les « fuites mémoire persistantes / pression mémoire », avec Go il ne faut pas créer de goroutine sans savoir clairement comment la nettoyer ensuite.
    • pprof fonctionne déjà plutôt bien ; je me demande quelles fonctionnalités supplémentaires seraient souhaitées.
  • Cette tentative me paraît correcte. Il existe un risque si le mécanisme de requêtes client change un jour, mais les en-têtes changent très peu — surtout lors de l’ajout de nouvelles plateformes. Comme Go ne vise que amd64 et arm64, cela limite les problèmes. Le vrai avantage de cette amélioration n’est pas tant la prévention des fuites mémoire que l’identification précise de la mémoire non initialisée, car l’analyse devient difficile si la mémoire recyclée n’est pas « empoisonnée » pour interdire son usage. Cette fonctionnalité est aussi assez utile pour les autres outils, à l’exception de cachegrind et callgrind.
  • J’ai presque l’impression que cette amélioration est moins un progrès qu’un petit échec pour l’écosystème Go. J’adore Valgrind et je l’utilisais souvent à l’époque où je développais en C. Mais le simple fait que Go ait besoin de Valgrind donne l’impression qu’il y a un manque quelque part dans le langage ou l’écosystème. Cela fait environ six ans que j’utilise Rust et je n’ai jamais ressenti le besoin d’utiliser Valgrind (à part une fois pour un collègue), même si je sais bien que cette impression vient probablement de cgo ; malgré tout, cela donne un sentiment de régression.
    • J’ai du mal à comprendre pourquoi les commentaires les mieux classés sur Go mentionnent presque toujours Rust sur un ton moqueur. Cela donne de plus en plus une impression à la fois défensive et teintée de supériorité.
    • Le but principal de cette fonctionnalité n’est pas tant le suivi mémoire correct que son usage pour tester du code en temps constant. Cette explication associée reste encore un peu pertinente.
    • Il m’est arrivé d’utiliser un tout petit peu Valgrind avec Rust aussi. On n’en a pas souvent besoin, mais le besoin existe clairement. Rust est un langage utilisé dans des contextes très variés : depuis du code fonctionnel de haut niveau jusqu’à du code embarqué bas niveau proche du C ; certains projets n’utilisent jamais unsafe, d’autres en dépendent fortement. L’usage de C via FFI est également courant, donc tôt ou tard le besoin peut se présenter. À une époque, en développant un module Rust pour nginx (avant l’existence de bindings officiels ou non officiels), je faisais souvent des erreurs et Valgrind m’a aidé.
    • La fréquence d’utilisation dépend de la quantité de code unsafe, du nombre de crates unsafe et du niveau d’intégration avec des bibliothèques C/C++. On peut aussi en avoir besoin en Java, .NET ou Node à cause de dépendances externes.