Библиотека отладки нитей (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 для работы с несколькими нитями.
При каждой остановке отладчика после инициализации сеанса необходимо вызывать функцию 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. Если список нитей ядра пуст, для фиксации не нужно возобновлять работу каких-либо нитей.
У отладчика есть два способа узнать о том, что все изменения состояния блокировки зафиксированы:
Для блокировки или освобождения нитей перед отладкой группы нитей или пошаговой отладкой одной нити нужно выполнить следующие действия:
С помощью функции 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.