La douleur du débogage de WebAssembly


Si vous savez quelque chose sur WebAssembly (WASM), il est probable que WASM vous permet d’exécuter du code compilé à partir de langages tels que C, C ++, Rust ou autres dans le navigateur à des vitesses presque natives. Vous êtes peut-être moins familier avec le fait que WASM n’est pas seulement une technologie intéressante dans le navigateur, mais également dans d’autres environnements qui nécessitent un sandboxing rapide. En tant que tel, WASM a trouvé une certaine popularité avec l’informatique de pointe et en tant que remplacement de docker léger pour certaines situations.

Ce dernier est activé par un standard appelé ÉTAIS-JE, qui fournit une abstraction indépendante de la plate-forme pour s’interfacer avec le système d’exploitation pour les entrées / sorties de base, l’accès au système de fichiers, l’obtention de l’heure actuelle, etc. WASI est implémenté par des runtimes tels que wasmtime ou wasmer.

WASM peut faire des choses impressionnantes. Mais qu’est-ce qui n’est pas génial? Débogage avec WASM.

Nous sommes nombreux à travailler sur des systèmes complexes où il est de plus en plus difficile de reproduire de vrais bogues. En tant que développeurs, nous comprenons ce défi. C’est pourquoi nous avons veillé à ce que vous puissiez utiliser les cartes source de Sentry pour trouver des traces de pile qui montrent de manière fiable à quoi ressemblait votre véritable trace de pile, même sur une version JavaScript réduite. Et quand il s’agit de WASM, nous voulons avoir une trace de pile fiable avec des noms de fonction, des numéros de ligne et des noms de fichiers fiables en production, afin que nous puissions l’envoyer à un système comme Sentry. Mais c’est là que nous nous heurtons actuellement à des limitations.

Poignées de débogage actuelles

Le WASM en son cœur est assez différent de nombreux systèmes que les langues natives traitent normalement. C’est une machine à empiler! Les fonctions ne sont pas «adressables» dans le même espace mémoire que la mémoire avec laquelle nous travaillons. Ce concept «nouveau» a des avantages, mais une grande partie des outils existants de WASM n’est pas conçue pour notre réalité actuelle.

Déroulement de la pile

Commençons par les bases: pour obtenir une trace de pile, nous devons dérouler la pile. Dans le monde du code natif, cela se fait généralement avec des bibliothèques comme libunwind – qui implémente le schéma de déroulement de la plateforme. Pour nos besoins, nous nous soucions uniquement des adresses de retour pour une fonction et rien de plus.

Il existe généralement deux façons de se détendre. La première consiste à vider toute la mémoire de la pile avec les registres dans un vidage de la mémoire (comme un minidump), puis à se dérouler après coup. La seconde consiste à capturer les registres et à se dérouler dans le programme en cours d’exécution. Ce dernier est également nécessaire lorsque des exceptions C ++ ou des paniques Rust sont levées. Ces exceptions ou paniques nous indiquent déjà que le déroulement de la pile n’est pas seulement nécessaire pour créer une trace de pile sophistiquée, mais aussi pour exécuter des destructeurs en présence d’exceptions.

À l’heure actuelle, WASM ne prend pas en charge le déroulement de la pile. Bien que cela semble être une limitation assez sévère, cela ne s’avère pas être un gros problème (du moins pour le cas du navigateur JavaScript). Il s’avère qu’en raison de la nature basée sur la pile de WASM, les appels de fonction WASM sont visibles dans la trace de pile JavaScript. Cela signifie que si une fonction JavaScript appelle WASM et que WASM appelle JavaScript, nous pouvons observer les cadres WASM à partir de JavaScript. Donc, comme solution de contournement sophistiquée, nous pouvons créer un objet d’exception JavaScript, puis analyser sa trace de pile.

NAIN

DWARF est notre standard de débogage préféré, principalement parce que c’est le seul qui soit pensé et documenté. (L’autre étant les cartes sources et PDB, ce qui est tellement bizarre que nous pourrions avoir du matériel pour le contenu futur).

Bien que DWARF soit génial, il ne fonctionne tout simplement pas encore avec WASM – il est au moins pris en charge par Chrome pour le débogage de WASM, mais il reste des problèmes.

DWARF fonctionne en incorporant des sections avec des données de débogage DWARF dans un fichier exécutable ou objet. Étant donné que WASM est un format d’objet extensible, il est absolument possible d’y incorporer DWARF. Cependant, nous avons déjà mentionné précédemment que WASM est basé sur la pile et que les fonctions ne sont pas en mémoire. Cela fonctionne parce qu’il y a un DWARF pour WASM spec et il indique que les adresses de code sont des décalages d’octets dans la section de code du fichier WASM.

Cool. Sauf que de nombreux outils WASM ne tiennent pas compte de choses comme les décalages d’octets. Pour une bonne raison, car WASM a en fait deux formats: un format binaire et WAST, qui est une représentation textuelle. Au sein de DWARF, ces deux formats ne sont plus interchangeables en présence d’informations de débogage, faute de quoi les décalages nécessiteraient une réécriture. Dommage.

Division DWARF

Alors d’accord, le genre DWARF fonctionne. Mais réfléchissons à *Comment* DWARF fonctionne. Les données de débogage DWARF sont intégrées directement dans le fichier WASM. Ceci est un problème pour deux raisons: la première est que la plupart des gens ne veulent pas que quiconque décompile facilement leur code et voit les noms de fichiers à partir de leurs machines de construction ou d’autres métadonnées.

Mais même si vous êtes un amoureux de l’open source qui ne craint pas les informations de débogage vivant sur Internet, vous ne voudriez toujours pas intégrer les informations de débogage dans le fichier WASM. À l’heure actuelle, les données DWARF pour WASM sont énormes et même dans le futur le plus optimiste, les données de débogage WASM seront toujours d’un ordre de grandeur plus grand que le fichier WASM principal.

C’est là que l’idée de séparer les données DWARF de l’exécutable principal entre en jeu. Sur macOS, ils s’appellent .dSYM fichiers – mais vraiment cela fonctionne partout, y compris avec WASM. Vous pouvez, en fait, déjà diviser un fichier WASM en code et déboguer les données très bien. Les données de débogage peuvent résider dans un fichier WASM non exécutable et non fonctionnel qui contient uniquement les informations de débogage.

Le problème est qu’après avoir scindé ces deux éléments, il est difficile de les relier. La spécification DWARF actuelle propose d’incorporer une référence à un fichier de débogage téléchargeable dans le fichier WASM principal en tant que section personnalisée. C’est essentiellement ainsi que fonctionnent les cartes source, mais pas comment le débogage natif fonctionne normalement. L’une des idées brillantes popularisées par Apple et Microsoft était de donner aux fichiers de débogage et exécutables des ID de débogage uniques au monde. Avec cette connaissance, vous pouvez lier un fichier de débogage et un fichier de code ensemble.

D’après ce que je peux dire, personne ne prend encore en charge le fractionnement DWARF. La seule spécification flottante utilise des références d’URL, ce qui est une expérience utilisateur pire que les ID de débogage.

ID de débogage

Comme vous pouvez le voir dans la dernière section, vous ne pouvez pas dire “Division DWARF” sans parler des ID de débogage. Ils utilisent également d’autres noms: ID de construction, ID de débogage, ID de code – tous les noms qui communiquent plus ou moins un ID unique pour nos données de débogage.

Les ID de débogage sont utiles car ils vous permettent de connecter le fichier de débogage au bon fichier WASM. Si je devais connecter les mauvaises données de débogage à votre fichier, alors il s’agit d’un exemple classique de deux éléments de données fiables non liés entre eux, à la poubelle.

J’ai proposé une telle extension pour WASM sur le suivi des problèmes de conventions de l’outil, mais jusqu’à présent, il n’est allé nulle part.

Ce qui rend les ID de débogage si merveilleux, c’est qu’ils sont si agréables à utiliser. Votre chaîne d’outils émettra simplement ces fichiers et vous pourrez les mettre sur un serveur de symboles, où les débogueurs peuvent télécharger les binaires et déboguer les données. (En remarque, Sentry aime également les ID de débogage – même si vous n’utilisez pas de serveur de symboles, vous pouvez télécharger ces fichiers facilement dans votre organisation sur Sentry et cela fonctionnera.

Cartes sources

Le format de la carte source WASM est complètement inadéquat (vous ne pouvez pas obtenir les noms de fonction, ne pouvez pas accéder aux variables dans un débogueur, ne pouvez pas obtenir les informations de portée, etc.) même pour le débogage JavaScript, mais il a trouvé son chemin dans le WASM monde aussi. Je pense qu’il vaudrait mieux que nous oublions collectivement qu’ils existent dans un monde WASM, mais pour le moment, ils sont la seule chose qui soit assez largement soutenue. Ils ne sont pas géniaux, ne peuvent pas mapper les noms de fonctions et ne montrent que les points d’instruction * dans la version d’assemblage de texte * de WASM.

Informations de trace de pile

Imaginons tous les travaux ci-dessus. Nous ne sommes toujours pas sortis du bois. WASM vient rarement seul. À tout le moins, il est livré avec son ami JavaScript, mais ce n’est pas rare, il apporte également d’autres modules WASM. Bien que les modules WASM soient autonomes, ils peuvent exporter et importer des fonctions. Lorsque vous avez une trace de pile impliquant WASM, un nom de fichier encode les informations d’emplacement WASM et ressemble à ceci: ${url}:wasm-function[${funcIndex}]:${pcOffset}. Il nous indique l’index de la fonction et le décalage à l’intérieur. Malheureusement, vous ne pouvez pas savoir où chercher l’index des fonctions ou distinguer les modules WASM chargés, car deux fonctions différentes de deux modules différents peuvent avoir la même funcIndex.

Le débogage est toujours une niche

C’est le méta-reproche. Il semble que tout le monde soit très enthousiaste à propos de Web Assembly, mais presque personne ne souhaite s’assurer qu’il est déboguable. Il y a des gens formidables qui travaillent là-dessus bien sûr, mais c’est une communauté relativement soudée. Si vous souhaitez participer ou suivre, consultez l’un des projets suivants:

Close Menu