Développement de débogueurs de programmes à unités d'exécution multiples

La bibliothèque de débogage pthread (libpthdebug.a) fournit un ensemble de fonctions qui permettent aux développeurs de fournir des fonctions de débogage aux applications qui utilisent la bibliothèque pthread.

La bibliothèque de débogage pthread est utilisée pour déboguer les applications pthreaded 32 bits et 64 bits. Cette bibliothèque est utilisée pour déboguer les processus de débogage ciblés uniquement. Il peut également être utilisé pour examiner les informations pthread de sa propre application. Cette bibliothèque peut être utilisée par un débogueur à unités d'exécution multiples pour déboguer une application à unités d'exécution multiples. Les débogueurs à unités d'exécution multiples sont pris en charge dans la bibliothèque libpthreads.a , qui admet les unités d'exécution multiples. La bibliothèque de débogage pthread contient un objet partagé 32 bits et un objet partagé 64 bits.

Les débogueurs utilisant la fonction ptrace doivent se connecter à la version 32 bits de la bibliothèque, car la fonction ptrace n'est pas prise en charge en mode 64 bits. Les débogueurs utilisant la fonction /proc peuvent se connecter à la version 32 bits ou 64 bits de cette bibliothèque.

La bibliothèque de débogage pthread permet aux débogueurs d'accéder aux informations de la bibliothèque pthread. Cela inclut des informations sur les pthreads, les attributs pthread, les mutexes, les attributs mutex, les variables de condition, les attributs de variable de condition, les verrous en lecture / écriture, les attributs de verrou en lecture / écriture et des informations sur l'état de la bibliothèque pthread. Cette bibliothèque permet également de contrôler l'exécution de pthreads.

Remarque: toutes les données (adresses, registres) renvoyées par cette bibliothèque sont au format 64 bits pour les applications 64 bits et 32 bits. Il incombe au débogueur de convertir ces valeurs au format 32 bits pour les applications 32 bits. Lors du débogage d'une application 32 bits, la moitié supérieure des adresses et des registres est ignorée.

La bibliothèque de débogage pthread ne signale pas les mutex, les attributs mutex, les variables de condition, les attributs de variable de condition, les verrous en lecture / écriture et les attributs de verrou en lecture / écriture qui ont la valeur pshared de PTHREAD_PROCESS_SHARED.

Initialisation

Le débogueur doit initialiser une session de bibliothèque de débogage pthread pour chaque processus de débogage. Cette opération ne peut pas être effectuée tant que la bibliothèque pthread n'a pas été initialisée dans le processus de débogage. La fonction pthdb_session_pthreaded a été fournie pour indiquer au débogueur que la bibliothèque pthread a été initialisée dans le processus de débogage. Chaque fois que la fonction pthdb_session_pthreaded est appelée, elle vérifie si la bibliothèque pthread a été initialisée. S'il est initialisé, il renvoiePTHDB_SUCCESS. Sinon, elle renvoiePTHDB_NOT_PTHREADED. Dans les deux cas, il renvoie un nom de fonction qui peut être utilisé pour définir un point d'arrêt pour la notification immédiate que la bibliothèque pthread a été initialisée. Par conséquent, la fonction pthdb_session_pthreaded fournit les méthodes suivantes pour déterminer quand la bibliothèque pthread a été initialisée:
  • Le débogueur appelle la fonction chaque fois que le processus de débogage s'arrête, pour voir si le programme en cours de débogage est de type pthreaded.
  • Le débogueur appelle la fonction une seule fois et si le programme en cours de débogage n'est pas à unités d'exécution multiples, il définit un point d'arrêt pour avertir le débogueur lorsque le processus de débogage est à unités d'exécution multiples.

Une fois le processus de débogage traité, le débogueur doit appeler la fonction pthdb_session_init afin d'initialiser une session pour le processus de débogage. La bibliothèque de débogage pthread prend en charge une session pour un processus de débogage unique. Le débogueur doit affecter un identificateur utilisateur unique et le transmettre à pthdb_session_init qui, à son tour, affectera un identificateur de session unique qui doit être transmis en tant que premier paramètre à toutes les autres fonctions de la bibliothèque de débogage pthread, à l'exception de pthdb_session_pthreaded, en retour. Chaque fois que la bibliothèque de débogage pthread appelle une fonction de rappel, elle transmet au débogueur l'ID utilisateur unique affecté au débogueur. La fonction pthdb_session_init vérifie la liste des fonctions de rappel fournies par le débogueur et initialise les structures de données de la session. En outre, cette fonction définit les indicateurs de session.

Fonctions de rappel

La bibliothèque de débogage pthread utilise des fonctions de rappel pour effectuer les opérations suivantes:
  • Obtenir des adresses et des données
  • Ecrire des données
  • Attribuer la gestion du stockage au débogueur
  • Aide au débogage de la bibliothèque de débogage pthread

Fonction de mise à jour

Chaque fois que le débogueur est arrêté, après l'initialisation de la session, il est nécessaire d'appeler la fonction pthdb_session_update . Cette fonction définit ou réinitialise les listes des pthreads, des attributs pthread, des mutex, des attributs mutex, des variables de condition, des attributs de variable de condition, des verrous en lecture / écriture, des attributs de verrou en lecture / écriture, des clés spécifiques pthread et des clés actives. Il utilise des fonctions de rappel pour gérer la mémoire des listes.

Fonctions de mise en attente et de suppression de mise en attente

Les débogueurs doivent prendre en charge la mise en attente et l'annulation de la mise en attente des unités d'exécution pour les raisons suivantes:
  • Pour permettre à un utilisateur d'effectuer une seule étape d'une seule unité d'exécution, il doit être possible de conserver une ou plusieurs des autres unités d'exécution.
  • Pour que les utilisateurs puissent continuer à utiliser un sous-ensemble d'unités d'exécution disponibles, il doit être possible de conserver des unités d'exécution qui ne se trouvent pas dans l'ensemble.
La liste suivante répertorie les fonctions qui exécutent des tâches de mise en attente et de suppression de mise en attente:
  • La fonction pthdb_pthread_hold définit l' état de mise en attente d'un pthread surhold.
  • La fonction pthdb_pthread_unhold définit l' état de mise en attente d'une pthread surunhold.
    Remarque: les fonctions pthdb_pthread_hold et pthdb_pthread_unhold doivent toujours être utilisées, qu'un pthread ait ou non une unité d'exécution de noyau.
  • La fonction pthdb_pthread_holdstate renvoie l' état de mise en attente de pthread.
  • La fonction pthdb_session_committed indique le nom de la fonction qui est appelée après la validation de toutes les modifications de mise en attente et d'annulation de mise en attente. Un point d'arrêt peut être placé sur cette fonction pour avertir le débogueur que les modifications de mise en attente et de mise en attente ont été validées.
  • La fonction pthdb_session_stop_tid informe la bibliothèque de débogage pthread, qui informe la bibliothèque pthread de l'ID d'unité d'exécution (TID) de l'unité d'exécution qui a arrêté le débogueur.
  • La fonction pthdb_session_commit_tid renvoie la liste des unités d'exécution de noyau, une unité d'exécution de noyau à la fois, qui doivent être maintenues pour valider les modifications de mise en attente et d'annulation de mise en attente. Cette fonction doit être appelée à plusieurs reprises, jusqu'àPTHDB_INVALID_TIDest signalé. Si la liste des unités d'exécution du noyau est vide, il n'est pas nécessaire de continuer les unités d'exécution pour l'opération de validation.
Le débogueur peut déterminer à quel moment toutes les modifications apportées aux mises en attente et aux mises en attente ont été validées de l'une des manières suivantes:
  • Avant que l'opération de validation (poursuite de tous les tids renvoyés par la fonction pthdb_session_commit_tid ) ne soit démarrée, le débogueur peut appeler la fonction pthdb_session_committed pour obtenir le nom de la fonction et définir un point d'arrêt. (Cette méthode peut être effectuée une fois pour toute la durée du processus.)
  • Avant le démarrage de l'opération de validation, le débogueur appelle la fonction pthdb_session_stop_tid avec le TID de l'unité d'exécution qui a arrêté le débogueur. Lorsque l'opération de validation est terminée, la bibliothèque pthread s'assure que le même TID est arrêté qu'avant l'opération de validation.
Pour mettre en attente ou libérer des unités d'exécution, utilisez la procédure suivante avant de poursuivre un groupe d'unités d'exécution ou d'exécuter une seule étape sur une seule unité d'exécution:
  1. Utilisez les fonctions pthdb_pthread_hold et pthdb_pthread_unhold pour définir les pthreads qui seront détenus et ceux qui seront non détenus.
  2. Sélectionnez la méthode qui déterminera quand toutes les modifications de mise en attente et de non-mise en attente ont été validées.
  3. Utilisez la fonction pthdb_session_commit_tid pour déterminer la liste des ID de transaction qui doivent continuer à valider les modifications de mise en attente et d'annulation de mise en attente.
  4. Poursuivez les ID de transaction de l'étape précédente et de l'unité d'exécution qui a arrêté le débogueur.

La fonction pthdb_session_continue_tid permet au débogueur d'obtenir la liste des unités d'exécution du noyau qui doivent être poursuivies avant de passer à l'étape unique d'une seule unité d'exécution ou de poursuivre un groupe d'unités d'exécution. Cette fonction doit être appelée à plusieurs reprises, jusqu'àPTHDB_INVALID_TIDest signalé. Si la liste des unités d'exécution de noyau n'est pas vide, le débogueur doit continuer ces unités d'exécution de noyau avec les autres qui l'intéressent explicitement. Le débogueur est responsable de l'arrêt de l'unité d'exécution et de la poursuite de l'arrêt de l'unité d'exécution. L'unité d'exécution d'arrêt est l'unité d'exécution qui a provoqué l'entrée du débogueur.

Fonctions de contexte

La fonction pthdb_pthread_context obtient les informations de contexte et la fonction pthdb_pthread_setcontext définit le contexte. La fonction pthdb_pthread_context obtient les informations de contexte d'un pthread à partir du noyau ou de la structure de données pthread dans l'espace adresse du processus de débogage. Si pthread n'est pas associé à une unité d'exécution du noyau, les informations de contexte sauvegardées par la bibliothèque pthread sont obtenues. Si une unité d'exécution est associée à une unité d'exécution du noyau, les informations sont obtenues à partir du débogueur à l'aide de rappels. Il incombe au débogueur de déterminer si l'unité d'exécution du noyau est en mode noyau ou en mode utilisateur, puis de fournir les informations appropriées pour ce mode.

Lorsqu'un pthread avec une unité d'exécution de noyau est en mode noyau, vous ne pouvez pas obtenir le contexte de mode utilisateur complet car le noyau ne l'enregistre pas à un seul endroit. La fonction getthrds peut être utilisée pour obtenir une partie de ces informations, car elle sauvegarde toujours la pile en mode utilisateur. Le débogueur peut le reconnaître en vérifiant la structure thrdsinfo64.ti_scount . Si cette valeur est différente de zéro, la pile en mode utilisateur est disponible dans la structure thrdsinfo64.ti_ustk . A partir de la pile de mode utilisateur, il est possible de déterminer le registre d'adresse d'instruction (IAR) et les trames de rappel, mais pas les autres valeurs de registre. La structure thrdsinfo64 est définie dans le fichier procinfo.h .

Fonctions de liste

La bibliothèque de débogage pthread gère des listes pour les pthreads, les attributs pthread, les mutexes, les attributs mutex, les variables de condition, les attributs de variables de condition, les verrous en lecture / écriture, les attributs de verrou en lecture / écriture, les clés spécifiques pthread et les clés actives, chacune représentée par un descripteur spécifique au type. Les fonctions pthdb_objet renvoient le descripteur suivant dans la liste appropriée, où objet est l'un des suivants: pthread, attr, mutex, mutexattr, cond, condattr, rwlock, rwlockattr ou key. Si la liste est vide ou que la fin de la liste est atteinte, l'objet PTHDB_INVALID_ est signalé, où object est l'un des suivants: PTHREAD, ATTR, MUTEX, MUTEXATTR, COND, CONDATTR, RWLOCK, RWLOCKATTR ou KEY.

Fonctions de zone

Des informations détaillées sur un objet peuvent être obtenues à l'aide de la fonction de membre d'objet pthdb_objet_zone, où objet est l'un des suivants: pthread, attr, mutex, mutexattr, cond, condattr, rwlock, rwlockattr ou key et où zone est le nom d'une zone des informations détaillées de l'objet.

Personnalisation de la session

La fonction pthdb_session_setflags permet au débogueur de modifier les indicateurs qui personnalisent la session. Ces indicateurs contrôlent le nombre de registres lus ou écrits lors des opérations de contexte et contrôlent l'impression des informations de débogage.

La fonction pthdb_session_flags obtient les indicateurs en cours de la session.

Arrêt de la session

A la fin de la session de débogage, les structures de données de session doivent être libérées et les données de session doivent être supprimées. Pour ce faire, vous pouvez appeler la fonction pthdb_session_destroy , qui utilise une fonction de rappel pour libérer la mémoire. Toute la mémoire allouée par les fonctions pthdb_session_init et pthdb_session_update sera libérée.

Exemple de fonctions hold / unhold

L'exemple de pseudocode suivant montre comment le débogueur utilise le code hold / unhold:

/* includes */

#include <sys/pthdebug.h>

main()
{
    tid_t stop_tid; /* thread which stopped the process */
    pthdb_user_t user = <unique debugger value>;
    pthdb_session_t session; /* <unique library value> */
    pthdb_callbacks_t callbacks = <callback functions>;
    char *pthreaded_symbol=NULL;
    char *committed_symbol;
    int pthreaded = 0;
    int pthdb_init = 0;
    char *committed_symbol;

    /* fork/exec or attach to the program that is being debugged */

    /* the program that is being debugged uses ptrace()/ptracex() with PT_TRACE_ME */

    while (/* waiting on an event */) 
    {
      /* debugger waits on the program that is being debugged */

      if (pthreaded_symbol==NULL) {
        rc = pthdb_session_pthreaded(user, &callbacks, pthreaded_symbol);
        if (rc == PTHDB_NOT_PTHREADED)
        {
            /* set breakpoint at pthreaded_symbol */
        }
        else
          pthreaded=1;	
      }
      if (pthreaded == 1 && pthdb_init == 0) {
          rc = pthdb_session_init(user, &session, PEM_32BIT, flags, &callbacks);
          if (rc)
              /* handle error and exit */
          pthdb_init=1;
      }
  
      rc = pthdb_session_update(session)
      if ( rc != PTHDB_SUCCESS)
	/* handle error and exit */

      while (/* accepting debugger commands */)
      {
          switch (/* debugger command */)
          {
              ...
              case DB_HOLD:
                  /* regardless of pthread with or without kernel thread */
                  rc = pthdb_pthread_hold(session, pthread);
                  if (rc)
                      /* handle error and exit */
              case DB_UNHOLD:
                  /* regardless of pthread with or without kernel thread */
                  rc = pthdb_pthread_unhold(session, pthread);
                  if (rc)
                      /* handle error and exit */
              case DB_CONTINUE:
                  /* unless we have never held threads for the life */
                  /* of the process */
                  if (pthreaded)
                  {
                      /* debugger must handle list of any size */
                      struct pthread commit_tids;
                      int commit_count = 0;
                      /* debugger must handle list of any size */
                      struct pthread continue_tids;
                      int continue_count = 0;
		      
		      rc = pthdb_session_committed(session, committed_symbol);
		      if (rc != PTHDB_SUCCESS)
			  /* handle error */
	              /* set break point  at committed_symbol */	
		      
                      /* gather any tids necessary to commit hold/unhold */
                      /* operations */
                      do
                      {
                          rc = pthdb_session_commit_tid(session, 
                                                &commit_tids.th[commit_count++]);
                          if (rc != PTHDB_SUCCESS)
                              /* handle error and exit */
                      } while (commit_tids.th[commit_count - 1] != PTHDB_INVALID_TID);
  
                      /* set up thread which stopped the process to be */
                      /* parked using the stop_park function*/
  
		      if (commit_count > 0) {
                        rc = ptrace(PTT_CONTINUE, stop_tid, stop_park, 0, 
                                                              &commit_tids);
                        if (rc)
                            /* handle error and exit */
  
                        /* wait on process to stop */
		      }
  
                      /* gather any tids necessary to continue */
                      /* interesting threads */
                      do
                      {
                          rc = pthdb_session_continue_tid(session, 
                                          &continue_tids.th[continue_count++]);
                           if (rc != PTHDB_SUCCESS)
                              /* handle error and exit */
                      } while (continue_tids.th[continue_count - 1] != PTHDB_INVALID_TID);
  
                      /* add interesting threads to continue_tids */
  
                      /* set up thread which stopped the process to be parked */
                      /* unless it is an interesting thread */
  
                      rc = ptrace(PTT_CONTINUE, stop_tid, stop_park, 0, 
                                                                &continue_tids);
                      if (rc)
                          /* handle error and exit */
                  }
              case DB_EXIT:
		rc = pthdb_session_destroy(session);
		/* other clean up code */
		exit(0);
              ...
          }
      }
  
    }
    exit(0);
}