[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]

Программирование: Разработка и отладка программ


Разработка отладчиков для программ с несколькими нитями

Библиотека отладки нитей (libpthdebug.a) содержит набор функций, предназначенных для отладки программ, в которых используется библиотека нитей.

Эта библиотека предназначена для отладки 32- и 64-разрядных приложений с несколькими нитями. С ее помощью можно отлаживать только внешние процессы. Это означает, что ее нельзя подключить к приложению с нитями для анализа информации о нитях этого приложения. См. Разработка программ с несколькими нитями, которые проверяют и изменяют объекты из библиотеки phread. На основе библиотеки можно создать отладчик с несколькими нитями, предназначенный для приложений с несколькими нитями. Отладчики программ с несколькими нитями поддерживаются библиотекой libpthreads.a. Эта библиотека обеспечивает защиту нитей. Библиотека отладки нитей содержит 32-разрядный общий объект.

Отладчики, пользующиеся ptrace, могут применять только 32-разрядную версию этой библиотеки, потому что ptrace не поддерживается в 64-разрядном режиме. Отладчики, пользующиеся /proc, могут применять и 32-, и 64-разрядную версию этой библиотеки.

Библиотека отладки нитей предоставляет отладчикам доступ к информации, хранящейся в библиотеке нитей. Она включает данные о нитях и их атрибутах, взаимных блокировках и их атрибутах, переменных состояния и их атрибутах, блокировках ввода-вывода и их атрибутах, а также информацию о состоянии библиотеки нитей. Кроме того, в этой библиотеке содержатся средства для управления работой нитей.

Примечание: Все данные (адреса и регистры), возвращаемые функциями библиотеки, представлены в 64-разрядном формате как для 64-разрядных, так и для 32-разрядных приложений. Преобразование этих значений в 32-разрядный формат для 32-разрядных приложений должно выполняться отладчиком. При отладке 32-разрядных приложений старшие 32 разряда адреса или регистра игнорируются.

Примечание: отладочная библиотека pthread не позволяет отслеживать объекты mutex, mutexattr, conds condattr, rwlock и rwlockattrs, у которых атрибуту pshared присвоено значение PTHREAD_PROCESS_SHARED.

Инициализация

Отладчик должен инициализировать сеанс библиотеки отладки нитей для каждого процесса отладки. Это невозможно сделать до инициализации библиотеки нитей. Отладчик может узнать об инициализации библиотеки нитей с помощью функции pthdb_session_pthreaded(). Функция pthdb_session_pthreaded() проверяет, инициализирована ли библиотека нитей. Если да, то функция возвращает значение PTHDB_SUCCESS. Если нет, то возвращается значение PTHDB_NOT_PTHREADED. В любом случае возвращается имя функции, с помощью которой можно установить точку прерывания для немедленного уведомления об инициализации библиотеки нитей. Следовательно, существует два способа узнать об инициализации библиотеки нитей с помощью функции pthdb_session_pthreaded():

После инициализации библиотеки нитей отладчик должен вызвать функцию pthdb_session_init() для инициализации сеанса отладки. Библиотека отладки нитей поддерживает только один сеанс для каждого процесса отладки. Отладчик должен присвоить уникальный ИД пользователя и передать его в функцию pthdb_session_init(), которая, в свою очередь, должна присвоить уникальный идентификатор сеансу. Этот идентификатор передается в качестве первого параметра всем функциям библиотеки отладки нитей, кроме функции pthdb_session_pthreaded(). Когда библиотека отладки нитей вызывает функцию Функции callback для работы с несколькими нитями, она передает отладчику выделенный им идентификатора пользователя. Функция pthdb_session_init() проверяет список функций callback, созданный отладчиком, и инициализирует структуры данных сеанса. Кроме того, эта функция устанавливает флаги сеанса. См. описание функции pthdb_session_setflags.

Функции callback

С помощью функций callback библиотека отладки нитей считывает адреса и данные, записывает данные, позволяет отладчику работать с памятью и выполняет вспомогательные функции отладки. Дополнительная информация об этом приведена в разделе Функции callback для работы с несколькими нитями.

Функция обновления

При каждой остановке отладчика после инициализации сеанса необходимо вызывать функцию pthdb_session_update(). Эта функция устанавливает и обновляет списки нитей, атрибутов нитей, взаимных блокировок, атрибутов взаимных блокировок, переменных условия, атрибутов переменных условия, блокировок чтения/записи, атрибутов блокировок чтения/записи, ключей отдельных нитей и активных ключей. Память для списков выделяется с помощью функций callback.

Функции блокирования и освобождения

Отладчики должны поддерживать блокирование и освобождение нитей по двум причинам:

Функция pthdb_pthread_hold() устанавливает для флага состояния блокировки нити значение заблокирована.

Функция pthdb_pthread_unhold() устанавливает для флага состояния блокировки нити значение разблокирована.

Примечание: Функции pthdb_pthread_hold() и pthdb_pthread_unhold() должны применяться как для пользовательских нитей, так и для нитей ядра.

Функция pthdb_pthread_holdstate() возвращает значение флага состояния блокировки нити.

Функция pthdb_session_committed() возвращает имя функции, которая будет вызвана после фиксации всех изменений состояния блокировки нитей. В эту функцию можно поместить точку прерывания, чтобы сообщить отладчику о фиксации изменений.

Функция pthdb_session_stop_tid() передает в библиотеку отладки нитей ИД нити, вызвавшей остановку отладчика. Далее эта информация передается в библиотеку нитей.

Функция pthdb_session_commit_tid() возвращает поочередно возвращает ИД нитей ядра, работу которых нужно возобновить для фиксации изменений. Эту функцию нужно вызывать в цикле до тех пор, пока не будет получено значение PTHDB_INVALID_TID. Если список нитей ядра пуст, для фиксации не нужно возобновлять работу каких-либо нитей.

У отладчика есть два способа узнать о том, что все изменения состояния блокировки зафиксированы:

Для блокировки или освобождения нитей перед отладкой группы нитей или пошаговой отладкой одной нити нужно выполнить следующие действия:

  1. Заблокируйте и освободите нужные функции с помощью функций pthdb_pthread_hold() и pthdb_pthread_unhold() соответственно.
  2. Вызовите метод, проверяющий, все ли изменения состояния блокировки зафиксированы.
  3. С помощью функции pthdb_session_commit_tid() узнайте список ИД нитей, обработку которых необходимо продолжить для фиксации изменений.
  4. Возобновите работу нитей из этого списка, а также нити, вызвавшей остановку отладчика.

С помощью функции pthdb_session_continue_tid() отладчик может получить список нитей ядра, работу которых необходимо продолжить до перехода к пошаговой отладке нити или продолжения отладки группы нитей. Эту функцию нужно вызывать в цикле до тех пор, пока не будет получено значение PTHDB_INVALID_TID. Если список нитей ядра не пуст, отладчик должен возобновить работу указанных в нем нитей вместе с отлаживаемыми нитями. Остановка и возобновление работы отлаживаемой нити выполняется отладчиком. Отлаживаемая нить - это нить, вызвавшая остановку отладчика.

Функции работы с контекстом

Функция pthdb_pthread_context() считывает контекст, а функция pthdb_pthread_setcontext() устанавливает контекст. Функция pthdb_pthread_context() считывает контекст пользовательской нити из структуры данных нити ядра или пользовательской нити. Эта структура данных расположена в адресном пространстве процесса отладки. Если пользовательская нить не связана с нитью ядра, информация о контексте считывается из библиотеки нитей. Если пользовательская нить связана с нитью ядра, эта информация считывается из отладчика с помощью функций callback. При этом отладчик должен определить, находится ли нить ядра в режиме ядра или в пользовательском режиме, и выдать информацию для соответствующего режима.

Если пользовательская нить связана в нитью ядра, находящейся в режиме ядра, невозможно считать полную информацию о контексте для пользовательского режима, так как ядро хранит ее компоненты в разных местах. Для считывания неполной информации можно вызвать функцию getthrds(). Она всегда сохраняет стек пользовательского режима. Отладчик может получить доступ к этой информации, проверив значение thrdsinfo64.ti_scount. Если это значение отлично от нуля, стек пользовательского режима находится в thrdsinfo64.ti_ustk. С помощью стека пользовательского режима можно определить iar и страницы функций callback, но нельзя узнать значения других регистров. Определение структуры thrdsinfo64 содержится в файле procinfo.h.

Функции работы со списками

Библиотека отладки нитей управляет списками нитей, атрибутов нитей, взаимных блокировок, атрибутов взаимных блокировок, переменных условия, атрибутов переменных условия, блокировок чтения/записи, атрибутов блокировок чтения/записи, ключей отдельных нитей и активных ключей, представленных в виде описателей соответствующих типов. Функции pthdb_<объект>() Функции pthdb_<object>() возвращают следующий описатель в списке соответствующего типа. Поле <объект> может принимать следующие значения: pthread, attr, mutex, mutexattr, cond, condattr, rwlock, rwlockattr и key. Если список пуст или достигнут конец списка, возвращается значение PTHDB_INVALID_объект, где объект - это одно из следующих значений: PTHREAD, ATTR, MUTEX, MUTEXATTR, COND, CONDATTR, RWLOCK, RWLOCKATTR или KEY.

Функции работы с полями

Дополнительную информацию об объекте можно получить с помощью соответствующего метода объекта с именем pthdb_объект_поле(), где объект может принимать одно из следующих значений: pthread, attr, mutex, mutexattr, cond, condattr, rwlock, rwlockattr или key, а поле - имя поля данных объекта.

Настройка сеанса

Функция pthdb_session_setflags() позволяет отладчику изменить флаги для настройки сеанса. Эти флаги предназначены для изменения числа регистров, которые считываются или записываются при выполнении операций с контекстом, а также для управления печатью отладочной информации.

Функция pthdb_session_flags() возвращает текущие значения флагов сеанса.

Завершение сеанса

В конце сеанса отладки необходимо освободить память, занятую структурами данных сеанса, и удалить сами данные. Эти действия выполняет функция pthdb_session_destroy(), причем для освобождения памяти она использует функции callback. Освобождается вся память, выделенная функциями pthdb_session_init() и pthdb_session_update().

Пример

Приведенный ниже псевдокод иллюстрирует работу отладчика и содержит фрагменты кода для блокировки и освобождения:

/* директивы include */
 
#include <sys/pthdebug.h>
 
main()
{
    tid_t stop_tid; /* нить, вызывающая остановку процесса */
    pthdb_user_t user = <уникальное значение отладчика>;
    pthdb_session_t session; /* <уникальное библиотечное значение> */
    pthdb_callbacks_t callbacks = <функции callback>;
    char *pthreaded_symbol=NULL;
    char *committed_symbol;
    int pthreaded = 0;
    int pthdb_init = 0;
    char *committed_symbol;
 
    /* fork/exec или подключение к отлаживаемой программе */
 
    /* в отлаживаемой программе вызывается функция ptrace()/ptracex() с PT_TRACE_ME */
 
    while (/* ожидание события */) 
    {
      /* отладчик ожидает события в отлаживаемой программе */
 
      if (pthreaded_symbol==NULL) {
        rc = pthdb_session_pthreaded(user, &callbacks, pthreaded_symbol);
        if (rc == PTHDB_NOT_PTHREADED)
        {
            /* установка точки прерывания для pthreaded_symbol */
        }
        else
          pthreaded=1;	
      }
      if (pthreaded == 1 && pthdb_init == 0) {
          rc = pthdb_session_init(user, &session, PEM_32BIT, flags, &callbacks);
          if (rc)
              /* обработка ошибки и выход */
          pthdb_init=1;
      }
  
      rc = pthdb_session_update(session)
      if ( rc != PTHDB_SUCCESS)
/* обработка ошибки и выход */
 
      while (/* считывание команд отладчика */)
      {
          switch (/* команда отладчика */)
          {
              ...
              case DB_HOLD:
                  /* независимо от того, связана нить с нитью ядра или нет */
                  rc = pthdb_pthread_hold(session, pthread);
                  if (rc)
                      /* обработка ошибки и выход */
              case DB_UNHOLD:
                  /* независимо от того, связана нить с нитью ядра или нет */
                  rc = pthdb_pthread_unhold(session, pthread);
                  if (rc)
                      /* обработка ошибки и выход */
              case DB_CONTINUE:
                  /* если нам не нужно блокировать нить до конца */
                  /* процесса */
                  if (pthreaded)
                  {
                      /* отладчик должен обработать список любой длины */
                      struct pthread commit_tids;
                      int commit_count = 0;
                      /* отладчик должен обработать список любой длины */
                      struct pthread continue_tids;
                      int continue_count = 0;
		      
rc = pthdb_session_committed(session, committed_symbol);
		      if (rc != PTHDB_SUCCESS)
	  /* обработка ошибки */
	              /* установка точки прерывания для committed_symbol */	
		      
                      /* получение всех ИД нитей, необходимых для фиксации */
                      /* всех операций блокировки/освобождения */
                      do
                      {
                          rc = pthdb_session_commit_tid(session, 
                                                &commit_tids.th[commit_count++]);
                          if (rc != PTHDB_SUCCESS)
                              /* обработка ошибки и выход */
                      } while (commit_tids.th[commit_count - 1] != PTHDB_INVALID_TID);
  
                      /* остановка обработки нити, вызвавшей останов */
                      /* процесса, с помощью функции stop_park */
  
if (commit_count > 0) {
                        rc = ptrace(PTT_CONTINUE, stop_tid, stop_park, 0, 
                                                              &commit_tids);
                        if (rc)
                            /* обработка ошибки и выход */
  
                        /* ожидание остановки процесса */
		      }
  
                      /* получение всех ИД нитей, необходимых для продолжения */
                      /* обработки нужных нитей */
                      do
                      {
                          rc = pthdb_session_continue_tid(session, 
                                          &continue_tids.th[continue_count++]);
                           if (rc != PTHDB_SUCCESS)
                              /* обработка ошибки и выход */
                      } while (continue_tids.th[continue_count - 1] != PTHDB_INVALID_TID);
  
                      /* добавление нужных нитей к списку continue_tids */
  
                      /* остановка нити, вызвавшей остановку */
                      /* процесса, если она не нужна */
  
                      rc = ptrace(PTT_CONTINUE, stop_tid, stop_park, 0, 
                                                                &continue_tids);
                      if (rc)
                          /* обработка ошибки и выход */
                  }
              case DB_EXIT:
rc = pthdb_session_destroy(session);
/* завершение очистки */
exit(0);
              ...
          }
      }
  
    }
    exit(0);
}

Связанная информация

Файл pthread.h.


[ Страница назад | Страница вперед | Содержание | Индекс | Библиотека | Юридическая информация | Поиск ]