Comment fonctionne un débogueur?


170

Je me demande comment fonctionne un débogueur? Particulièrement celui qui peut être «attaché» à un exécutable déjà en cours d'exécution. Je comprends que le compilateur traduit le code en langage machine, mais comment le débogueur "sait-il" à quoi il est attaché?



@Oktalist Cet article est intéressant mais ne parle que de l'abstraction au niveau de l'API pour le débogage sous Linux. Je suppose qu'OP veut en savoir plus sur sous le capot.
smwikipedia

Réponses:


96

Les détails du fonctionnement d'un débogueur dépendent de ce que vous déboguez et du système d'exploitation. Pour le débogage natif sur Windows, vous pouvez trouver quelques détails sur MSDN: API de débogage Win32 .

L'utilisateur indique au débogueur à quel processus se connecter, soit par nom, soit par ID de processus. S'il s'agit d'un nom, le débogueur recherchera l'ID de processus et lancera la session de débogage via un appel système; sous Windows, ce serait DebugActiveProcess .

Une fois attaché, le débogueur entrera dans une boucle d'événements un peu comme pour n'importe quelle interface utilisateur, mais au lieu d'événements provenant du système de fenêtrage, le système d'exploitation générera des événements en fonction de ce qui se passe dans le processus en cours de débogage - par exemple une exception se produisant. Voir WaitForDebugEvent .

Le débogueur est capable de lire et d'écrire la mémoire virtuelle du processus cible, et même d'ajuster ses valeurs de registre via les API fournies par le système d'exploitation. Consultez la liste des fonctions de débogage pour Windows.

Le débogueur peut utiliser les informations des fichiers de symboles pour traduire des adresses en noms et emplacements de variables dans le code source. Les informations du fichier de symboles constituent un ensemble distinct d'API et ne font pas partie intégrante du système d'exploitation en tant que tel. Sur Windows, c'est via le SDK Debug Interface Access .

Si vous déboguez un environnement géré (.NET, Java, etc.), le processus sera généralement similaire, mais les détails sont différents, car l'environnement de la machine virtuelle fournit l'API de débogage plutôt que le système d'exploitation sous-jacent.


5
Cette question peut sembler stupide, mais comment le système d'exploitation garde-t-il une trace si une adresse spécifique à l'intérieur du programme est atteinte. Par exemple, un point d'arrêt est défini sur l'adresse 0x7710cafe. Lorsque le pointeur d'instruction change, le système d'exploitation (ou peut-être le processeur) devra comparer le pointeur d'instruction avec toutes les adresses de point d'arrêt, ou est-ce que je me trompe? Comment cela marche-t-il ..?
displayname

3
@StefanFalk J'ai écrit une réponse qui aborde certains des détails de niveau inférieur (sur x86).
Jonathon Reinhart

Pouvez-vous expliquer comment exactement les noms de variables correspondent aux adresses? L'application utilise-t-elle les mêmes adresses de mémoire pour les variables à chaque exécution? J'avais toujours supposé qu'il venait d'être mappé à partir de la mémoire disponible, mais je n'avais jamais vraiment pensé à savoir si les octets seraient mappés directement au même endroit dans l'espace mémoire de l'application. Il semble que ce serait un problème de sécurité majeur.
James Joshua Street

@JamesJoshuaStreet J'imagine que c'est un détail spécifique au débogueur.
moonman239

Cette réponse révèle quelque chose. Mais je pense qu'op s'intéresse plus à certains détails de bas niveau qu'à certaines abstractions d'API.
smwikipedia

63

Si je comprends bien:

Pour les points d'arrêt logiciels sur x86, le débogueur remplace le premier octet de l'instruction par CC( int3). Ceci est fait avec WriteProcessMemorysur Windows. Lorsque le CPU arrive à cette instruction et exécute leint3 , le processeur génère une exception de débogage. Le système d'exploitation reçoit cette interruption, réalise que le processus est en cours de débogage et informe le processus de débogage que le point d'arrêt a été atteint.

Une fois le point d'arrêt atteint et le processus arrêté, le débogueur recherche dans sa liste de points d'arrêt et remplace le CCpar l'octet qui s'y trouvait à l'origine. Le débogueur définit TF, l'indicateur d'interruption dans EFLAGS(en modifiant le CONTEXT) et continue le processus. L'indicateur d'interruption amène le processeur à générer automatiquement une exception en une seule étape ( INT 1) sur l'instruction suivante.

Lorsque le processus en cours de débogage s'arrête la prochaine fois, le débogueur remplace à nouveau le premier octet de l'instruction de point d'arrêt par CCet le processus se poursuit.

Je ne sais pas si c'est exactement ainsi qu'il est implémenté par tous les débogueurs, mais j'ai écrit un programme Win32 qui parvient à se déboguer en utilisant ce mécanisme. Complètement inutile, mais éducatif.


25

Sous Linux, le débogage d'un processus commence par l' appel système ptrace (2) . Cet article contient un excellent didacticiel sur la façon d'utiliser ptracepour implémenter des constructions de débogage simples.


1
Est-ce que le (2)nous dit quelque chose de plus (ou moins) que "ptrace est un appel système"?
Lazer

5
@eSKay, non pas vraiment. Le (2)est le numéro de section du manuel. Voir en.wikipedia.org/wiki/Man_page#Manual_sections pour une description des sections du manuel.
Adam Rosenfield

2
@AdamRosenfield Sauf que la section 2 est spécifiquement "Appels système". Donc indirectement, oui, cela nous dit que ptracec'est un appel système.
Jonathon Reinhart

1
Plus concrètement, le (2)nous dit que nous pouvons taper man 2 ptraceet obtenir la bonne page de manuel - pas important ici car il n'y en a pas d'autre ptracepour lever l'ambiguïté, mais pour comparer man printfavec man 3 printfLinux.
slim

9

Si vous utilisez un système d'exploitation Windows, une excellente ressource pour cela serait «Débogage des applications pour Microsoft .NET et Microsoft Windows» par John Robbins:

(ou même l'ancienne édition: "Debugging Applications" )

Le livre contient un chapitre sur le fonctionnement d'un débogueur qui inclut du code pour quelques débogueurs simples (mais fonctionnels).

Comme je ne suis pas familier avec les détails du débogage Unix / Linux, cela peut ne pas s'appliquer du tout aux autres OS. Mais je suppose qu'en tant qu'introduction à un sujet très complexe, les concepts - sinon les détails et les API - devraient «porter» sur la plupart des systèmes d'exploitation.


3

Le manuel des processeurs Intel (Intel® 64 et IA-32 Architectures Software Developer's Manual) est une autre source précieuse pour comprendre le débogage. Dans le volume 3A, chapitre 16, il a introduit le support matériel du débogage, comme les exceptions spéciales et les registres de débogage matériel. Ce qui suit est tiré de ce chapitre:

T (trap) flag, TSS - Génère une exception de débogage (#DB) lorsqu'une tentative est faite pour basculer vers une tâche avec l'indicateur T défini dans son TSS.

Je ne sais pas si Windows ou Linux utilisent ce drapeau ou non, mais il est très intéressant de lire ce chapitre.

J'espère que cela aide quelqu'un.


2

Je pense qu'il y a deux questions principales auxquelles répondre ici:

1. Comment le débogueur sait-il qu'une exception s'est produite?

Lorsqu'une exception se produit dans un processus en cours de débogage, le débogueur est notifié par le système d'exploitation avant que les gestionnaires d'exceptions utilisateur définis dans le processus cible aient la possibilité de répondre à l'exception. Si le débogueur choisit de ne pas gérer cette notification d'exception (de première chance), la séquence de distribution d'exceptions continue et le thread cible a alors la possibilité de gérer l'exception s'il le souhaite. Si l'exception SEH n'est pas gérée par le processus cible, le débogueur reçoit alors un autre événement de débogage, appelé notification de seconde chance, pour l'informer qu'une exception non gérée s'est produite dans le processus cible.La source

entrez la description de l'image ici


2. Comment le débogueur sait-il s'arrêter sur un point d'arrêt?

La réponse simplifiée est: lorsque vous mettez un point d'arrêt dans le programme, le débogueur remplace votre code à ce stade par une instruction int3 qui est une interruption logicielle . En conséquence, le programme est suspendu et le débogueur est appelé.


1

Je crois comprendre que lorsque vous compilez une application ou un fichier DLL, tout ce qu'il compile contient des symboles représentant les fonctions et les variables.

Lorsque vous avez une version de débogage, ces symboles sont beaucoup plus détaillés que lorsqu'il s'agit d'une version de version, permettant ainsi au débogueur de vous donner plus d'informations. Lorsque vous attachez le débogueur à un processus, il regarde quelles fonctions sont actuellement accédées et résout tous les symboles de débogage disponibles à partir d'ici (puisqu'il sait à quoi ressemblent les éléments internes du fichier compilé, il peut déterminer ce qui pourrait être en mémoire , avec le contenu des entiers, des flottants, des chaînes, etc.). Comme l'a dit la première affiche, ces informations et le fonctionnement de ces symboles dépendent grandement de l'environnement et de la langue.


2
Il ne s'agit que de symboles. Le débogage est bien plus que des symboles.
Jonathon Reinhart
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.