учебники, программирование, основы, введение в,

 

Трассировка пользовательских приложений

Основные идеи, понятия и объекты
Под трассировкой в стандарте POSIX-2001 понимается порождение, накопление и анализ данных о событиях, имевших место при выполнении пользовательского приложения.
Применительно к приложениям реального времени трассировка помогает достичь по крайней мере трех целей:

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

С логической точки зрения в трассировке (в том виде, как она рассматривается в стандарте POSIX-2001) участвуют три процесса (физически они могут совпадать между собой):

  • трассируемый (целевой);
  • трассирующий (управляющий трассировкой);
  • анализирующий данные трассировки.

Сведения о действиях, производимых при выполнении приложения, фиксируются в виде объектов данных, называемых событиями трассировки (или, для краткости, просто событиями). События записываются в потоки трассировки, которые содержат также служебные данные, необходимые для интерпретации событий.
Трассируемый процесс должен быть специальным образом оборудован: его программа должна содержать точки трассировки – действия, способные генерировать события трассировки. Для каждого трассируемого процесса должен быть открыт по крайней мере один поток трассировки.
Процесс, создавший поток трассировки, называется трассирующим (управляющим трассировкой).
Анализирующим называется процесс, извлекающий события трассировки из потока с целью получения информации о поведении трассируемого приложения.
Одним из главных требований к системе трассировки является минимизация накладных расходов. Чтобы выполнить это требование, потоки трассировки, как правило, хранят только в оперативной памяти. Если нужно получить стабильную копию потока для последующего анализа, его следует сбросить в журнал трассировки, располагающийся в долговременной памяти.
События трассировки подразделяются на две категории:

  • пользовательские;
  • системные.

Пользовательские события генерируются при вызове функции posix_trace_event().
Системные события генерируются реализацией в ответ на действия ОС или приложения. Отметим, что в первом случае событие не ассоциировано с каким-либо процессом.
У событий трассировки есть типы и имена, между которыми устанавливается взаимно-однозначное соответствие (вообще говоря, свое для каждого потока). Удобочитаемые имена предназначены для человека; в потоке трассировки хранятся идентификаторы типов событий (и таблица соответствия имен и идентификаторов типов).
В стандарте POSIX-2001 специфицированы следующие типы системных событий, анализ которых помогает интерпретировать поток трассировки.
POSIX_TRACE_START
Начало трассировки.
POSIX_TRACE_STOP
Приостановка трассировки.
POSIX_TRACE_OVERFLOW
Возникновение ситуации переполнения потока трассировки.
POSIX_TRACE_RESUME
Устранение ситуации переполнения потока трассировки.
POSIX_TRACE_FLUSH_START
Начало сброса потока трассировки в журнал.
POSIX_TRACE_FLUSH_STOP
Завершение сброса потока трассировки в журнал.
POSIX_TRACE_ERROR
Ошибка трассировки, зависящая от реализации.
Стандартизован также один тип пользовательских событий – POSIX_TRACE_UNNAMED_USEREVENT. Это значение возвращается, когда приложение пытается зарегистрировать больше имен событий, чем допускается в соответствии со значением конфигурационной константы TRACE_USER_EVENT_MAX.
Стандартом POSIX-2001 предусмотрен механизм фильтрации, позволяющий отключить генерацию событий определенных типов и, тем самым, уменьшать объем трассировочных данных. Это полезно, например, в ситуациях, когда у приложения есть заведомо «неинтересные» фрагменты, трассировать которые нет смысла, или многочисленные системные события (такие, например, как переключение контекстов), «неинтересные» с точки зрения приложения.
Фильтр устанавливает трассирующий процесс. По умолчанию события не фильтруются.
При смене фильтра генерируется системное событие POSIX_TRACE_FILTER.
Ключевыми элементами реализации средств трассировки являются описанные в заголовочном файле <trace.h> структуры posix_trace_event_info и posix_trace_status_info. Первая специфицирует формат событий, вторая – данные, хранящиеся в потоке трассировки.
Согласно стандарту POSIX-2001, структура типа posix_trace_event_info должна содержать по крайней мере следующие поля.
trace_event_id_t     posix_event_id;    
/* Идентификатор типа события     */
pid_t     posix_pid;    
/* Идентификатор трассируемого процесса */
void     *posix_prog_address;    
/* Адрес точки трассировки */
int     posix_truncation_status;
/* Признак усечения данных */
struct timespec     posix_timestamp;    
/* Временной штамп события */
pthread_t     posix_thread_id;    
/* Идентификатор трассируемого потока управления */
Смысл некоторых полей, вероятно, нуждается в пояснении.
Если, в соответствии с типом (поле posix_event_id) событие является системным и не ассоциировано с каким-либо процессом, значение posix_pid должно быть нулевым.
Для пользовательских событий в поле posix_prog_address помещается адрес вызова posix_trace_event(); для системных, сгенерированных в результате явного обращения приложения к системному сервису (такому, например, как fork()), в этом поле находится адрес вызова системного сервиса.
В поле posix_truncation_status хранится признак того, были ли данные усечены при генерации или чтении события. Возможных значения три:
POSIX_TRACE_NOT_TRUNCATED
Доступны все данные, ничего не усечено.
POSIX_TRACE_TRUNCATED_RECORD
Данные усечены при генерации.
POSIX_TRACE_TRUNCATED_READ
Данные усечены при чтении (буфер оказался мал). Это значение подавляет признак усечения при генерации.
В поле posix_timestamp помещается время генерации события. Выбор используемых для этого часов осуществляется реализацией.
Поле posix_thread_id по смыслу и трактовке близко к posix_pid, только в нем хранится идентификатор потока, а не процесса (если, конечно, реализация поддерживает потоки).

Помимо величин, фигурирующих в структуре типа posix_trace_event_info, с событиями (и пользовательскими, и системными) могут быть ассоциированы дополнительные данные, помогающие в их интерпретации событий.
Структура типа posix_trace_status_info содержит информацию о состоянии активного потока трассировки. Согласно стандарту POSIX-2001, она должна содержать по крайней мере следующие поля.
int posix_stream_status;    
/* Состояние потока трассировки */
int posix_stream_full_status;    
/* Признак заполненности потока    */
int posix_stream_overrun_status;   
/* Признак потери информации    */
int posix_stream_flush_status;    
/* Идет ли в данный момент сброс в журнал */
int posix_stream_flush_error;    
/* Были ли ошибки при последнем сбросе     */
int posix_log_overrun_status;    
/* Признак потери информации в журнале     */
int posix_log_full_status;    
/* Признак заполненности журнала    */
Значение поля posix_stream_status отражает режим, в котором находится поток трассировки:
POSIX_TRACE_RUNNING
Трассировка выполняется, поток принимает события.
POSIX_TRACE_SUSPENDED
Трассировка приостановлена (или не начиналась), поток не принимает события.
Поле posix_stream_full_status отражает состояние заполненности потока. Оно может содержать следующие значения.
POSIX_TRACE_FULL
Пространство в потоке исчерпано.
POSIX_TRACE_NOT_FULL
В потоке есть свободное пространство для событий.
Отметим, что на практике возможны все четыре комбинации значений полей posix_stream_status и posix_stream_full_status. В частности, сочетание POSIX_TRACE_RUNNING и POSIX_TRACE_FULL свидетельствует о том, что трассировка идет, но с потерей информации (новые события записываются на место старых).
Поле posix_stream_overrun_status содержит признак потери информации. Возможные значения:
POSIX_TRACE_OVERRUN
По крайней мере одно событие было потеряно (и, следовательно, не содержится в потоке трассировки).
POSIX_TRACE_NO_OVERRUN
Ни одно событие не потеряно.
События могут теряться, если они генерируются при заполненном потоке или во время сброса потока трассировки в журнал.
Поле posix_stream_flush_status содержит признак того, что в данный момент происходит сброс потока трассировки в журнал. Возможных значений, разумеется, два.
POSIX_TRACE_FLUSHING
В данный момент происходит сброс потока трассировки в журнал.
POSIX_TRACE_NOT_FLUSHING
В данный момент не происходит сброс потока трассировки в журнал.
В поле posix_stream_flush_error находится нуль, если последний сброс прошел нормально; в противном случае там хранится код первой случившейся при сбросе ошибки (информация о последующих ошибках теряется).
Поле posix_log_overrun_status содержит признак потери информации в журнале с двумя возможными значениями: POSIX_TRACE_OVERRUN и POSIX_TRACE_NO_OVERRUN.
Поле posix_log_full_status служит признаком заполненности журнала. Возможных значений, естественно, два: POSIX_TRACE_FULL и POSIX_TRACE_NOT_FULL.
При создании потока трассировки его атрибуты стандартным для POSIX-2001 образом извлекаются из атрибутного объекта. Стандартом предусмотрены следующие атрибуты:

  • имя версии системы трассировки;
  • имя потока трассировки;
  • время создания потока трассировки;
  • разрешающая способность часов, с помощью которых проставляются временные штампы;
  • минимальное число байт, резервируемое для записи событий;
  • правила обработки ситуации заполнения потока трассировки;
  • максимальный размер (в байтах) записываемых в поток событий трассировки;
  • признак наследования трассировки порожденными процессами;
  • максимальный размер (в байтах) журнала трассировки, ассоциированного с потоком;
  • правила обработки ситуации заполнения журнала трассировки.

И для потоков, и для журналов трассировки правила обработки ситуации заполнения могут сводиться к записи новых событий поверх самых старых (политика POSIX_TRACE_LOOP) или к приостановке трассировки (POSIX_TRACE_UNTIL_FULL). Кроме того, для потоков может быть предусмотрен сброс в журнал с последующей очисткой (политика POSIX_TRACE_FLUSH), а для для журналов – потенциально неограниченное расширение (POSIX_TRACE_APPEND).
Трассировка с записью в создаваемый поток может как наследоваться, так и не наследоваться порожденными процессами. Соответствующие значения признака наследования именуются POSIX_TRACE_INHERITED и POSIX_TRACE_CLOSE_FOR_CHILD.
В заключение раздела еще раз подчеркнем важные достоинства, которыми обладает механизм трассировки.

  • В большинстве случаев трассировка приложения допустимым образом влияет на временные характеристики его выполнения, поэтому трассировку можно применять в штатном режиме функционирования.
  • Трассировка позволяет отразить поведение программы, состоящей из произвольного числа сущностей.
  • Трассировка позволяет унифицировать механизм сообщений о поведении приложения. Она является готовым решением по организации такого механизма.

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

Функции для работы с атрибутными объектами потоков трассировки
Согласно стандарту POSIX-2001, система трассировки строится в объектно-ориентированном стиле. Структура большинства используемых объектов скрыта от приложения, поэтому требуется довольно много относительно мелких функций – конструкторов, селекторов, итераторов.
Для систематического рассмотрения целесообразно представить множество функций трассировки в виде трехуровневой иерархии. Разветвление на верхнем уровне производится в соответствии с ролью процесса (трассирующий, трассируемый, анализирующий), а на втором уровне – в соответствии с классом обслуживаемых объектов (потоки и их атрибутные объекты, журналы, события и т.д.). На третьем уровне располагаются функциональные группы, предназначенные для выполнения определенных операций (создание/уничтожение, опрос/изменение и т.п.) над объектами определенных классов.
Чтобы получить общее представление о функциях трассировки, рассмотрим сначала два верхних уровня описанной иерархии.
Трассирующий процесс управляет ресурсами, ассоциированными с потоками трассировки, а именно:

  • атрибутными объектами потоков трассировки;
  • собственно потоками трассировки;
  • журналами трассировки;
  • идентификаторами типов событий;
  • фильтрами.

Трассируемый процесс имеет дело со следующими объектами:

  • события;
  • идентификаторы типов событий;
  • точки трассировки.

Анализирующий процесс читает:

  • потоки трассировки;
  • журналы;
  • события;
  • идентификаторы типов событий.

Перейдем к детальному рассмотрению функций трассировки.
Для создания и уничтожения атрибутных объектов потоков трассировки служат функции posix_trace_attr_init() и posix_trace_attr_destroy()
int posix_trace_attr_init (
trace_attr_t *attr);
int posix_trace_attr_destroy (
trace_attr_t *attr);

Листинг 8.1. Описание функций создания и уничтожения атрибутных объектов потоков трассировки. Функция posix_trace_attr_init() инициализирует атрибутный объект подразумеваемыми значениями для всех атрибутов, используемых реализацией. Отметим, что два атрибута – имя версии системы трассировки и разрешающая способность часов, с помощью которых проставляются временные штампы, – доступны только на чтение.
Нормальным является нулевой результат; при наличии ошибки возвращается ее номер.
Для манипулирования атрибутами, идентифицирующими поток трассировки, служат функции posix_trace_attr_getgenversion(), posix_trace_attr_getname(), posix_trace_attr_setname(), posix_trace_attr_getcreatetime() (см. #include <trace.h>
int posix_trace_attr_getgenversion (
const trace_attr_t *attr,
char *genversion);
int posix_trace_attr_getname (
const trace_attr_t *attr,
char *tracename);
int posix_trace_attr_setname (
trace_attr_t *attr, const
char *tracename);
#include <time.h>
#include <trace.h>
int posix_trace_attr_getcreatetime (
const trace_attr_t *attr,
struct timespec *createtime);

Листинг 8.2. Описание функций манипулирования атрибутами, идентифицирующими поток трассировки.
Функции опроса копируют значения соответствующих атрибутов (имя версии системы трассировки, имя и время создания потока трассировки) из атрибутного объекта, заданного аргументом attr, в символьный массив (длина которого должна быть не меньше TRACE_NAME_MAX) или в структуру. Функция posix_trace_attr_setname() копирует имя (длина которого не должна превышать TRACE_NAME_MAX) в противоположном направлении.
Функция posix_trace_attr_getclockres()) позволяет опросить разрешающую способность часов, с помощью которых проставляются временные штампы.
#include <time.h>
#include <trace.h>
int posix_trace_attr_getclockres (
const trace_attr_t *attr,
struct timespec *resolution);

Листинг 8.3. Описание функции опроса разрешающей способности часов, с помощью которых проставляются временные штампы.

Поведенческие атрибуты потоков и журналов обслуживаются функциями: posix_trace_attr_getinherited(), posix_trace_attr_setinherited(), posix_trace_attr_getstreamfullpolicy(), posix_trace_attr_setstreamfullpolicy(), posix_trace_attr_getlogfullpolicy(), posix_trace_attr_setlogfullpolicy()).
#include <trace.h>
int posix_trace_attr_getinherited (
const trace_attr_t *restrict attr,
int *restrict inheritancepolicy);
int posix_trace_attr_setinherited (
trace_attr_t *attr,
int inheritancepolicy);
int posix_trace_attr_getstreamfullpolicy (
const trace_attr_t *restrict attr,
int *restrict streampolicy);
int posix_trace_attr_setstreamfullpolicy (
trace_attr_t *attr, int streampolicy);
int posix_trace_attr_getlogfullpolicy (
const trace_attr_t *restrict attr,
int *restrict logpolicy);
int posix_trace_attr_setlogfullpolicy (
trace_attr_t *attr, int logpolicy);

Листинг 8.4. Описание функций манипулирования значениями поведенческих атрибутов потоков и журналов в атрибутных объектах.
Функции posix_trace_attr_getinherited() и posix_trace_attr_setinherited() позволяют, соответственно, опросить и установить значение атрибута наследования трассировки порожденными процессами. Если значение этого атрибута равно POSIX_TRACE_INHERITED, то после вызовов fork() и/или spawn() родительский и порожденный процессы будут трассироваться параллельно с использованием общего потока трассировки. Подразумеваемое значение атрибута суть POSIX_TRACE_CLOSE_FOR_CHILD (не трассировать порожденные процессы).
Атрибут, специфицирующий правила обработки ситуации заполнения, и для потоков (функции posix_trace_attr_getstreamfullpolicy() и posix_trace_attr_setstreamfullpolicy()), и для журналов трассировки (функции posix_trace_attr_getlogfullpolicy() и posix_trace_attr_setlogfullpolicy()) может принимать следующие значения.
POSIX_TRACE_LOOP
Для потоков – продолжение трассировки с записью новых событий поверх самых старых; для журналов – сброс всех событий из потока также с записью поверх самых старых.
POSIX_TRACE_UNTIL_FULL
Для потоков – приостановка трассировки с генерацией события POSIX_TRACE_STOP. Реализация обязана возобновить трассировку (с генерацией события POSIX_TRACE_START) после очистки потока, но может сделать это и раньше, за счет повторного использования пространства, которое занимали прочитанные события. Для журналов – прекращение сброса (последним в журнал помещается событие POSIX_TRACE_STOP). События, которые не поместились в журнал, пропадают.
Для потоков трассировки еще одним возможным значением служит
POSIX_TRACE_FLUSH
Регулярно сбрасывать поток в журнал трассировки; в остальном действовать по правилам POSIX_TRACE_UNTIL_FULL.
Если с потоком ассоциирован журнал, то подразумеваемым значением данного атрибута является POSIX_TRACE_FLUSH; в противном случае – POSIX_TRACE_LOOP.
Для журналов трассировки описываемый атрибут может принимать значение
POSIX_TRACE_APPEND
Потенциально неограниченное расширение журнала.
Подразумеваемым для журналов является значение POSIX_TRACE_LOOP.
Опросить и/или установить хранящиеся в атрибутном объекте размеры событий, потоков и журналов можно с помощью функций: posix_trace_attr_getmaxdatasize(), posix_trace_attr_setmaxdatasize(), posix_trace_attr_getmaxsystemeventsize(), posix_trace_attr_getmaxusereventsize(), posix_trace_attr_getstreamsize(), posix_trace_attr_setstreamsize(), posix_trace_attr_getlogsize(), posix_trace_attr_setlogsize()).
#include <sys/types.h>
#include <trace.h>
int posix_trace_attr_getmaxdatasize (
const trace_attr_t *restrict attr,
size_t *restrict maxdatasize);
int posix_trace_attr_setmaxdatasize (
trace_attr_t *attr, size_t maxdatasize);
int posix_trace_attr_getmaxsystemeventsize (
const trace_attr_t *restrict attr,
size_t *restrict eventsize);
int posix_trace_attr_getmaxusereventsize (
const trace_attr_t *restrict attr,
size_t data_len, size_t *restrict eventsize);
int posix_trace_attr_getstreamsize (
const trace_attr_t *restrict attr,
size_t *restrict streamsize);
int posix_trace_attr_setstreamsize (
trace_attr_t *attr, size_t streamsize);
int posix_trace_attr_getlogsize (
const trace_attr_t *restrict attr,
size_t *restrict logsize);
int posix_trace_attr_setlogsize (
trace_attr_t *attr, size_t logsize);

Листинг 8.5. Описание функций манипулирования размерами событий, потоков и журналов в атрибутных объектах.
Функции posix_trace_attr_getmaxdatasize() и posix_trace_attr_setmaxdatasize() позволяют опросить и установить максимальный размер (в байтах) данных, ассоциируемых с пользовательскими событиями. Подразумеваемое значение этого атрибута зависит от реализации.
Функции posix_trace_attr_getmaxsystemeventsize() и posix_trace_attr_getmaxusereventsize() служат для опроса максимального размера системных и пользовательских событий, соответственно (во втором случае учитывается размер ассоциированных данных, определяемый значением аргумента data_len).
Функции posix_trace_attr_getstreamsize() и posix_trace_attr_setstreamsize() ведают минимальным объемом памяти, который система трассировки обязана зарезервировать в потоке для хранения событий. Накладные расходы на размещение атрибутов потока, таких как идентифицирующие данные или правила поведения, в эту величину не входят. Пока сумма максимальных размеров событий не превосходит значения данного атрибута, события должны записываться без потери информации.
Функции posix_trace_attr_getlogsize() и posix_trace_attr_setlogsize() обслуживают атрибут журнала трассировки – максимальный размер, резервируемый для хранения событий. Если правило обработки ситуации заполнения журнала определено как POSIX_TRACE_APPEND, данный атрибут игнорируется.

Функции, обслуживающие жизненный цикл потоков трассировки.
На показаны основные элементы жизненного цикла потока трассировки и функции, вызов которых трассирующим процессом обеспечивает переход между состояниями.
Рис. 8.1.  Основные элементы жизненного цикла потока трассировки.
Создание потока трассировки осуществляется функциями posix_trace_create() или posix_trace_create_withlog() (см..
#include <sys/types.h>
#include <trace.h>
int posix_trace_create (pid_t pid,
const trace_attr_t *restrict attr,
trace_id_t *restrict trid);
int posix_trace_create_withlog (pid_t pid,
const trace_attr_t *restrict attr, int fildes,
trace_id_t *restrict trid);

Листинг 8.6. Описание функций создания потоков трассировки. 
Функция posix_trace_create() создает активный поток, в который будут записываться события трассировки целевого процесса с идентификатором pid (при нулевом pid трассируется вызывающий процесс). Ресурсы в потоке отводятся в соответствии со значениями элементов атрибутного объекта, заданного аргументом attr (если attr равен NULL, используются подразумеваемые значения).
Идентификатор созданного потока записывается по указателю trid. Его может использовать только вызывающий процесс.
Фильтр, ассоциированный с новым потоком, пуст.
Одновременно могут быть активными несколько потоков трассировки, однако их общее число не должно превышать значения конфигурационной константы TRACE_SYS_MAX.
Разумеется, попытки трассировать другие процессы подвержены контролю прав доступа.
Функция posix_trace_create_withlog() делает то же, что и posix_trace_create(), но дополнительно ассоциирует с потоком журнал трассировки, заданный файловым дескриптором fildes.
Сразу после создания потока трассировка считается приостановленной. Чтобы активизировать ее, следует воспользоваться функцией posix_trace_start(); для последующей приостановки нужно вызвать функцию posix_trace_stop() (#include <trace.h>
int posix_trace_start (trace_id_t trid);
int posix_trace_stop (trace_id_t trid);

Листинг 8.7. Описание функций активизации и приостановки трассировки.
После вызова posix_trace_start() в поток помещается системное событие POSIX_TRACE_START, а статус потока получает значение POSIX_TRACE_RUNNING (если трассировка уже шла, вызов posix_trace_start() игнорируется).
После вызова posix_trace_stop() в поток помещается системное событие POSIX_TRACE_STOP, а статус потока получает значение POSIX_TRACE_SUSPENDED (если трассировка уже была приостановлена, вызов posix_trace_stop() игнорируется).
Если поток трассировки заполнен, вызовы posix_trace_start() и posix_trace_stop() игнорируются.
Функция posix_trace_shutdown()) завершает трассировку и освобождает ресурсы, ассоциированные с потоком, независимо от того, прочитал ли анализирующий процесс все записанные события.
#include <sys/types.h>
#include <trace.h>
int posix_trace_shutdown (trace_id_t trid);

Листинг 8.8. Описание функции завершения трассировки.)
Если с потоком ассоциирован журнал, в него сбрасываются все несохраненные события, записывается необходимая служебная информация (атрибуты трассировки, типы и имена событий и соответствие между ними и т.п.), после чего журнал закрывается.
После возврата из posix_trace_shutdown() аргумент trid перестает быть корректным идентификатором потока трассировки.
Трассировка может завершаться и неявным образом, когда трассирующий процесс терминируется или вызывает функцию семейства exec().
По ходу трассировки управляющий процесс может инициировать сброс потока в журнал, обратившись к функции posix_trace_flush()).
#include <sys/types.h>
#include <trace.h>
int posix_trace_flush (trace_id_t trid);

Листинг 8.9. Описание функции сброса потока трассировки в журнал.
Сброс осуществляется с учетом правил обработки ситуации заполнения журнала.
После завершения сброса пространство в потоке трассировки, которое занимали записанные в журнал события, может быть использовано повторно.
Узнать о завершении операции сброса можно, опрашивая статус потока трассировки с помощью функции posix_trace_get_status() (см. #include <trace.h>
int posix_trace_get_status (trace_id_t trid,
struct posix_trace_status_info
*statusinfo);

Листинг 8.10. Описание функции опроса статуса потока трассировки.
Функция обеспечивает согласованность данных, которые записываются в структуру типа posix_trace_status_info по указателю statusinfo. Сразу после завершения вызова признаки переполнения и для потока, и для журнала получают значение POSIX_TRACE_NO_OVERRUN, а в поле posix_stream_flush_error помещается нуль.

Стандартом POSIX-2001 предусмотрен опрос атрибутов потока трассировки. Для этого служит функция posix_trace_get_attr() (см. #include <trace.h>
int posix_trace_get_attr (
trace_id_t trid, trace_attr_t *attr);

Листинг 8.11. Описание функции опроса атрибутов потока трассировки.
Для возврата значений атрибутов найдено красивое решение – они помещаются в атрибутный объект по указателю attr, откуда могут быть извлечены рассмотренными выше функциями для работы с атрибутными объектами.
При необходимости поток и журнал трассировки можно очистить от хранящихся там событий (но не от служебной информации), воспользовавшись функцией posix_trace_clear() (см#include <sys/types.h>
#include <trace.h>
int posix_trace_clear (trace_id_t trid);

Листинг 8.12. Описание функции очистки потока трассировки. Разумеется, после очистки признак заполненности получает значение POSIX_TRACE_NOT_FULL.
Если для журнала предусмотрено неограниченное расширение (POSIX_TRACE_APPEND), эффект от вызова posix_trace_clear() не специфицирован.
При опросе и установке фильтров событий трассировки (функции posix_trace_get_filter() и posix_trace_set_filter(), см. применяется апробированная в других частях стандарта POSIX-2001 схема работы с множествами, основанная на функциях: posix_trace_eventset_empty(), posix_trace_eventset_fill(), posix_trace_eventset_add(), posix_trace_eventset_del(), posix_trace_eventset_ismember() (см..
#include <trace.h>
int posix_trace_get_filter (
trace_id_t trid, trace_event_set_t *set);
int posix_trace_set_filter (trace_id_t trid,
const trace_event_set_t *set, int how);

Листинг 8.13. Описание функций опроса и установки фильтров событий трассировки. (
#include <trace.h>
int posix_trace_eventset_empty (
trace_event_set_t *set);
int posix_trace_eventset_fill (
trace_event_set_t *set, int what);
int posix_trace_eventset_add (
trace_event_id_t event_id,
trace_event_set_t *set);
int posix_trace_eventset_del (
trace_event_id_t event_id,
trace_event_set_t *set);
int posix_trace_eventset_ismember (
trace_event_id_t event_id,
const trace_event_set_t *restrict set,
int *restrict ismember);

Листинг 8.14. Описание функций для работы с множествами событий трассировки. (
Когда вызывается функция posix_trace_set_filter(), в поток трассировки помещается событие POSIX_TRACE_FILTER с указанием старого и нового фильтров. Аргумент how, определяющий, как меняется фильтр, может принимать следующие значения.
POSIX_TRACE_SET_EVENTSET
Новое значение фильтра задается аргументом set.
POSIX_TRACE_ADD_EVENTSET
Новое значение фильтра вычисляется как объединение текущего значения и множества, заданного аргументом set.
POSIX_TRACE_SUB_EVENTSET
Новое значение фильтра вычисляется как разность текущего значения и множества, заданного аргументом set.
Вероятно, функции для работы с множествами событий трассировки не нуждаются в пространном описании. Аргумент event_id задает тип события. Функция posix_trace_eventset_empty() создает пустое множество событий. Функция posix_trace_eventset_fill() помещает в создаваемое множество события, определяемые аргументом what, который может принимать следующие значения.
POSIX_TRACE_WOPID_EVENTS
Все зависящие от реализации системные события, не ассоциированные с каким-либо процессом.
POSIX_TRACE_SYSTEM_EVENTS
Все зависящие от реализации системные события.
POSIX_TRACE_ALL_EVENTS
Все события (системные и пользовательские).
Для функций posix_trace_eventset_add() и posix_trace_eventset_del() добавление уже присутствующего или, соответственно, удаление отсутствующего типа не считается ошибкой.
Функция posix_trace_eventset_ismember() помещает по указателю ismember ненулевое значение, если тип event_id принадлежит множеству, заданному аргументом set.
Как и (почти) везде в системе трассировки, нормальным для описанных функций является нулевой результат.
Играющие техническую роль функции posix_trace_trid_eventid_open(), posix_trace_eventid_get_name() и posix_trace_eventid_equal()обслуживают идентификаторы типов событий, рассматриваемые как абстрактные объекты.
#include <trace.h>
int posix_trace_trid_eventid_open (
trace_id_t trid,
const char *restrict event_name,
trace_event_id_t *restrict event);

int posix_trace_eventid_get_name (
trace_id_t trid,
trace_event_id_t event,
char *event_name);

int posix_trace_eventid_equal (
trace_id_t trid,
trace_event_id_t event1,
trace_event_id_t event2);

Листинг 8.15. Описание функций для работы с идентификаторами типов событий трассировки. (
Функция posix_trace_trid_eventid_open() ассоциирует с именем event_name, длина которого не должна превышать значения конфигурационной константы TRACE_EVENT_NAME_MAX, уникальный для потока трассировки, заданного аргументом trid, идентификатор типа событий и записывает его по указателю event. При повторном вызове posix_trace_trid_eventid_open() с тем же именем возвращается ранее созданный идентификатор. При попытке превысить максимально допустимое для процесса количество типов пользовательских событий TRACE_USER_EVENT_MAX возвращается предопределенный идентификатор POSIX_TRACE_UNNAMED_USEREVENT.
Функция posix_trace_eventid_get_name() записывает в массив event_name имя события, ассоциированное с типом, заданным аргументом event.
Сравнить два идентификатора типов из одного потока трассировки можно посредством функции posix_trace_eventid_equal(), возвращающей в случае равенства результат, отличный от нуля.
Функции posix_trace_eventtypelist_getnext_id() и posix_trace_eventtypelist_rewind() (см. позволяют обрабатывать совокупность идентификаторов типов событий, присутствующих в заданном потоке трассировки.
#include <trace.h>
int posix_trace_eventtypelist_getnext_id (
trace_id_t trid,
trace_event_id_t *restrict event,
int *restrict unavailable);
int posix_trace_eventtypelist_rewind (trace_id_t trid);

Листинг 8.16. Описание функций для работы с совокупностью идентификаторов типов событий. (
Функция posix_trace_eventtypelist_getnext_id() является итератором. При первом обращении она записывает по указателю event первый идентификатор типа. При каждом следующем вызове туда же помещается очередной идентификатор. Пока перебор идентификаторов не закончен, по указателю unavailable записывается нулевое значение.
Функция posix_trace_eventtypelist_rewind() позволяет вернуться к началу перебора.
Приведем пример программы, выясняющей подразумеваемые значения атрибутов потока трассировки (
Листинг 8.17. Пример программы, выясняющей подразумеваемые значения атрибутов потока трассировки. (
Обратим внимание на использование конфигурационной константы TRACE_NAME_MAX для задания размера буфера имен.
При запуске этой программы под управлением операционной системы реального времени oc2000 могут быть получены следующие результаты (см.
Листинг 8.18. Возможные результаты работы программы, выясняющей подразумеваемые значения атрибутов потока трассировки. (
Отметим нестандартность конфигурации системы применительно к обработке ситуации заполнения потока: POSIX-2001 предусматривает, что подразумеваемыми (в зависимости от наличия журнала) могут быть лишь значения POSIX_TRACE_LOOP или POSIX_TRACE_FLUSH, но не POSIX_TRACE_UNTIL_FULL.
Читателю предлагается самостоятельно вычислить, сколько событий максимального размера можно записать в поток.

Функции для работы с событиями
От рассмотрения средств, доступных трассирующему процессу, мы переходим к функциям, используемым для оборудования трассируемых пользовательских приложений. Таких функций две: posix_trace_event() и posix_trace_eventid_open() (см.
#include <sys/types.h>
#include <trace.h>

void posix_trace_event (
trace_event_id_t event_id,
const void *restrict data_ptr,
size_t data_len);

int posix_trace_eventid_open (
const char *restrict event_name,
trace_event_id_t *restrict event_id);

Листинг 8.19. Описание функций для оборудования трассируемых пользовательских приложений. (
Вызов функции posix_trace_event() создает точку трассировки, при попадании в которую генерируется пользовательское событие с идентификатором типа event_id и ассоциированными данными, специфицированными аргументами data_ptr (адрес буфера) и data_len (размер буфера). Если трассировка идет и события заданного типа не задерживаются фильтром, соответствующая информация будет записана в потоки, созданные ранее для трассировки вызывающего процесса. В противном случае вызов posix_trace_event() игнорируется.
Функция posix_trace_eventid_open(), ассоциирующая имя события с идентификатором типа, по сути аналогична posix_trace_trid_eventid_open(), только поток задается неявно (как поток, куда идет трассировка вызывающего процесса). Очевидно, подобная функция необходима для формирования аргумента event_id перед вызовами posix_trace_event().
Что касается анализирующего процесса, то обычно он в первую очередь открывает ранее записанный журнал трассировки, обращаясь к функции posix_trace_open(), а, обработав, закрывает его с помощью функции posix_trace_close(), быть может, в промежутке позиционируясь на начало журнала посредством функции posix_trace_rewind() (см. #include <trace.h>
int posix_trace_open (
int fildes, trace_id_t *trid);
int posix_trace_close (trace_id_t trid);
int posix_trace_rewind (trace_id_t trid);

Листинг 8.20. Описание функций для работы с журналами трассировки. (
Функция posix_trace_open(), отправляясь от файлового дескриптора журнала трассировки fildes, открытого на чтение, создает поток трассировки и записывает его идентификатор по указателю trid. С помощью этого идентификатора анализирующий процесс может опрашивать атрибуты и статус потока и, главное, читать из него события, вызывая функцию posix_trace_getnext_event() (см.
#include <sys/types.h>
#include *lt;trace.h>

int posix_trace_getnext_event (
trace_id_t trid,
struct posix_trace_event_info
*restrict event,
void *restrict data_ptr,
size_t num_bytes,
size_t *restrict data_len,
int *restrict unavailable);

int posix_trace_timedgetnext_event (
trace_id_t trid,
struct posix_trace_event_info
*restrict event,
void *restrict data_ptr,
size_t num_bytes,
size_t *restrict data_len,
int *restrict unavailable,
const struct timespec *restrict abstime);

int posix_trace_trygetnext_event (
trace_id_t trid,
struct posix_trace_event_info
*restrict event,
void *restrict data_ptr,
size_t num_bytes,
size_t *restrict data_len,
int *restrict unavailable);

Листинг 8.21. Описание функций чтения событий трассировки. (
Функция posix_trace_getnext_event() выдает очередное событие из потока трассировки, который может быть как предварительно записанным в журнале, так и активным (в последнем случае анализирующий процесс должен совпадать с трассирующим). События выдаются в порядке их генерации, то есть от самого старого к самому новому.
Информация о событии помещается в структуру типа posix_trace_event_info, на которую указывает аргумент event. Данные, ассоциированные с событием, записываются в буфер с адресом data_ptr и длиной num_bytes; по указателю data_len размещается число записанных в буфер байт данных. Наконец, в случае успешного чтения по указателю unavailable помещается нулевое значение.
Если аргумент trid идентифицирует активный поток трассировки, вызов posix_trace_getnext_event() блокируется при отсутствии непрочитанных событий.
Функции posix_trace_timedgetnext_event() и posix_trace_trygetnext_event() применимы только к активным потокам трассировки. При отсутствии непрочитанных событий первая блокируется, но с контролем длительности ожидания (до наступления заданного абсолютного момента времени), а вторая немедленно завершится, записав по указателю unavailable ненулевое значение.
В качестве иллюстрации применения описанных выше средств рассмотрим трассировку обеда философов (см.). Мы не будем приводить весь исходный текст, а перечислим лишь изменения в трассируемой части программы и полностью приведем новую функцию main(), в которой сосредоточены управление трассировкой и анализ сгенерированных событий.
Листинг 8.22. Фрагмент исходного текста программы обеда философов с трассировкой. (
Отметим, что в качестве данных, ассоциированных с пользовательскими событиями, выступают целые числа – номера философов (по одному на событие, см. вызовы posix_trace_event()).
Отметим также стиль формирования фильтра системных событий. Сначала в фильтрующее множество включаются все системные события (вызов posix_trace_eventset_fill(), а затем из него удаляются типы событий, представляющих интерес (вызовы posix_trace_eventset_del()).
Если интересоваться системными событиями, то исходный текст управляющей и анализирующей частей неизбежно получается зависящим от целевой платформы. Приведенный вариант программы ориентирован на операционную систему реального времени oc2000. В этой связи обратим внимание на идентификаторы типов системных событий (traceSigGeneration, traceSigDelivery, traceSigCatchFunc) и на структуру ассоциированных с событиями данных (evdat [0], evdat [2], evdat [5] и т.п.), вообще говоря, свою для каждого типа.
Фрагмент возможных результатов трассировки обеда двух философов показан на
Листинг 8.23. Фрагмент возможных результатов трассировки обеда двух философов на операционной платформе oc2000. (
Трассировка – по-настоящему полезный инструмент в ситуациях, когда многопотоковая программа по непонятным причинам ведет себя не так, как хотелось бы. Рассмотрим пример, предложенный автору Н.В. Шмыревым (смПусть имеется критический интервал, вход в который охраняется мьютексом. Один поток управления пытается захватывать этот мьютекс с ожиданием, другой – без ожидания, с паузами между попытками (должен же мьютекс когда-нибудь освободиться?). Выясняется, что второму потоку не удается войти в критический интервал. Почему?
Чтобы получить ответ на этот вопрос, уместно воспользоваться трассировкой системных событий, связанных с мьютексами.
Листинг 8.24. Программа, демонстрирующая трассировку системных событий, связанных с мьютексами, на операционной платформе oc2000. (
Результаты трассировки сводятся к многократному повторению фрагмента, показанного на Второй поток управления не может попасть в критический интервал, потому что мьютекс оказывается захваченным первым потоком. Разумеется, величины задержек в приведенном примере подобраны так, чтобы длительность пауз между попытками входа в критический интервал второго потока управления была кратна периоду выполнения первого потока (что вполне может иметь место и в реальном приложении реального времени). Абстрактная надежда на то, что «все проходит» и мьютекс должен когда-нибудь освободиться, надо только набраться терпения и подождать, может и не сбыться. На подобные проблемы указывал еще Э. Дейкстра в своих первых работах по синхронизации параллельных процессов (см.в дополнительной литературе).
Листинг 8.25. Фрагмент возможных результатов трассировки системных событий, связанных с мьютексами, на операционной платформе oc2000.
Из результатов трассировки видно, что большую часть времени мьютекс свободен, что он освободится всего через две сотых секунды после того, как второй поток управления сделает очередную неудачную попытку захвата, но ...

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика