Comment GDB suspend une exécution

16

Comme vous le savez peut-être, nous pouvons utiliser GDB et définir des points d'arrêt sur notre code pour suspendre l'exécution pour le débogage.

Ma question est de savoir comment GDB suspend un processus et vous permet de visualiser le contenu des registres en utilisant i rpar exemple. Ces registres ne sont-ils pas constamment utilisés par d'autres processus du système d'exploitation? comment ne sont-ils pas écrasés?

S'agit-il uniquement d'un instantané du contenu et non de données en direct?

Joe
la source
2
Comment se fait-il que tous les registres ne soient pas écrasés lorsque le système d'exploitation décide de suspendre votre programme pendant un moment et d'en exécuter un autre?
user253751
CppCon 2018: Simon Brand «Comment fonctionnent les débogueurs C ++» youtube.com/watch?v=0DDrseUomfU
Robert Andrzejuk

Réponses:

24

Cela varie légèrement avec l'architecture, mais les points importants s'appliquent presque universellement:

  • L'interruption de service entraîne la sauvegarde de l'état du processeur (y compris les registres) avant d'exécuter l'ISR et sa restauration à la fermeture de l'ISR.

  • Si une routine de service d'interruption échange le contenu de l'emplacement de mémoire où ces registres sont enregistrés, elle peut effectuer un changement de contexte . Chaque thread a une région de mémoire où ses registres sont enregistrés lorsque le thread n'est pas en cours d'exécution.

  • Le changement de contexte est contrôlé par un planificateur de threads qui prend en compte si un thread attend les E / S, la synchronisation, quelle est sa priorité, la livraison du signal, etc. Souvent, il y a un compte de suspension qui est pris en compte.

  • Le débogueur peut incrémenter le nombre de suspensions, ce qui garantit que le thread n'est pas exécutable. Ensuite, il peut inspecter (et modifier) ​​la copie enregistrée des threads des registres.

Ben Voigt
la source
14

En plus des excellentes informations de @BenVoigt, permettez-moi de faire quelques ajouts:

Un point d'arrêt est défini par le débogueur en remplaçant une valeur de code machine (une instruction ou une partie d'une instruction) dans le processus en cours de débogage avec une instruction de déroutement particulière à l'emplacement dans le code qui correspond à la ligne (source) souhaitée pour la rupture. Cette instruction d'interruption particulière est destinée à être utilisée comme point d'arrêt - le débogueur le sait, tout comme le système d'exploitation.

Lorsque le processus / thread débogué atteint l'instruction d'interruption, cela déclenche le processus décrit par @Ben, qui comprend la moitié d'un échange de contexte qui suspend le thread en cours d'exécution (qui inclut l'enregistrement de son état CPU en mémoire) pour une reprise ultérieure potentielle. Étant donné que cette interruption est une interruption de point d'arrêt, le système d'exploitation maintient le processus en cours de débogage suspendu en utilisant peut-être un mécanisme décrit par @Ben, et notifie et finalement reprend le débogueur.

Le débogueur utilise ensuite les appels système pour accéder à l'état enregistré du processus / thread suspendu en cours de débogage.

Pour exécuter (reprendre) la ligne de code qui s'est cassée (qui a maintenant l'instruction de déroutement particulière), le débogueur restaurera la valeur de code machine d'origine qu'il a écrasée avec l'instruction de déroutement de point d'arrêt, peut-être définir un autre déroutement ailleurs (par exemple, si une seule étape, ou l'utilisateur crée de nouveaux points d'arrêt), et marquez le processus / thread comme exécutable, en utilisant peut-être un mécanisme comme décrit @Ben.

Les détails réels peuvent être plus compliqués, en ce sens que le maintien d'un point d'arrêt de longue durée qui est atteint signifie faire quelque chose comme échanger l'interruption de point d'arrêt pour du code réel afin que la ligne puisse s'exécuter, puis échanger à nouveau le point d'arrêt ...

Ces registres ne sont-ils pas constamment utilisés par d'autres processus du système d'exploitation? comment ne sont-ils pas écrasés?

Comme @Ben le décrit, en utilisant la fonction de suspension / reprise de threads déjà existante (le changement de contexte / échange de multitâche ) qui permet aux processeurs d'être partagés par plusieurs processus / threads en utilisant le découpage temporel.

S'agit-il uniquement d'un instantané du contenu et non de données en direct?

C'est les deux. Étant donné que le thread qui a atteint le point d'arrêt est suspendu, c'est un instantané des données en direct (registres du processeur, etc.) au moment de la suspension, et le maître faisant autorité des valeurs du registre du processeur à restaurer dans le processeur si le thread est repris. . Si vous utilisez l'interface utilisateur du débogueur pour lire et / ou modifier les registres du processeur (du processus en cours de débogage), il lira et / ou modifiera cet instantané / maître à l'aide d'appels système.

Erik Eidt
la source
1
Eh bien, la plupart des architectures de processeur prennent en charge les pièges de débogage qui se déclenchent par exemple lorsque l'IP (pointeur d'instruction) est égal à l'adresse stockée dans un registre de points d'arrêt, ce qui évite de réécrire le code. (En faisant correspondre des registres autres que IP, vous pouvez obtenir des points d'arrêt de données et en interceptant après chaque instruction, vous pouvez obtenir un pas à pas unique) Ce que vous avez décrit est également possible bien sûr, tant que le code n'est pas dans une mémoire en lecture seule.
Ben Voigt
Re "Si vous modifiez les registres CPU ..." dans le dernier paragraphe, je pense que vous voulez dire "Si vous modifiez la copie enregistrée des registres CUP ..." Ensuite, lorsque le système d'exploitation reprend le processus, ces données modifiées sont réécrites aux registres réels.
jamesqf
@jamesqf, oui, merci!
Erik Eidt
@BenVoigt, d'accord. bien que les débogueurs puissent gérer un nombre illimité de points d'arrêt, le matériel peut en gérer zéro ou quelques-uns, donc le débogueur doit faire quelques jonglages.
Erik Eidt
@jamesqf: Décrire cela comme une copie est un peu trompeur. Il s'agit du stockage officiel de l'état du thread lorsque le thread n'est pas en cours d'exécution.
Ben Voigt
5

À strictement parler, au moins dans la plupart des cas typiques, gdb lui-même ne suspend pas l'exécution. Au lieu de cela, gdb demande à l'OS, et l'OS suspend l'exécution.

Cela pourrait initialement sembler être une distinction sans différence - mais honnêtement, il y a vraiment une différence. La différence est la suivante: cette capacité est déjà intégrée dans le système d'exploitation typique, car elle doit pouvoir suspendre et redémarrer l'exécution du thread de toute façon - lorsqu'un thread n'est pas planifié pour s'exécuter (par exemple, il a besoin d'une ressource qui n'est pas disponible actuellement), le système d'exploitation doit le suspendre jusqu'à ce qu'il puisse être planifié pour s'exécuter.

Pour ce faire, le système d'exploitation dispose généralement d'un bloc de mémoire réservé à chaque thread pour enregistrer l'état actuel de la machine. Lorsqu'il doit interrompre un thread, l'état actuel de la machine est enregistré dans cette zone. Lorsqu'il doit reprendre un thread, l'état de la machine est restauré à partir de cette zone.

Lorsque le débogueur a besoin de suspendre un thread, le système d'exploitation met ce thread en pause exactement de la même manière que pour d'autres raisons. Ensuite, pour lire l'état du thread en pause, le débogueur examine l'état enregistré du thread. Si vous modifiez l'état, le débogueur écrit dans l'état enregistré, puis prend effet lorsque le thread reprend.

Jerry Coffin
la source