Partager via


Débogage en direct du processus distant de Linux

Cet article explique comment établir une connexion WinDbg en direct à Linux. Le débogage en direct du processus distant de Linux nécessite la version 1.2402.24001.0 de WinDbg ou supérieure.

Le débogueur de GNU, GDBServer, est utilisé sur Linux pour prendre en charge la connexion WinDbg. Pour plus d’informations sur GDBServer, consultez https://en.wikipedia.org/wiki/Gdbserver. Pour consulter la documentation du débogage gdb distant, utilisez le lien suivant : https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging

Les exemples de cette page utilisent le Sous-système Windows pour Linux (WSL), mais d’autres implémentations Linux peuvent également être utilisées.

Types de débogage de processus distants WinDbg

Le débogage distant avec WinDbg s’effectue au moyen de deux méthodes principales : un serveur de processus ou un serveur de connexion KD. Les serveurs de processus sont utilisés pour le débogage en mode utilisateur ; les serveurs de connexion KD pour le débogage en mode noyau. Consultez Serveurs de processus (mode utilisateur) et Serveurs de connexion KD (mode noyau) pour obtenir des informations générales sur ces types de connexions WinDbg.

Il existe deux façons de lancer le débogage des processus en mode utilisateur sous Linux. Soit on lance gdbserver sur un processus particulier, soit sur un serveur de processus qui peut répertorier les processus existants et s’y attacher. Cela ressemble beaucoup au serveur de processus DbgSrv (dbgsrv.exe) sur Windows. Pour en savoir plus, consultez Activation d’un serveur de processus.

Débogage du processus Linux en mode utilisateur

Il est possible de se connecter à un processus spécifique en mode utilisateur unique ou, en multimode, pour afficher tous les processus dans une liste et en sélectionner un pour se connecter. Les deux méthodes sont décrites dans cette rubrique. Elles partagent la même syntaxe de la chaîne de connexion, décrite ci-dessous.

Format de la chaîne de connexion gdbserver

Le format utilisé pour se connecter à gdbserver est « protocol :arguments », où les arguments sont une liste d’« argument=value » séparés par des virgules. Pour une connexion gdbserver en mode utilisateur, le protocole est gdb et l’ensemble d’arguments est le suivant.

server=<address> - Obligatoire : indique l’adresse IP du gdbserver auquel se connecter.

port=<port> - Obligatoire : indique le numéro de port du gdbserver auquel se connecter.

threadEvents=<true|false> - Facultatif : indique si les événements de threads pour cette version de gdbserver fonctionnent correctement en mode arrêt.

Les versions actuelles de gdbserver présentent un problème : l’activation des événements de thread avec un serveur en mode arrêt (que WinDbg utilise) provoque le plantage de gdbserver. Si cette valeur est false (valeur par défaut), les événements de démarrage et d’arrêt du thread sont synthétisés, mais peuvent apparaître beaucoup plus tard que l’heure réelle de création/destruction des threads. Lorsqu’un correctif sera disponible dans gdbserver, les événements réels pourront être activés via cette option.

Connexion à un processus en mode utilisateur unique

Cette section explique comment identifier et se connecter à un processus en mode utilisateur unique sous Linux, à l’aide de WinDbg.

WSL (Sous-système Windows pour Linux)

Les exemples de cette page utilisent le WSL (Sous-système Windows pour Linux), mais d’autres implémentations Linux peuvent également être utilisées. Pour en savoir plus sur la configuration et l’utilisation du WSL, consultez :

Sélectionnez le processus souhaité

Répertoriez les processus dans Linux à l’aide de la commande ps -A pour déterminer le processus en cours d’exécution auquel se connecter.

user1@USER1:/mnt/c/Users/USER1$ ps -A
    PID TTY          TIME CMD
    458 pts/1    00:00:00 bash
    460 ?        00:00:00 rtkit-daemon
    470 ?        00:00:00 dbus-daemon
    482 ?        00:00:19 python3
   1076 ?        00:00:00 packagekitd
   1196 pts/0    00:00:00 ps

Dans cet exemple de procédure pas à pas, nous allons nous connecter à Python3.

Localisez l’adresse IP du système cible

Si vous vous connectez à une cible Linux distante, utilisez une commande telle que ip route showpour déterminer l’adresse IP externe.

user1@USER1:/mnt/c/Users/USER1$ ip route show
default via 192.168.1.1 dev enp3s0 proto dhcp metric 100
172.25.144.0/24 dev enp3s0 proto kernel scope link src 192.168.1.107 metric 100

Dans cette procédure pas à pas, nous allons nous connecter à WSL qui s’exécute sur le même PC et utiliser l’adresse IP de localhost.

Attacher GDBServer au processus sélectionné

Sur la console Linux WSL, saisissez gdbserver localhost:1234 python3 pour lancer le gdbserver sur le port 1234 et l’attacher au processus Python3.

USER1@USER1:/mnt/c/Users/USER1$ gdbserver localhost:1234 python3
Process python3 created; pid = 1211
Listening on port 1234

Pour certains environnements Linux, il faut exécuter la commande en tant qu’administrateur, par exemple à l’aide de sudo - sudo gdbserver localhost:1234 python3. Soyez prudent en accordant à l’administrateur du débogueur l’accès au niveau racine et utilisez-le uniquement lorsque c’est nécessaire.

Créer la connexion du serveur de processus dans WinDbg

Ouvrez WinDbg, sélectionnez « Fichier/Connecter au débogueur distant », puis saisissez une chaîne de protocole pour la connexion. Dans cet exemple, nous allons utiliser gdb:server=localhost,port=1234.

capture d’écran de l’écran de démarrage de débogage de WinDbg montrant la chaîne de connexion.

Une fois que vous cliquez sur le bouton OK, le débogueur doit se connecter au gdbserver et vous devez être à la pause de démarrage du processus initial.

Une fois que vous êtes au point d’arrêt initial, vous pouvez appuyer sur « g » plusieurs fois. Vous obtiendrez des messages de chargement de module (et les événements de type sxe « pause au chargement du module » devraient fonctionner correctement).

Il convient de noter que cela peut prendre un certain temps pour atteindre ce point, car les symboles de débogage sont chargés dans le cache. En plus de la recherche de symboles et de binaires via le serveur de symboles ou votre chemin de recherche local, l’intégration de GDBServer est capable d’extraire ces fichiers du système de fichiers distant s’ils ne peuvent pas être trouvés via symsrv ou localement. Cette opération est généralement beaucoup plus lente que l’obtention de symboles à partir de symsrv ou d’un chemin de recherche local, mais elle permet d’améliorer l’expérience globale en localisant les symboles appropriés.

Utilisez la commande k stacks pour répertorier la pile. Nous voyons des modules Python3, ce qui confirme que nous déboguons python3 sur Linux à l’aide de WinDbg.

0:000> k
 # Child-SP          RetAddr               Call Site
00 00007fff`ffffce10 00007fff`f786d515     libc_so!_select+0xbd
01 00007fff`ffffce80 00005555`55601ce8     readline_cpython_310_x86_64_linux_gnu!PyInit_readline+0xac5
02 00007fff`ffffcf60 00005555`556f06a1     python3!PyOS_Readline+0x109
03 00007fff`ffffcfa0 00005555`556eee7e     python3!PyFrame_LocalsToFast+0x62a1
04 00007fff`ffffd000 00005555`556edcf0     python3!PyFrame_LocalsToFast+0x4a7e
05 00007fff`ffffdb80 00005555`557a18e9     python3!PyFrame_LocalsToFast+0x38f0
06 00007fff`ffffdc00 00005555`557a1470     python3!PyCodec_LookupError+0xb09
07 00007fff`ffffdc50 00005555`557b89dc     python3!PyCodec_LookupError+0x690
08 00007fff`ffffdc70 00005555`5560b42f     python3!PyUnicode_Tailmatch+0xc6c
09 00007fff`ffffdcb0 00005555`5560b012     python3!PyRun_InteractiveLoopObject+0x4e0
0a 00007fff`ffffdd50 00005555`557b7678     python3!PyRun_InteractiveLoopObject+0xc3
0b 00007fff`ffffdda0 00005555`555f55c8     python3!PyRun_AnyFileObject+0x68
0c 00007fff`ffffddd0 00005555`555ea6e8     python3!PyRun_AnyFileExFlags+0x4f
0d 00007fff`ffffde00 00005555`55780cad     python3!Py_str_to_int+0x2342a
0e 00007fff`ffffdef0 00007fff`f7c7cd90     python3!Py_BytesMain+0x2d
0f 00007fff`ffffdf20 00007fff`f7c7ce40     libc_so!_libc_init_first+0x90
10 00007fff`ffffdfc0 00005555`55780ba5     libc_so!_libc_start_main+0x80
11 00007fff`ffffe010 ffffffff`ffffffff     python3!start+0x25
12 00007fff`ffffe018 00000000`00000000     0xffffffff`ffffffff

À ce stade, vous devriez être en mesure de faire presque tout ce qui peut être fait en utilisant WinDbg attaché à un débogueur Windows distant via un serveur de processus distant. Vous pouvez parcourir, déboguer au niveau source, définir des points d’arrêt, inspecter les variables locales, etc.

Une fois le débogage terminé, utilisez Ctrl+D pour quitter la fenêtre gbdserver dans WSL.

Connexion à un serveur de processus

Outre la connexion à un processus unique via GDBServer en mode utilisateur, vous pouvez en configurer un en tant que serveur de processus, lister les processus existants sur le système et vous y attacher. Pour ce faire, gdbserver est lancé avec l’argument de ligne de commande « --multi » - gdbserver --multi localhost:1234

user1@USER1:/mnt/c/Users/USER1$ sudo gdbserver --multi localhost:1234
Listening on port 1234

Pour vous connecter au serveur de processus, sélectionnez « Fichier/Connecter au serveur de processus » dans WinDbg et saisissez la même chaîne de protocole que dans l’exemple gdbserver de processus unique ci-dessus :

gdb:server=localhost,port=1234

Une fois que vous avez cliqué sur le bouton « OK », vous devriez être connecté à gdbserver en tant que serveur de processus. Comme pour dbgsrv, vous pouvez soit créer un nouveau processus, soit lister les processus existants et vous attacher à l’un d’entre eux.

Dans cet exemple, utilisez l’option « Attacher au processus ».

capture d’écran de l’écran de débogage de WinDbg montrant l’attachement au processus avec près de 20 processus répertoriés.

Il convient de noter que vous verrez la plupart des éléments visibles pour les processus Windows (y compris le PID, l’utilisateur et la ligne de commande). Certaines colonnes de la boîte de dialogue « attacher au processus » ne sont pas pertinentes pour Linux et ne contiennent pas de données.

Terminer la session

Utilisez Ctrl+D pour quitter la fenêtre gbdserver dans WSL et sélectionnez Arrêter le débogage dans WinDbg. Pour mettre fin à la session, il est parfois nécessaire de quitter le débogueur.

Reconnexion au serveur de processus

WinDbg reconnaît un « serveur de processus » par rapport à une « cible unique » selon que le gdbserver est attaché à un processus ou non. Si vous vous attachez à un processus, que vous le laissez gelé, que vous fermez le débogueur et que vous essayez de vous reconnecter au serveur de processus, il est fort probable que nous ne le reconnaîtrons pas en tant que serveur de processus. Dans ce cas, redémarrez le gdbserver cible et reconnectez le débogueur.

Fonctionnalité WinDbg Linux

Alors que la plupart des fonctionnalités du débogueur fonctionneront comme prévu lors du débogage des vidages du noyau (par exemple : exploration de pile, symboles, informations de type, variables locales, désassemblage, etc...), il est important de noter que l’ensemble de la chaîne d’outils de débogage n’a pas encore été informé des ELF, DWARF, et des différences qui en résultent par rapport à la sémantique de Windows. Certaines commandes du débogueur peuvent actuellement produire des résultats inattendus. Par exemple, lm affiche toujours des informations erronées pour un module ELF, car il attend et analyse manuellement les en-têtes PE.

Mode noyau Linux via EXDI

Le débogueur Windows prend en charge le débogage du noyau à l’aide d’EXDI. Il est ainsi possible de déboguer une grande variété de matériels et de systèmes d’exploitation. Pour des informations générales sur la configuration et la résolution des problèmes liés aux connexions EXDI, consultez Configuration du transport du débogueur EXDI.

Pour en savoir plus sur la configuration du débogage en mode noyau QEMU à l’aide d’EXDI, consultez Configuration du débogage en mode noyau QEMU à l’aide d’EXDI.

Symboles et sources Linux

Cette section décrit l’utilisation de base et la disponibilité des symboles Linux. Pour en savoir plus, consultez les Symboles et sources Linux et l’Accès étendu au code source.

Serveurs de symboles DebugInfoD

À compter de la version 1.2104 de WinDbg, la commande de chemin d’accès source (.srcpath, .lsrcpath (Définir le chemin de la source)) prend en charge la récupération de fichiers à partir de serveurs DebugInfoD via la balise DebugInfoD*.

La balise DebugInfoD* peut pointer vers un ou plusieurs serveurs DebugInfoD avec chaque URL de serveur mise en forme avec https://domain.com et séparée par *. Les serveurs seront recherchés dans le même ordre que dans le chemin d’accès source et les fichiers seront récupérés depuis la première URL correspondante. Pour plus d’informations, consultez Source Code Extended Access (Accès étendu au code source).

Par exemple, vous pouvez utiliser la commande .sympath (Définir le chemin du symbole) pour définir un chemin DebugInfoD comme suit.

.sympath+ DebugInfoD*https://debuginfod.elfutils.org

Pour obtenir des informations générales sur la définition du chemin des symboles, consultez Utilisation de symboles.

Pour afficher des informations sur les symboles en cours de chargement, utilisez !sym noisy. Pour plus d’informations, consultez !sym.

Le téléchargement automatique de sources à partir de serveurs DebugInfoD qui prennent en charge le retour de ce type d’artefact est également pris en charge. Vous pouvez, en substance faire ce qui suit :

.srcpath+ DebugInfoD*https://debuginfod.elfutils.org

Pour en savoir plus sur l’utilisation des symboles DWARF et des utilitaires de symboles Linux, tels que !sourcemap et !diesym, consultez les Symboles et sources Linux.

Procédure pas à pas avec l’application C++

  1. Utilisez un éditeur de texte (tel que nano ou vi) pour créer votre fichier C++. Par exemple :

nano DisplayGreeting.cpp

  1. Dans l’éditeur de texte, écrivez votre programme C++. Voici un programme simple qui affiche des messages d’accueil et qui a besoin d’être débogué :
#include <array>
#include <cwchar>
#include <cstdio>
#include <iostream>
using namespace std;

void GetCppConGreeting(wchar_t* buffer, size_t size)
{
    wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!";
    wcsncpy(buffer, message, size);
}

int main()
{
    std::array<wchar_t, 50> greeting{};
    GetCppConGreeting(greeting.data(), greeting.size());

    cin.get();
    wprintf(L"%ls\n", greeting.data());

    return 0;
}
  1. Enregistrez (CTRL-O) et quittez (CTRL-X) l’éditeur nano.

  2. Compilez le fichier C++ à l’aide de g++. L’option -o est utilisée pour spécifier le nom du fichier de sortie, l’option -g génère un fichier de symboles :

g++ DisplayGreeting.cpp -g -o DisplayGreeting

  1. En l’absence d’erreurs dans votre code, la commande g++ crée un fichier exécutable nommé DisplayGreeting dans votre répertoire.

  2. Vous pouvez exécuter le programme à l’aide de la commande suivante :

./DisplayGreeting

  1. Appuyez sur la touche de retour pour afficher le message dans l’application. En examinant la sortie, il semble que le message d’accueil soit tronqué et que « ???? » s’affiche à la place.

HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YO????

Débogage de DisplayGreeting

  1. Une fois que le code est prêt à s’exécuter, nous pouvons démarrer l’application à l’aide de gdbserver.

gdbserver localhost:1234 DisplayGreeting

  1. Ouvrez WinDbg, sélectionnez « Fichier/Connecter au débogueur distant », puis saisissez une chaîne de protocole pour la connexion. Dans cet exemple, nous allons utiliser gdb:server=localhost,port=1234.

  2. Une fois connecté, la sortie doit indiquer qu’elle écoute sur le port 1234 et que la connexion de débogage à distance est établie.

Bob@Bob6:/mnt/c/Users/bob$ gdbserver localhost:1234 DisplayGreeting
Process /mnt/c/Users/bob/DisplayGreeting created; pid = 725
Listening on port 1234
Remote debugging from host 127.0.0.1, port 47700

Comme indiqué plus haut, pour certains environnements Linux, il faut exécuter la commande en tant qu’administrateur, généralement à l’aide de sudo. Soyez prudent en accordant à l’administrateur du débogueur l’accès au niveau racine et utilisez-le uniquement lorsque c’est nécessaire.

Ajouter les chemins d’accès aux sources et aux symboles à la session de débogage

Pour définir des points d’arrêt et afficher le code source et les variables, définissez le chemin d’accès aux symboles et à la source. Pour obtenir des informations générales sur la définition du chemin des symboles, consultez Utilisation de symboles.

Utilisez .sympath pour ajouter le chemin d’accès au symbole à la session du débogueur. Dans cet exemple, le code s’exécute à cet emplacement dans WSL Linux Ubuntu, pour un utilisateur nommé Bob.

\\wsl$\Ubuntu\mnt\c\Users\Bob\

Dans WSL, ce répertoire est mappé à l’emplacement du système d’exploitation Windows de : C:\Users\Bob\

Ces deux commandes sont donc utilisées.

.sympath C:\Users\Bob\

.srcpath C:\Users\Bob\

Pour en savoir plus sur le système de fichiers WSL, consultez Autorisations de fichier pour WSL.

  1. Pour utiliser des symboles supplémentaires du système d’exploitation Linux, ajoutez les symboles DebugInfoD à l’aide de l’emplacement .sympath, comme suit.

.sympath+ DebugInfoD*https://debuginfod.elfutils.org

  1. Le téléchargement automatique de sources à partir de serveurs DebugInfoD qui prennent en charge le retour de ce type d’artefact est également pris en charge. Pour en profiter, ajoutez le serveur elfutils à l’aide de .srcpath.

.srcpath+ DebugInfoD*https://debuginfod.elfutils.org

Définir un point d’arrêt

Définissez un point d’arrêt dans la partie principale de l’application DisplayGreeting.

0:000> bp DisplayGreeting!main
0:000> bl
     0 e Disable Clear  00005555`55555225  [/mnt/c/Users/bob/DisplayGreeting.cpp @ 14]     0001 (0001)  0:**** DisplayGreeting!main

Utilisez la commande Go ou l’option de menu pour redémarrer l’exécution du code.

Chargement du code source

Utilisez la commande .reload pour recharger les symboles.

Utilisez la commande lm pour confirmer que nous exécutons l’application DisplayGreeting.

0:000> lm
start             end                 module name
00005555`55554000 00005555`55558140   DisplayGreeting T (service symbols: DWARF Private Symbols)        c:\users\bob\DisplayGreeting
00007fff`f7a54000 00007fff`f7a732e8   libgcc_s_so   (deferred)             
00007fff`f7a74000 00007fff`f7b5a108   libm_so    (deferred)             
00007fff`f7b5b000 00007fff`f7d82e50   libc_so  T (service symbols: DWARF Private Symbols)        C:\ProgramData\Dbg\sym\_.debug\elf-buildid-sym-a43bfc8428df6623cd498c9c0caeb91aec9be4f9\_.debug
00007fff`f7d83000 00007fff`f7fae8c0   libstdc___so   (deferred)             
00007fff`f7fc1000 00007fff`f7fc1000   linux_vdso_so   (deferred)             
00007fff`f7fc3000 00007fff`f7ffe2d8   ld_linux_x86_64_so T (service symbols: DWARF Private Symbols)        C:\ProgramData\Dbg\sym\_.debug\elf-buildid-sym-9718d3757f00d2366056830aae09698dbd35e32c\_.debug

Une fois que la commande déclenche l’accès au code d’affichage du message d’accueil, celui-ci est affiché dans WinDbg.

capture d’écran du code DisplayGreeting.cpp dans WinDbg avec point d’arrêt défini sur la ligne 19, wprint

Utilisez la commande k pour répertorier la pile.

0:000> k
 # Child-SP          RetAddr               Call Site
00 00007fff`ffffde00 00007fff`f7b84d90     DisplayGreeting!main+0x1f [/mnt/c/Users/BOB/DisplayGreeting.cpp @ 15] 
01 00007fff`ffffdef0 00007fff`f7b84e40     libc_so!__libc_start_call_main+0x80 [./csu/../sysdeps/x86/libc-start.c @ 58] 
02 00007fff`ffffdf90 00005555`55555125     libc_so!__libc_start_main_impl+0x80 [./csu/../sysdeps/nptl/libc_start_call_main.h @ 379] 
03 00007fff`ffffdfe0 ffffffff`ffffffff     DisplayGreeting!start+0x25
04 00007fff`ffffdfe8 00000000`00000000     0xffffffff`ffffffff```

Utilisez la commande dx pour afficher la variable locale accueil. Notez que sa taille est 50.

0:000> dx greeting
greeting                 : { size=50 } [Type: std::array<wchar_t, 50>]
    [<Raw View>]     [Type: std::array<wchar_t, 50>]

Examinez le code et notez que 50 peut ne pas être une taille suffisante pour le message d’accueil.

wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL

Vérifiez cela en développant la variable locale pour le message d’accueil et en voyant que le message d’accueil est tronqué.

Résolution des problèmes liés à la connexion gdbserver

Utilisez l’option --debug pour afficher des informations supplémentaires sur la console gdbserver pour collecter plus d’informations sur l’état de la connexion. Par exemple, pour démarrer un serveur de processus, utilisez cette commande.

gdbserver --debug --multi localhost:1234

Voir aussi

Symboles et sources Linux

Accès étendu au code source

ELFUTILS debuginfod

Choix de la meilleure méthode de débogage à distance