Déboguer WebAssembly avec des outils modernes

Ingvar Stepanyan
Ingvar Stepanyan

La route jusqu'à présent

Il y a un an, Chrome a annoncé la prise en charge initiale du débogage WebAssembly natif dans les outils pour les développeurs Chrome.

Nous avons fait preuve d'un soutien de base en marche et nous avons parlé des opportunités l'utilisation des informations DWARF au lieu de les cartes sources s'ouvriront à l'avenir:

  • Résoudre les noms de variables
  • Types d'impression élégante
  • Évaluer des expressions dans les langues sources
  • ... et bien plus !

Aujourd'hui, nous avons le plaisir de vous présenter les fonctionnalités annoncées et les progrès réalisés par les équipes d'Emscripten et des outils pour les développeurs Chrome cette année, en particulier pour les applications C et C++.

Avant de commencer, veuillez noter qu'il s'agit encore d'une version bêta vous devez utiliser la dernière version de tous les outils à vos propres risques et, si vous rencontrez des problèmes, veuillez les signaler à https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350.

Commençons par le même exemple C simple que la dernière fois:

#include <stdlib.h>

void assert_less(int x, int y) {
  if (x >= y) {
    abort();
  }
}

int main() {
  assert_less(10, 20);
  assert_less(30, 20);
}

Pour le compiler, nous utilisons la dernière version d'Emscripten. et transmettre un indicateur -g, comme dans le post d'origine, pour inclure le débogage informations:

emcc -g temp.c -o temp.html

Nous pouvons maintenant diffuser la page générée à partir d'un serveur HTTP localhost (pour par exemple, avec serve), et ouvrez-le dans la dernière version de Chrome Canary.

Cette fois, nous avons également besoin d'une extension d'assistance qui s'intègre à Chrome. Les outils de développement vous aident à interpréter toutes les informations de débogage encodée dans le fichier WebAssembly. Veuillez l'installer à l'adresse lien: goo.gle/wasm-debugging-extension

Vous devez également activer le débogage WebAssembly dans les outils de développement Tests : Ouvrez les outils pour les développeurs Chrome, puis cliquez sur l'icône en forme de roue dentée () En haut à droite du volet "DevTools", accédez au panneau Experiments (Tests). Cochez la case WebAssembly Debugging: Enable DWARF support (Débogage WebAssembly : Activer la prise en charge DWARF).

Volet &quot;Tests&quot; des paramètres DevTools

Lorsque vous fermez Settings (Paramètres), les outils de développement vous suggèrent de s'actualiser. pour appliquer des paramètres, alors c’est exactement ça. C'est tout pour cette session ponctuelle configuration.

Revenez maintenant au panneau Sources et activez Mettre en veille sur les exceptions (icône ⏸), puis cochez la case Mettre en veille sur les exceptions interceptées et actualiser la page. Les outils de développement doivent être mis en veille sur une exception:

Capture d&#39;écran du panneau &quot;Sources&quot; montrant comment activer &quot;Suspendre sur les exceptions interceptées&quot;

Par défaut, il s'arrête sur un code Glue généré par Emscripten, mais sur À droite, vous voyez une vue Call Stack (Pile d'appel) représentant la trace de la pile de l'erreur et accéder à la ligne C d'origine qui a appelé abort:

DevTools est mis en pause dans la fonction &quot;assert_less&quot; et affiche les valeurs de &quot;x&quot; et &quot;y&quot; dans la vue &quot;Champ d&#39;application&quot;.

Si vous regardez dans la vue Champ d'application, vous pouvez voir les noms et les valeurs d'origine des variables dans le code C/C++. Vous n'avez plus à essayer de comprendre la signification de noms tronqués comme $localN et leur relation avec le code source que vous avez écrit.

Cela s'applique non seulement aux valeurs primitives telles que les entiers, mais aussi aux valeurs comme les structures, les classes, les tableaux, etc.

Compatibilité avec les types enrichis

Étudions un exemple plus complexe pour les illustrer. Ce dessinons une fractale de Mandelbrot avec le code C++ suivant:

#include <SDL2/SDL.h>
#include <complex>

int main() {
  // Init SDL.
  int width = 600, height = 600;
  SDL_Init(SDL_INIT_VIDEO);
  SDL_Window* window;
  SDL_Renderer* renderer;
  SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL, &window,
                              &renderer);

  // Generate a palette with random colors.
  enum { MAX_ITER_COUNT = 256 };
  SDL_Color palette[MAX_ITER_COUNT];
  srand(time(0));
  for (int i = 0; i < MAX_ITER_COUNT; ++i) {
    palette[i] = {
        .r = (uint8_t)rand(),
        .g = (uint8_t)rand(),
        .b = (uint8_t)rand(),
        .a = 255,
    };
  }

  // Calculate and draw the Mandelbrot set.
  std::complex<double> center(0.5, 0.5);
  double scale = 4.0;
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      std::complex<double> point((double)x / width, (double)y / height);
      std::complex<double> c = (point - center) * scale;
      std::complex<double> z(0, 0);
      int i = 0;
      for (; i < MAX_ITER_COUNT - 1; i++) {
        z = z * z + c;
        if (abs(z) > 2.0)
          break;
      }
      SDL_Color color = palette[i];
      SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
      SDL_RenderDrawPoint(renderer, x, y);
    }
  }

  // Render everything we've drawn to the canvas.
  SDL_RenderPresent(renderer);

  // SDL_Quit();
}

Comme vous pouvez le voir, cette application est encore relativement petite. Il s'agit d'un seul contenant 50 lignes de code. Cette fois, j'utilise aussi des API externes, comme la bibliothèque SDL pour des images, ainsi que des nombres complexes issus bibliothèque standard C++.

Je vais la compiler avec le même indicateur -g que ci-dessus pour inclure informations de débogage. Je demande aussi à Emscripten de fournir le fichier SDL2 bibliothèque et autorisent une mémoire de taille arbitraire:

emcc -g mandelbrot.cc -o mandelbrot.html \
     -s USE_SDL=2 \
     -s ALLOW_MEMORY_GROWTH=1

Lorsque j'accède à la page générée dans le navigateur, je vois avec des couleurs aléatoires:

Page de démonstration

Lorsque j'ouvre les outils de développement, je peux à nouveau voir le fichier C++ d'origine. Ce mais comme il n'y a pas d'erreur dans le code (ouf !), un point d'arrêt au début de notre code à la place.

Lorsque nous actualisons la page, le débogueur se met en pause directement dans notre Source C++:

Outils de développement interrompus dans l&#39;appel &quot;SDL_Init&quot;

Nous pouvons déjà voir toutes nos variables à droite, mais seulement width et height sont initialisés pour le moment. Il n'y a donc pas grand-chose à inspecter.

Définissons un autre point d'arrêt dans notre boucle principale Mandelbrot, puis reprenons l'exécution pour avancer un peu.

Outils de développement mis en pause dans les boucles imbriquées

À ce stade, notre palette a été rempli de couleurs aléatoires. Nous pouvons développer le tableau lui-même, ainsi que les structures SDL_Color individuelles, et inspecter leurs composants pour vérifier que tout est correct (par exemple, que le canal "alpha" est toujours défini sur une opacité complète). De même, nous pouvons développer et vérifier les conditions réelles et les parties imaginaires du nombre complexe stockées dans la variable center.

Si vous souhaitez accéder à une propriété profondément imbriquée qui est autrement difficile à via la vue Champ d'application, vous pouvez utiliser la console l'évaluation ! Notez toutefois que les expressions C++ plus complexes ne sont pas déjà pris en charge.

Panneau de la console affichant le résultat de &quot;palette[10].r&quot;

Reprenons l'exécution plusieurs fois pour voir comment la x interne est ou en consultant à nouveau la vue Portée, en ajoutant le nom de la variable à la liste de surveillance, en l'évaluant dans la console ou en en pointant sur la variable dans le code source:

Info-bulle sur la variable &quot;x&quot; dans la source affichant sa valeur &quot;3&quot;

À partir de là, nous pouvons entrer ou passer les instructions C++ et observer comment d'autres variables changent également:

Info-bulles et vue du champ d&#39;application affichant les valeurs de la &quot;couleur&quot;, le &quot;point&quot; et d&#39;autres variables

Tout fonctionne très bien lorsqu'une information de débogage est disponible, que faire si nous voulons déboguer un code qui n'a pas été créé avec l'API de débogage ?

Débogage WebAssembly brut

Par exemple, nous avons demandé à Emscripten de fournir une bibliothèque SDL prédéfinie pour au lieu de les compiler nous-mêmes à partir de la source, Le débogueur ne peut actuellement pas trouver les sources associées. Revenons à l'étape suivante pour accéder à SDL_RenderDrawColor:

Outils de développement affichant la vue de démontage de &quot;mandelbrot.wasm&quot;

Nous revenons à l'expérience de débogage WebAssembly brute.

Cela semble un peu effrayant, et la plupart des développeurs Web à résoudre, mais il peut arriver que vous souhaitiez déboguer est créée sans informations de débogage, que ce soit parce qu'il s'agit d'une bibliothèque tierce sur laquelle vous n'avez aucun contrôle, ou parce que vous rencontrer l’un de ces bugs qui ne se produit qu’en production.

Pour vous aider, nous avons apporté des améliorations expérience de débogage.

Tout d'abord, si vous avez déjà utilisé le débogage WebAssembly brut, que le démontage complet est maintenant affiché dans un seul fichier devinez plus facilement à quelle fonction correspond une entrée Sources wasm-53834e3e/ wasm-53834e3e-7.

Nouveau schéma de génération de nom

Nous avons également amélioré les noms dans la vue de démontage. Auparavant, vous verriez que des index numériques ou, dans le cas de fonctions, aucun nom.

Nous générons maintenant des noms de la même manière que d'autres outils de démontage, à l'aide des indications de la section des noms WebAssembly les chemins d'importation/exportation et, enfin, si tout le reste échoue, générer en fonction du type et de l'index de l'élément, par exemple $func123. Dans la capture d'écran ci-dessus, vous pouvez voir comment cela permet déjà d'obtenir des traces de pile et un désassemblage légèrement plus lisibles.

Lorsqu'aucune information de type n'est disponible, il peut être difficile d'inspecter Toutes les valeurs autres que les primitives. Par exemple, les pointeurs s'affichent. comme des entiers normaux, sans aucun moyen de savoir ce qui est stocké derrière eux dans mémoire.

Inspection de la mémoire

Auparavant, vous ne pouviez développer que l'objet de mémoire WebAssembly, représenté par env.memory dans la vue Scope (Champ d'application) pour rechercher octets individuels. Cela fonctionnait dans certains scénarios insignifiants, mais n'était pas particulièrement pratique à développer et ne permet pas de réinterpréter les données dans des formats autres que les valeurs en octets. Nous avons ajouté une nouvelle fonctionnalité pour vous aider avec ceci aussi: un inspecteur de mémoire linéaire.

Si vous effectuez un clic droit sur env.memory, vous devriez maintenant voir appelée Inspect Memorystore (Inspecter la mémoire) :

Menu contextuel de &quot;env.memory&quot; dans le volet &quot;Scope&quot; (Champ d&#39;application) affichant la mention &quot;Inspect Memory&quot; (Inspecter la mémoire) élément

Lorsque vous cliquez dessus, un outil d'inspection de mémoire s'affiche. que vous pouvez inspecter la mémoire WebAssembly dans les vues hexadécimales et ASCII, accéder à des adresses spécifiques, et interpréter les données différents formats:

Volet de l&#39;outil d&#39;inspection de mémoire dans les outils de développement affichant des vues hexadécimales et ASCII de la mémoire

Scénarios avancés et mises en garde

Profiler du code WebAssembly

Lorsque vous ouvrez les outils de développement, le code WebAssembly est "hiérarchisé" à un version non optimisée pour activer le débogage. Cette version est beaucoup plus lente, ce qui signifie que vous ne pouvez pas compter sur console.time, performance.now et d'autres méthodes de mesure de la vitesse de votre code. Les outils de développement car les chiffres obtenus ne reflètent pas les performances réelles du tout.

Utilisez plutôt le panneau Performance des outils de développement. qui exécutera le code à pleine vitesse et vous fournira une répartition détaillée du temps passé dans différentes fonctions:

Panneau de profilage affichant différentes fonctions Wasm

Vous pouvez aussi exécuter votre application avec les outils de développement fermés une fois que vous avez terminé, ouvrez-les pour inspecter la console.

Nous améliorerons les scénarios de profilage à l'avenir, mais pour le moment, il s'agit mise en garde à prendre en compte. Pour en savoir plus sur WebAssembly de hiérarchisation, consultez nos documents sur le pipeline de compilation WebAssembly.

Compiler et déboguer sur différentes machines (y compris Docker / hôte)

Lorsque vous compilez dans un Docker, une machine virtuelle ou sur un serveur de compilation distant, vous rencontrerez probablement des situations où les chemins d'accès aux fichiers sources utilisés lors de la compilation ne correspondent pas aux chemins d'accès de votre propre système de fichiers où les outils pour les développeurs Chrome sont en cours d'exécution. Dans ce cas, les fichiers apparaissent Sources, mais son chargement échoue.

Pour résoudre ce problème, nous avons implémenté une fonctionnalité de mappage de chemin d'accès dans les options d'extension C/C++. Vous pouvez l'utiliser pour remapper des chemins arbitraires et pour aider les outils de développement à localiser les sources.

Par exemple, si le projet de votre machine hôte se trouve sous un chemin d'accès C:\src\my_project, mais a été créée dans un conteneur Docker où ce chemin était représenté par /mnt/c/src/my_project, vous pouvez le remapper lors du débogage en spécifiant ces chemins d'accès en tant que préfixes:

Page d&#39;options de l&#39;extension de débogage C/C++

Le premier préfixe correspondant "gagnant" Si vous connaissez d'autres langages C++ de débogage, cette option est semblable à la commande set substitute-path dans GDB ou un paramètre target.source-map dans LLDB.

Déboguer les builds optimisés

Comme pour tout autre langage, le débogage fonctionne mieux si les optimisations sont sont désactivées. Les optimisations peuvent intégrer des fonctions dans une autre, réorganiser du code ou en supprimer certaines parties. risque de perturber le débogueur et, par conséquent, vous, en tant qu'utilisateur.

Si l'expérience de débogage plus limitée ne vous dérange pas et que vous souhaitez tout de même déboguer un build optimisé, la plupart des optimisations fonctionnent à l'exception de l'intégration de fonctions. Nous prévoyons d'aborder les problèmes à l'avenir. Pour le moment, utilisez -fno-inline pour la désactiver lors de la compilation avec toute optimisation de niveau -O, par exemple :

emcc -g temp.c -o temp.html \
     -O3 -fno-inline

Séparer les informations de débogage

Les informations de débogage conservent de nombreux détails sur votre code, définis les types, variables, fonctions, champs d'application et emplacements, ainsi que tout ce qui pourrait être utiles au débogueur. Il est donc souvent plus important que le code lui-même.

Pour accélérer le chargement et la compilation du module WebAssembly, vous pouvez diviser ces informations de débogage dans un composant WebAssembly distinct . Pour le faire dans Emscripten, transmettez un indicateur -gseparate-dwarf=… avec le nom de fichier souhaité:

emcc -g temp.c -o temp.html \
     -gseparate-dwarf=temp.debug.wasm

Dans ce cas, l'application principale ne stocke qu'un nom de fichier temp.debug.wasm, et l'extension d'assistance peut le localiser et le charger lorsque vous ouvrez DevTools.

Associée aux optimisations décrites ci-dessus, cette fonctionnalité peut même les utiliser pour envoyer des versions de production presque optimisées application, puis les déboguer plus tard avec un fichier secondaire local. Dans ce cas, nous devons également remplacer l'URL stockée pour aider l'extension à trouver le fichier secondaire, par exemple :

emcc -g temp.c -o temp.html \
     -O3 -fno-inline \
     -gseparate-dwarf=temp.debug.wasm \
     -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]

À suivre...

Ça fait beaucoup de nouvelles fonctionnalités !

Avec toutes ces nouvelles intégrations, les outils pour les développeurs Chrome deviennent un débogueur puissant non seulement pour JavaScript, mais aussi pour les applications C et C++. Il n'a jamais été aussi facile d'utiliser des applications grâce à une variété et les intégrer à un Web partagé et multiplate-forme.

Cependant, notre voyage n'est pas encore terminé. Certaines des choses que nous allons à partir de là:

  • Nettoyer les ébauches de l'expérience de débogage.
  • Prise en charge des outils de mise en forme personnalisés.
  • Nous nous efforçons d'améliorer profilage pour les applications WebAssembly
  • Ajout de la prise en charge de la couverture de code pour faciliter la recherche code inutilisé.
  • Amélioration de la prise en charge des expressions dans l'évaluation de la console.
  • Ajout de la prise en charge de plus de langues.
  • Et bien d'autres…

En attendant, aidez-nous en testant la version bêta actuelle sur votre propre code et en nous signalant les problèmes de problèmes à https://issues.chromium.org/issues/new?noWizard=true&amp;template=0&amp;component=1456350.

Télécharger les canaux de prévisualisation

Vous pouvez utiliser Chrome Canary, Dev ou Bêta comme navigateur de développement par défaut. Ces versions preview vous permettent d'accéder aux dernières fonctionnalités des outils de développement, de tester des API de plates-formes Web de pointe et de détecter les problèmes sur votre site avant vos utilisateurs.

Contacter l'équipe des outils pour les développeurs Chrome

Utilisez les options suivantes pour discuter des nouvelles fonctionnalités et des modifications décrites dans l'article, ou pour tout autre sujet lié aux outils de développement.

  • Envoyez-nous vos suggestions ou vos commentaires via crbug.com.
  • Signalez un problème lié aux outils de développement en cliquant sur Autres options   Plus > Aide > Signalez un problème dans les outils de développement.
  • Tweetez à l'adresse @ChromeDevTools.
  • Faites-nous part de vos commentaires sur les vidéos YouTube sur les nouveautés des outils de développement ou sur les vidéos YouTube de conseils pour les outils de développement.