Lors du Chrome Dev Summit 2020, nous avons présenté pour la première fois l'assistance Chrome pour le débogage des applications WebAssembly sur le Web. Depuis, l'équipe a investi beaucoup d'énergie pour faire évoluer l'expérience de développement pour les applications volumineuses, voire très volumineuses. Dans ce post, nous allons vous montrer les commandes que nous avons ajoutées (ou prises en compte) dans les différents outils et comment les utiliser.
Débogage évolutif
Reprenons là où nous nous sommes arrêtés dans notre article de 2020. Voici l'exemple que nous examinions à l'époque:
#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();
}
Il s'agit encore d'un exemple assez petit et vous ne verrez probablement aucun des vrais problèmes que vous rencontreriez dans une application très volumineuse, mais nous pouvons tout de même vous montrer les nouvelles fonctionnalités. C'est simple et rapide à configurer et à essayer par vous-même !
Dans le dernier article, nous vous avons expliqué comment compiler et déboguer cet exemple. Recommençons, mais jetons aussi un coup d'œil aux //performance//:
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH
Cette commande génère un binaire Wasm de 3 Mo. Et la majeure partie de ces informations, comme vous vous en doutez, concerne les informations de débogage. Vous pouvez le vérifier à l'aide de l'outil llvm-objdump
[1], par exemple:
$ llvm-objdump -h mandelbrot.wasm
mandelbrot.wasm: file format wasm
Sections:
Idx Name Size VMA Type
0 TYPE 0000026f 00000000
1 IMPORT 00001f03 00000000
2 FUNCTION 0000043e 00000000
3 TABLE 00000007 00000000
4 MEMORY 00000007 00000000
5 GLOBAL 00000021 00000000
6 EXPORT 0000014a 00000000
7 ELEM 00000457 00000000
8 CODE 0009308a 00000000 TEXT
9 DATA 0000e4cc 00000000 DATA
10 name 00007e58 00000000
11 .debug_info 000bb1c9 00000000
12 .debug_loc 0009b407 00000000
13 .debug_ranges 0000ad90 00000000
14 .debug_abbrev 000136e8 00000000
15 .debug_line 000bb3ab 00000000
16 .debug_str 000209bd 00000000
Ce résultat indique toutes les sections du fichier Wasm généré. La plupart sont des sections WebAssembly standards, mais il existe également plusieurs sections personnalisées dont le nom commence par .debug_
. C'est là que le binaire contient nos informations de débogage. Si nous additionnons toutes les tailles, nous constatons que les informations de débogage représentent environ 2,3 Mo de notre fichier de 3 Mo. Si nous appliquons également une commande time
à la commande emcc
, nous constatons que l'exécution a pris environ 1,5 s sur notre machine. Ces chiffres constituent une petite base de référence, mais ils sont si faibles que personne ne les retiendra peut-être. Cependant, dans les applications réelles, le binaire de débogage peut facilement atteindre une taille en Go et prendre plusieurs minutes à se compiler.
Ignorer Binaryen
Lors de la création d'une application Wasm avec Emscripten, l'une de ses dernières étapes de compilation consiste à exécuter l'optimiseur Binaryen. Binaryen est un kit de compilation qui optimise et légalise les binaires de type WebAssembly. L'exécution de Binaryen dans le cadre de la compilation est assez coûteuse, mais elle n'est requise que dans certaines conditions. Pour les versions de débogage, nous pouvons réduire considérablement la durée de compilation si nous évitons d'avoir besoin de cartes Binaryen. La carte de Binaryen la plus courante permet de légaliser des signatures de fonction faisant intervenir des valeurs entières de 64 bits. Pour éviter cela, activez l'intégration de WebAssembly BigInt à l'aide de -sWASM_BIGINT
.
$ emcc -sUSE_SDL=2 -g -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
Nous avons ajouté l'indicateur -sERROR_ON_WASM_CHANGES_AFTER_LINK
pour faire bonne mesure. Il permet de détecter quand Binaryen est en cours d'exécution et de réécrire le binaire de manière inattendue. De cette façon, nous pouvons nous assurer que nous
suivons la voie rapide.
Même si notre exemple est assez petit, nous pouvons encore voir l'effet de sauter le Binaryen ! D'après time
, cette commande s'exécute un peu moins d'une seconde, soit une demi-seconde plus vite qu'avant.
Réglages avancés
Ignorer l'analyse du fichier d'entrée
Normalement, lorsque vous associez un projet Emscripten, emcc
analyse tous les fichiers d'objets d'entrée et toutes les bibliothèques. Cela permet d'implémenter des dépendances précises entre les fonctions de la bibliothèque JavaScript et les symboles natifs dans votre programme. Pour les projets plus volumineux, cette analyse supplémentaire des fichiers d'entrée (à l'aide de llvm-nm
) peut augmenter considérablement la durée de l'association.
Il est possible de l'exécuter à la place avec -sREVERSE_DEPS=all
, qui indique à emcc
d'inclure toutes les dépendances natives possibles des fonctions JavaScript. Cela entraîne une petite taille de code, mais peut accélérer les temps de liaison et peut être utile pour les versions de débogage.
Pour un projet aussi petit que notre exemple, cela ne fait pas de vraie différence, mais si vous avez des centaines, voire des milliers de fichiers d'objets dans votre projet, cela peut significativement améliorer la durée des liens.
Supprimer la section "name" (nom)
Dans les projets de grande envergure, en particulier ceux avec une grande utilisation du modèle C++, la section "name" de WebAssembly peut être très volumineuse. Dans notre exemple, il ne s'agit que d'une toute petite fraction de la taille globale du fichier (voir le résultat de llvm-objdump
ci-dessus), mais dans certains cas, elle peut s'avérer très importante. Si la section "name" (nom) de votre application est très volumineuse et que les informations de débogage sont suffisantes pour vos besoins de débogage, il peut être intéressant de supprimer cette section:
$ emstrip --no-strip-all --remove-section=name mandelbrot.wasm
Cela supprimera la section "name" de WebAssembly tout en conservant les sections de débogage DWARF.
Déboguer les problèmes
Les binaires contenant de nombreuses données de débogage n'exercent pas seulement une pression sur le temps de compilation, mais aussi sur le temps de débogage. Le débogueur doit charger les données et créer un index pour celles-ci afin de pouvoir répondre rapidement aux requêtes telles que "Quel est le type de la variable locale x ?".
La fission de débogage nous permet de diviser les informations de débogage d'un binaire en deux parties: la première qui reste dans le binaire et l'autre, contenue dans un fichier distinct appelé DWARF (.dwo
). Vous pouvez l'activer en transmettant l'indicateur -gsplit-dwarf
à Emscripten:
$ emcc -sUSE_SDL=2 -g -gsplit-dwarf -gdwarf-5 -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
Vous trouverez ci-dessous les différentes commandes et les fichiers générés en compilant sans données de débogage, avec les données de débogage, et enfin avec les données de débogage et la fission de débogage.
Lors de la division des données DWARF, une partie des données de débogage réside avec le binaire, tandis que la grande partie est placée dans le fichier mandelbrot.dwo
(comme illustré ci-dessus).
Pour mandelbrot
, nous n'avons qu'un seul fichier source, mais les projets sont généralement plus volumineux et incluent plusieurs fichiers. Le débogage de débogage génère un fichier .dwo
pour chacun d'eux. Pour que la version bêta actuelle du débogueur (0.1.6.1615) puisse charger ces informations de débogage de fractionnement, nous devons les regrouper dans un package DWARF (.dwp
), comme suit:
$ emdwp -e mandelbrot.wasm -o mandelbrot.dwp
Construire le package DWARF à partir de chaque objet a l'avantage que vous n'avez besoin de diffuser qu'un seul fichier supplémentaire ! Nous travaillons actuellement au chargement de tous les objets individuels dans une prochaine version.
Qu'est-ce que DWARF 5 ?
Vous avez peut-être remarqué que nous avons intégré une autre option dans la commande emcc
ci-dessus : -gdwarf-5
. L'activation de la version 5 des symboles DWARF, qui n'est pas activée par défaut pour le moment, est une autre astuce pour accélérer le débogage. Avec lui, certaines informations sont stockées dans le binaire principal que la version 4 par défaut a omise. Plus précisément, nous pouvons déterminer l'ensemble complet des fichiers sources à partir du binaire principal. Cela permet au débogueur d'effectuer des actions de base, comme afficher l'arborescence source complète et définir des points d'arrêt sans charger ni analyser l'intégralité des données de symboles. Cela accélère considérablement le débogage avec les symboles fractionnés. Nous utilisons donc toujours les indicateurs de ligne de commande -gsplit-dwarf
et -gdwarf-5
ensemble.
Le format de débogage DWARF5 nous donne également accès à une autre fonctionnalité utile. Elle introduit un index de nom dans les données de débogage qui sont générées lors de la transmission de l'indicateur -gpubnames
:
$ emcc -sUSE_SDL=2 -g -gdwarf-5 -gsplit-dwarf -gpubnames -O0 -o mandelbrot.html mandelbrot.cc -sALLOW_MEMORY_GROWTH -sWASM_BIGINT -sERROR_ON_WASM_CHANGES_AFTER_LINK
Lors d'une session de débogage, les recherches de symboles ont souvent lieu en recherchant une entité par son nom, par exemple lors de la recherche d'une variable ou d'un type. L'index de nom accélère cette recherche en pointant directement vers l'unité de compilation qui définit ce nom. Sans index de noms, une recherche exhaustive dans l'ensemble des données de débogage serait nécessaire pour trouver l'unité de compilation correcte qui définit l'entité nommée recherchée.
Pour les plus curieux: consulter les données de débogage
Vous pouvez utiliser llvm-dwarfdump
pour avoir un aperçu des données DWARF. Essayons ceci:
llvm-dwarfdump mandelbrot.wasm
Cela nous donne un aperçu des "unités de compilation" (ou fichiers sources) pour lesquelles nous disposons d'informations de débogage. Dans cet exemple, nous ne disposons que des informations de débogage pour mandelbrot.cc
. Les informations générales nous indiqueront que nous disposons d'une unité squelette, ce qui signifie simplement que nous disposons de données incomplètes dans ce fichier, et qu'il existe un fichier .dwo
distinct contenant les informations de débogage restantes:
Vous pouvez également consulter les autres tables de ce fichier, par exemple la table de lignes qui montre le mappage du bytecode Wasm sur les lignes C++ (essayez d'utiliser llvm-dwarfdump -debug-line
).
Nous pouvons également examiner les informations de débogage contenues dans le fichier .dwo
distinct:
llvm-dwarfdump mandelbrot.dwo
Résumé: Quel est l'avantage d'utiliser la fission de débogage ?
Diviser les informations de débogage avec des applications volumineuses présente plusieurs avantages:
Association plus rapide: l'éditeur de liens n'a plus besoin d'analyser l'intégralité des informations de débogage. Les linkers doivent généralement analyser l'intégralité des données DWARF qui se trouvent dans le binaire. En décomposant une grande partie des informations de débogage dans des fichiers distincts, les éditeurs de liens traitent les binaires plus petits, ce qui réduit les délais d'association (en particulier pour les applications volumineuses).
Débogage plus rapide: le débogueur peut ignorer l'analyse des symboles supplémentaires dans les fichiers
.dwo
/.dwp
pour certaines recherches de symboles. Pour certaines recherches (comme les requêtes sur le mappage de lignes des fichiers Wasm vers C++), nous n'avons pas besoin d'examiner les données de débogage supplémentaires. Cela nous permet de gagner du temps, car nous n'avons pas besoin de charger ni d'analyser les données de débogage supplémentaires.
1: Si une version récente de llvm-objdump
n'est pas installée sur votre système et que vous utilisez emsdk
, vous pouvez la trouver dans le répertoire emsdk/upstream/bin
.
Télécharger les canaux de prévisualisation
Nous vous conseillons d'utiliser Chrome Canary, Dev ou Beta 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 pointe de plates-formes Web et de détecter les problèmes sur votre site avant qu'ils ne le fassent.
Contacter l'équipe des outils pour les développeurs Chrome
Utilisez les options suivantes pour discuter des nouvelles fonctionnalités et des modifications dans l'article, ou de toute autre question concernant les outils de développement.
- Envoyez-nous une suggestion ou des commentaires via crbug.com.
- Signalez un problème dans les outils de développement via Plus d'options > Aide > Signaler un problème dans les outils de développement dans les Outils de développement.
- Envoyez un tweet à @ChromeDevTools.
- Dites-nous en plus sur les nouveautés concernant les vidéos YouTube dans les outils de développement ou sur les vidéos YouTube de nos conseils relatifs aux outils de développement.