Bugsnag a récemment ajouté la prise en charge du traitement des minidumps afin que les clients puissent suivre les plantages natifs sur Electron ou les plantages générés lors de l'utilisation de Breakpad ou Crashpad .
L'ajout de la prise en charge de minidump s'est accompagné d'un certain nombre de défis techniques que nous avons dû relever pour nous assurer que nous pouvons traiter les fichiers de manière efficace et évolutive sans affecter notre débit de traitement normal des événements d'erreur.
Un minidump est un fichier généré par certains processus lorsqu'ils se bloquent. Ils sont plus petits que les vidages mémoire et fournissent uniquement les données requises pour effectuer les opérations de débogage de base.
Le format de fichier minidump a été inventé pour être utilisé dans Windows lorsque le système d'exploitation rencontre une erreur inattendue. Par la suite, des outils tels que Breakpad et Crashpad de Google ont adopté le même format pour générer des vidages sur incident d'application sur plusieurs plates-formes. Les applications créées avec Electron génèrent également des fichiers minidump pour les plantages natifs (puisqu'Electron utilise Crashpad).
Le fichier minidump contient des informations sur le processus au moment du crash et inclut des détails tels que :
Dans le minidump, il y a une pile d'exécution de chaque thread actif (au moment où l'erreur s'est produite) et les valeurs de registre pour ces threads.
Ces détails peuvent être utilisés pour parcourir la pile et produire une trace de pile pour chaque thread. Dans certains cas, il est possible d'obtenir une trace de pile valide (bien que non symbolisée) en utilisant cette méthode seule, mais dans d'autres cas, nous avons besoin d'informations de trame d'appel (CFI) supplémentaires fournies dans les fichiers de débogage.
Les enregistrements d'informations de trame d'appel (CFI) décrivent comment restaurer les valeurs de registre à une adresse de machine particulière. Ces données sont généralement fournies dans les fichiers de débogage et permettent de générer une trace de pile plus fiable lors du parcours de la pile.
Sans les informations CFI, le marcheur de pile peut avoir besoin d'analyser la pile pour tenter de trouver le cadre appelant, mais cela peut être moins fiable et peut produire des traces de pile non valides.
Une fois qu'une trace de pile a été obtenue, elle ne contiendra que les adresses de chaque trame. Afin de produire une trace de pile significative (avec le nom de la fonction, le fichier source et le numéro de ligne source pour chaque image), nous devons la "symboliser", c'est-à-dire leur appliquer les symboles de débogage.
Le fichier de débogage pertinent du compilateur (par exemple dSYMs pour macOS) pourrait être utilisé pour cela, mais Breakpad a défini son propre format de fichier de symboles qui peut être utilisé à la place.
Le fichier de symboles Breakpad est plus simple que la plupart des fichiers de débogage générés par le compilateur (il ne contient pas de détails tels que l'arbre de syntaxe abstraite) mais fournit les détails requis, dans un format lisible, pour symboliser la trace de la pile.
Breakpad fournit un certain nombre d'outils pour aider à traiter manuellement un minidump ou à les télécharger sur un serveur/service dédié pour le traitement :
minidump_stackwalk Prend un fichier minidump et des fichiers de symboles Breakpad facultatifs et génère la trace de la pile pour chaque thread avec d'autres informations (telles que la raison du crash, les valeurs de registre pour chaque image, les détails du système d'exploitation, etc.). Il s'agit d'un outil utile pour analyser les minidumps et en obtenir des traces de pile significatives.
minidump_dump Fournit des informations plus détaillées sur le minidump (telles que les détails de chacun des flux dans le minidump).
minidump_upload Télécharge les fichiers minidump sur un serveur dédié pour traitement (tel que Bugsnag).
dump_syms Génère des fichiers de symboles Breakpad à partir des fichiers binaires et de débogage de l'application.
symupload Télécharge les fichiers de symboles Breakpad sur un serveur dédié pour traitement (tel que Bugsnag).
Un exemple de la sortie de l'outil minidump_stackwalk avec les fichiers de symboles pertinents peut être vu ci-dessous :
-- CODE language-plaintext --Système d'exploitation : Windows NT 10.0.19041 1151CPU : amd64 family 23 model 24 stepping 1 8 CPUs
GPU : INCONNU
Raison du plantage : Exception C++ non gérée Adresse du plantage : 0x7ff887f54ed9Temps de disponibilité du processus : 4 secondes
Thread 0 (crash) 0 KERNELBASE.dll + 0x34ed9
rax = 0x655c67616e736775 rdx = 0x6f6d2d6576697461 rcx = 0x6e2d7070635c656d rbx = 0x00007ff87f731600 rsi = 0x000000b80f3fcb70 rdi = 0x000000b80f3fca40 rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fc8d0 r8 = 0xaaaaaaaa0065646f r9 = 0xaaaaaaaaaaaaaaaa r10 = 0xaaaaaaaaaaaaaaaa r11 = 0xaaaaaaaaaaaaaaaa r12 = 0x00007ff87f6f1ba0 r13 = 0x000010ff084af60d r14 = 0xffffffff00000000 r15 = 0x0000000000000420 rip = 0x00007ff887f54ed9 Found by: given as instruction pointer in context 1 KERNELBASE.dll + 0x34ed9 rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fc908 rip = 0x00007ff887f54ed9 Found by: stack scanning 2 ntdll.dll + 0x34a5f rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fc960 rip = 0x00007ff88a6a4a5f Found by: stack scanning 3 my_example.node!CxxThrowException [throw.cpp : 131 + 0x14] rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fc9b0 rip = 0x00007ff87f6fab75 Trouvé par : analyse de la pile 4 my_example.node!RunExample(Nan ::I nfo v8::Value const &) [my_example.cpp : 26 + 0x22] rbp = 0x000000b80f3fca10 rsp = 0x000000b80f3fca20 rip = 0x00007ff87f6f1ec2 Trouvé par : call frame info.. .
Nous avons rencontré quelques difficultés techniques lors de l'ajout du support minidump à Bugsnag.
Les minidumps varient beaucoup de nos charges utiles JSON d'événement d'erreur habituelles, nous avons donc dû nous assurer que nous pouvions les traiter de manière efficace, résiliente et évolutive.
Les fichiers Minidump peuvent être beaucoup plus volumineux que les charges utiles normales que nous recevons ; nos charges utiles normales sont en moyenne d'environ 20 Ko, tandis que les minidumps ont généralement une taille de plusieurs centaines de kilo-octets. De plus, les minidumps peuvent devenir assez volumineux (des dizaines de mégaoctets) s'il y a beaucoup de threads actifs ou de threads avec de grandes piles.
Normalement, lorsque nous recevons des charges utiles d'événements d'erreur, nous les ajoutons à une file d'attente Kafka pour un traitement asynchrone afin que nous soyons en mesure de gérer tout arriéré avec des téléchargements.
Nous devions nous assurer que le mécanisme de mise en file d'attente serait fiable si nous mettions en file d'attente des fichiers minidump plus volumineux. Les fichiers minidump se compressent bien (généralement à environ 10 % de leur taille d'origine), mais il y avait toujours un risque que les fichiers compressés soient trop volumineux.
Nous avons effectué des tests de charge avec des messages de différentes tailles sur une instance interne de Kafka et avons constaté que :
Le débit de données et le décalage de réplication n'étaient pas vraiment affectés par la taille des fichiers.
Le nombre de messages pouvant être traités par seconde diminuait à mesure que la taille moyenne des fichiers augmentait.
Cette diminution du débit des messages n'était significative que lors de la mise en file d'attente simultanée de nombreux fichiers particulièrement volumineux, mais nous prévoyons que ceux-ci devraient être très rares et que Kafka conviendra donc à cet effet.
Lors de la symbolisation d'un minidump à l'aide de l'outil minidump_stackwalk de Breakpad, le traitement du minidump peut prendre beaucoup plus de temps lorsque les fichiers de symboles Breakpad sont fournis (en raison du temps nécessaire pour charger les fichiers de symboles, les analyser et rechercher les données de symboles pertinentes).
Sur un échantillon de minidump Electron, il a fallu 20 ms sans les fichiers de symboles Breakpad et 14 s avec eux !
Le temps de traitement plus lent n'est pas trop un problème lors de la symbolisation manuelle d'un seul minidump, mais nous devons nous assurer que nous pouvons traiter et symboliser les minidumps aussi efficacement que possible afin de pouvoir maintenir un débit élevé de traitement des minidumps.
Pour cela, nous avons implémenté notre propre logique de symbolisation. Le format de fichier de symboles Breakpad est simple et bien documenté, ce qui signifie que nous pouvons analyser le fichier pour produire un fichier de mappage sur mesure qui permet une recherche d'adresse facile.
Le fichier sur mesure est beaucoup plus volumineux que le fichier de symboles Breakpad, mais il est également beaucoup plus efficace pour effectuer les recherches. Faire ce pré-traitement du fichier de symboles Breakpad à l'avance signifie que le temps nécessaire pour traiter un minidump est considérablement réduit (au prix d'une augmentation des besoins de stockage pour les fichiers de symboles).
Dans la conception initiale du traitement du minidump, nous avons complètement omis l'utilisation des fichiers de symboles Breakpad lors de la navigation dans la pile (pour améliorer l'efficacité), mais nous nous sommes vite rendu compte que cela entraînait parfois des traces de pile non valides en raison des données d'informations de trame d'appel manquantes.
Nous savions que si nous transmettions les fichiers de symboles Breakpad complets pour la marche de la pile, cela serait lent (car il essayait également de symboliser la trace de la pile), donc nous avons plutôt opté pour la production d'une version allégée du fichier qui contenait juste le informations nécessaires pour parcourir la pile.
Cela a considérablement réduit la taille du fichier de symboles Breakpad ainsi que le temps nécessaire pour traiter le minidump, mais ce n'était toujours pas très efficace (en prenant 1,5 s pour un exemple de minidump Electron).
Nous avons donc exploré la possibilité de sérialiser le fichier de symboles Breakpad rogné afin qu'il puisse être lu plus efficacement (plutôt que d'avoir à analyser le fichier à chaque fois). L'utilisation d'une version sérialisée du fichier a réduit le temps de traitement de 1,5 s à 200 ms.
Cette amélioration des performances signifie que nous devrions être capables de prendre en charge un débit plus élevé de minidumps par instance du service, ce qui signifie que nous pouvons réduire les coûts d'infrastructure.
Au fur et à mesure que l'adoption de la nouvelle fonctionnalité se développera, nous surveillerons de près l'utilisation de notre infrastructure pour nous assurer que nous continuons à traiter les minidumps de manière efficace et voir s'il y a d'autres améliorations de performances à apporter.
Bugsnag vous aide à hiérarchiser et à corriger les bogues logiciels tout en améliorant la stabilité de votre application.