MSDN Magazine > Home > All Issues > 2007 > April >  Трассировка событий: Улучшение отладки и настро...
Трассировка событий
Улучшение отладки и настройки производительности с помощью ETW
Доктор Инсунг Парк (Insung Park)  and Рики Буч (Ricky Buch)

В статье рассматривается:
  • Понимание архитектуры ETW
  • Что нового в ETW в Windows Vista
  • Разработка при помощи интерфейсов API поставщика событий
Продукты и технологии:
Windows Vista
Увеличивающиеся размеры и сложность современных программных систем превращают разработку и управление программным обеспечением в тяжелую задачу. Предусмотреть все возможные варианты исполнения практически невозможно, и приложения зачастую ведут себя так, как разработчики не ожидали. Более того, огромное число комбинаций оборудования и постоянно изменяющиеся характеристики нагрузки еще больше усложняют диагностику различных проблем с программным обеспечением. Поэтому нет ничего удивительного в том, что надежность и управляемость становятся важными характеристиками. Эти характеристики, в свою очередь, создают потребность в дополнительном инструментарии.
Разумно подобранный инструментарий, добавленный для нескольких ключевых состояний ошибки в исполнении приложения, может значительно уменьшить время, затрачиваемое на отладку проблем. Инструментарий полезен также и в других случаях. В управляемом деловом окружении определенные нежелательные ситуации, такие как сбои программного и аппаратного обеспечения и недостаточность ресурсов, необходимо отслеживать и обрабатывать для большого числа компьютеров. Кроме того, инструментарий может быть очень полезен для решения проблем с производительностью, которые может быть тяжело диагностировать, поскольку они зависят от внешней нагрузки, параметров настройки и состояния лежащего в основе оборудования и программного обеспечения. Трассировка на эксплуатируемом компьютере в момент спада производительности может позволить разработчикам и администраторам выявить непроизводительно работающие компоненты и службы или узкие места, не предусмотренные во время разработки. И наконец, ИТ-специалисты используют различные средства управления для расчета статистики по использованию ресурсов на основе трассировки транзакций для планирования мощностей и анализа тенденций.
Трассировка событий для Windows ® (ETW) является универсальным высокоскоростным средством трассировки событий, предоставляемым операционной системой. Используя механизм буферизации и регистрации, реализованный в ядре, ETW предоставляет возможность универсальной и высокоскоростной трассировки событий, порождаемых как приложениями пользовательского режима, так и драйверами устройств режима ядра. В дополнение, ETW дает возможность динамически разрешать и запрещать регистрацию, облегчая детализированную трассировку в эксплуатационном окружении без необходимости перезагрузки системы или перезапуска приложений. Механизм регистрации использует ассоциированные с каждым процессором буферы, которые записываются на диск потоком асинхронной записи. Это позволяет крупномасштабным серверным приложениям записывать события с минимумом затрат.
Впервые технология ETW была представлена в Windows 2000. С тех пор различные компоненты базовой ОС и серверные компоненты приняли ETW как средство для реализации своих задач, и теперь это одна из ключевых инструментальных технологий платформы Windows. Растущее число приложений сторонних производителей также создаются с использованием ETW, а некоторые пользуются событиями, предоставляемыми самой ОС Windows. ETW была также перенесена в в технологию трассировки программного обеспечения Windows preprocessor (WPP), предоставляющую набор легких в использовании макросов для трассировки сообщений в стиле «printf» для отладки во время разработки.
В Windows Vista™ технология ETW подверглась значительному обновлению, и одним из наиболее значимых изменений является введение единой модели и интерфейсов API поставщика событий. Вкратце, новые унифицированные API объединяют трассировку и запись в Event Viewer в один согласованный механизм, легкий в использовании для поставщиков событий. В то же время, несколько новых возможностей было добавлено для повышения удобства работы разработчиков и конечных пользователей. В этой статье мы представим новую модель поставщика ETW и объясним, как разработчики могут перейти на новую модель в своих приложениях для Windows Vista.
Мы начнем с обзора архитектуры и модели использования ETW, а затем опишем новую модель событий и интерфейсы. Затем последует краткое описание разработки и реализации событий и рассмотрение готовых средств для контроля сеансов ETW, обработки событий журнала и их анализа для создания более высокоуровневого отчета.

Трассировка событий для Windows
Базовая архитектура ETW показана на рис. 1. Как вы видите, в ETW четыре основных типа компонентов: поставщики событий, контроллеры, потребители и сеансы трассировки событий. Буферизация и регистрация происходит в сеансах трассировки событий, которые получают события и создают трассировочный файл. В сеансах ETW доступно несколько режимов регистрации. Например, сеанс может быть настроен таким образом, чтобы доставлять события напрямую приложению-потребителю или перезаписывать старые события в файле, переходя в начало, когда достигнут определенный размер файла. Отдельный поток для записи создается для каждого сеанса для сброса событий в файл или приложению-потребителю реального времени. Для обеспечения высокой производительности, используются ассоциированные с процессорами буферы, чтобы не создавать блокировок при регистрации.
Рис. 1 Архитектура ETW (Щелкните изображение, чтобы увеличить его)
Поставщик событий – это логическая сущность, которая пишет события сеансам ETW. Любая значимая отмечаемая активность может быть событием, и каждая представляется событием, посылаемым ETW. Поставщиком событий может быть приложение пользовательского режима, управляемое приложение, драйвер или любой другое программное обеспечение. Единственным требованием к поставщику событий является необходимость зарегистрировать свой идентификатор (ID) в ETW при помощи API для регистрации. Сперва поставщик регистрируется в ETW, а затем пишет события из различных сегментов кода, обращаясь к интерфейсу регистрации ETW. Когда приложение-контролллер ETW динамически включает поставщика, вызовы API регистрации отправляют события указанному контроллером конкретному сеансу трассировки. Каждое сообщение, посылаемое поставщиком событий сеансу трассировки, состоит из заголовка заданного формата, содержащего метаданные события, и дополнительных произвольных данных пользовательского контекста. Из-за увеличения использования событий во многих компонентах ОС, даже простое приложение для Windows Vista будет содержать несколько компонентов, являющихся поставщиками событий.
Когда событие передается сеансу, ETW дополняет данные, предоставленные пользователем, еще несколькими значениями. Среди них метка времени, идентификаторы процесса и потока, номер процесса и данные по нагрузке на центральный процессор регистрирующего потока. Они заносятся в заголовок события ETW и передаются потребителям событий вместе с переменным содержимым события, заданным поставщиком. Многие трассировочные потребители считают эти данные необходимыми для своего анализа.
Контроллер запускает и останавливает сеансы ETW и подключает к ним поставщиков. В некоторых случаях, таких как отладка и диагностика, контроллер запускается по мере необходимости для тщательной трассировки. Напротив, для событий, ориентированных на администраторов (мы дадим определение дальше), которые всегда должны попадать в Event Viewer, поставщики включаются автоматически, когда они регистрируются, службой журнала событий. Для управления сеансами в Windows Vista контроллеру необходимы разрешения ETW, которые по умолчанию даются только небольшой группе привилегированных пользователей.
Потребитель – это приложение, получающее события в реальном времени, читая файлы журнала или слушая сеанс, и обрабатывающее их. Получение событий основано на обратных вызовах, потребитель регистрирует обратный вызов для события и ETW вызывает его для каждого события. События доставляются потребителю ETW в хронологическом порядке. Есть универсальные средства, использующие потребители сообщений для выгрузки событий в различных форматах. На рис. 2 показана выгрузка в XML события «Process», посланного поставщиком событий ядра, сделанная при помощи средства tracerpt.exe в Windows Vista. Это событие сообщает о запуске процесса Блокнот. Поскольку события содержат пользовательскую часть, задаваемую поставщиком, необходим какой-то вариант метаданных для их правильного декодирования. От поставщиков, использующих новые API, ожидается предоставление манифеста событий – файла XML, определяющего все события поставщика и их компоновку. Универсальное приложение-потребитель пользуется интерфейсом Trace Data Helper (TDH) API для получения метаданных и декодирования и отображения событий.
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
    <System>
        <Provider Guid="{9e814aad-3204-11d2-9a82-006008a86939}" />
        <EventID>0</EventID>
        <Version>2</Version>
        <Level>0</Level>
        <Task>0</Task>
        <Opcode>1</Opcode>
        <Keywords>0x0</Keywords>
        <TimeCreated SystemTime="2006-12-18T12:26:27.887309500Z" />
        <Correlation 
            ActivityID="{00000000-0000-0000-0000-000000000000}" />
        <Execution ProcessID="3396" ThreadID="3260" ProcessorID="0" 
            KernelTime="390" UserTime="195" />
        <Channel />
        <Computer />
    </System>
    <EventData>
        <Data Name="UniqueProcessKey">0xFFFFFA800143FA80</Data>
        <Data Name="ProcessId">0x10EC</Data>
        <Data Name="ParentId">0xD44</Data>
        <Data Name="SessionId">1</Data>
        <Data Name="ExitStatus">0</Data>
        <Data Name="UserSID">guest</Data>
        <Data Name="ImageFileName">notepad.exe</Data>
        <Data Name="CommandLine">notepad</Data>
    </EventData>
    <RenderingInfo Culture="en-US">
        <Opcode>Start</Opcode>
        <Provider>MSNT_SystemTrace</Provider>
        <EventName xmlns=
            "http://schemas.microsoft.com/win/2004/08/events/trace">
            Process</EventName>
        </RenderingInfo>
        <ExtendedTracingInfo xmlns="
            http://schemas.microsoft.com/win/2004/08/events/trace">
        <EventGuid>{3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c}</EventGuid>
        </ExtendedTracingInfo>
</Event>
Для многих трассировка означает сбор событий от интересующих поставщиков. С такой точки зрения, сеанс трассировки связан в единое целое с одним или несколькими поставщиками из абстрактного набора, и зачастую собственно сеанс не воспринимается как регистрирующий обработчик. Архитектура ETW позволяет более динамично и гибко управлять трассировкой и событиями. В ней поставщики и сеансы существуют в различных пространствах. Контроллер запускает и останавливает сеансы ETW и динамически подключает к ним поставщиков. Так, контроллер может подключить группу поставщиков к сеансу, отключить часть из них через какое-то время, а потом подключить другого поставщика к тому же сеансу. Сеансы существуют в ядре и не привязаны к поставщикам постоянно. Аналогично, поставщикам обычно неизвестно, каким сеансам передаются их события. Существуют крупномасштабные приложения и службы, являющиеся поставщиками, контроллерами и потребителями одновременно. Для любых операций поставщиков, контроллеров и потребителей предоставлены интерфейсы API, и приложения могут сочетать любые комбинации ролей. Обычно, однако, разработчики реализуют только поставщиков событий и пользуются готовыми средствами для сбора и просмотра трассировок.
Одним из преимуществ разделения поставщиков и сеансов трассировки в том, что трассировка перестает зависеть от проблем приложения, таких как сбои и зависания. События, переданные поставщиками перед сбоем, останутся в памяти ядра, если не успеют к этому моменту оказаться в файле трассировки, что особенно полезно при отладке отклонений в поведении приложений.
Как упоминалось ранее, события используются разработчиками, администраторами ИТ и разработчиками средств управления для отладки, мониторинга, диагностики и планирования ресурсов. Используемые методологии анализа, основанные на событиях, могут быть распределены по следующим категориям по используемым способам.
Сканирование Пользователи могут сканировать выгрузку событий в поисках одного важного события либо небольших последовательностей известных событий. Обычно так поступают, когда события используются для отладки случаев неполадок, связанных с проблемами конечных пользователей, либо для поиска значительных неполадок в журнале событий.
Разностный анализ Поскольку ETW снабжает каждое событие меткой времени и данными по нагрузке на центральный процессор, простейший разностный анализ вида
Property (Event B)-Property (Event A)
позволяет вести статистику по времени отклика и нагрузке на центральный процессор. Если два события являются метками начала и конца определенной задачи, большая последовательность событий, собранная для приложений в режиме эксплуатации, может быть обработана таким образом, давая заключение по статистике по времени отклика и нагрузке на центральный процессор.
Статистический анализ Иногда даже просто подсчет определенных событий позволяет сделать дополнительные выводы о поведении программного обеспечения .
Конечный автомат и отслеживание ресурсов Достаточный набор событий позволяет построение конечного автомата и моделирование, основанное на трассировках. Например, поскольку большая часть задач базовой ОС снабжена механизмом событий ETW, трассировку ОС можно использовать для построения конечного автомата, следящего за планировщиком, памятью, задачами ввода-вывода и так далее.
Сквозная трассировка Большие приложения часто состоят из некоторого числа распределенных компонентов, объединенных сложными связями. Зачастую они занимают несколько компьютеров, где каждый играет отдельную роль. Инструментарий, ориентированный на отслеживание запросов, является одним из подходов к борьбе с увеличивающейся сложностью диагностики проблем в таких окружениях. В этом варианте, инструментарий дополнительно используется в приложениях, записывая задачи вместе с уникальным идентификатором обслуживаемого запроса. После трассировки устанавливают связи между событиями, относящимися к одному запросу, чтобы можно было отследить его выполнение. Позже можно отдельно рассмотреть конкретные интересующие запросы для выявления проблем на разных этапах обслуживания, либо группа запросов может быть оценена статистческим анализом. Помощник производительности сервера (Server Performance Advisor, SPA), доступный через центр загрузки Microsoft – средство для управления и диагностики, использующее серверный инструментарий, разработанный для сквозной трассировки.

Единая модель и интерфейсы API поставщика событий
В Windows Vista представлен новый набор интерфейсов API поставщика событий, которые легче в употреблении и в то же время предлагают больше возможностей и расширенные настройки безопасности. Также новые интерфейсы задействованы при записи в Event Viewer, объединяя трассировочные файлы и журналы событий в согласованный набор API . В этом разделе модель и интерфейсы описаны более подробно. По мере необходимости будет подчеркиваться разница между новыми и существующими интерфейсами.
Чтобы стать поставщиком ETW, программному компоненту необходимо зарегистрироваться в ETW при помощи API EventRegister. EventRegister необходим глобально уникальный идентификатор (GUID), называемый ProviderId, который однозначно идентифицирует поставщика. Любой программный компонент (приложение, библиотека DLL или драйвер) может зарегистрироваться в качестве поставщика, поскольку нет обязательной привязки поставщика к какому-то компоненту ОС. Обычно регистрация производится в точке входа программного компонента: подпрограмме подключения DLL или входе в драйвер, например. Возвращается регистрационный дескриптор, который необходимо использовать в последующих вызовах API регистрации. В завершение выполнения поставщика вызывается EventUnregister.
В существующих API поставщика, поставщики должны задавать обратный вызов для уведомления о разрешении/запрете. То есть, когда контроллер включает поставщика, зарегистрированная функция обратного вызова вызывается с установкой включения. Одним из ее параметров является дескриптор сеанса, для которого производится включение поставщика. Получив обратный вызов включения, поставщик устанавливает глобальную переменную (например TracingOn) показывать включена или выключена трассировка, а также сохраняет дескриптор сеанса. В дальнейшем поставщик использует полученный в обратном вызове дескриптор сеанса при обращении к API регистрации (в зависимости от значения TracingOn).
В новой модели поставщика ETW запоминает настройки включения для поставщиков. Другими словами, поставщики регистрируются и используют вызовы регистрации без необходимости проверять, включены они или нет. В API регистрации ETW быстро проверяет настройки включения и отправляет события сеансам, только если они включены. В противном случае регистрирующий вызов игнорируется. Поэтому в новой модели обратный вызов включения/выключения необязателен. Однако, в некоторых ситуациях обратные вызовы все еще могут понадобиться. Например, инструментарию, предназначенному для построения конечного автомата, часто требуются события обзора состояния в начале и конце трассировок.
API регистрации, EventWrite, использует регистрационный дескриптор (в противоположность использованию дескриптора сеанса в старой модели). Использование регистрационного дескриптора в вызовах регистрации позволяет сделать настройки включения прозрачными для поставщиков. Поскольку используемый в регистрации дескриптор – это прозрачный дескриптор новой модели, ETW теперь поддерживает многоадресную доставку событий. То есть, поставщик может быть включен для нескольких сеансов ETW, что было невозможно в старой модели, где использовался дескриптор сеанса.
В ETW есть отдельные интерфейсы, EventEnabled и EventProviderEnabled, чтобы проверить, включен ли поставщик. Хотя API регистрации проверяют настройку включения перед записью событий, иногда поставщикам необходимо проделать дополнительную работу, когда трассировка включена. Одна из таких ситуаций возникает при сборе и заполнении данными информационного события, необязательного для выполнения приложения. При помощи этих интерфейсов поставщик может в любой момент выяснить, включена или выключена трассировка, если понадобится.
Как говорилось ранее, пользователи могут добавлять различные контекстные данные к каждому событию. Интерфейс API регистрации использует механизм scatter/gather для сбора данных, специфических для события. Вызывающие передают дополнительные данные к событиям, составляя массив дескрипторов данных. Дескриптор данных – это структура с полями указателя и размера. Пользователи добавляют по дескриптору данных для каждого включаемого элемента данных. Для облегчения составления дескриптора данных предоставлен макрос EventDataDescCreate. Затем ETW копирует предоставленное пользователем содержимое в буфер сеанса во время регистрации.
В соответственном манифесте событий при помощи тега <Template> должна быть указана компоновка события . Шаблон описывает заданные пользователем контекстные данные, включаемые в каждое событие. Шаблон может определить компоновку, содержащую индивидуальные поля, такие как числа и строки, либо сложные структуры данных, такие как массив структур. Наличие шаблон не обязательно для всех событий, если шаблон не указан, ожидается, что в событии не будет пользовательских данных. Несколько событий могут описываться одним шаблоном в манифесте, например события Start и Stop, контекстная информация которых одинакова. Когда событие достигает приложения-потребителя, приложение обнаруживает шаблон при помощи TDH API и декодирует данные события в соответствии с ним. Поставщики, использующие старые API, предоставляют информацию о компоновке через форматы управляемого объекта (Managed Object Formats, MOFs) платформы Windows Management Instrumentation (WMI).
На рис. 3 приведен пример кода поставщика, использующего новый API для регистрации и регистрации событий. Это поставщик пользовательского режима, использующий API поставщика пользовательского режима, но доступен также соответствующий набор API поставщика режима ядра. Первое событие на рис. 3 включает два предоставляемых пользователем элемента данных. Один из них типа ULONG, а другой – строка WCHAR, ограниченная NULL. EventDataDescCreate вызывается для построения соответствующего массива дескрипторов данных. В дополнение к показанному здесь интерфейсу EventWrite есть еще два API: EventWriteString и EventWriteTransfer. EventWriteString позволяет простую регистрацию необъявленной строки . При вызове EventWriteString ETW помечает в заголовке события, что данные события содержат единственную строку WCHAR, ограниченную NULL. В этом случае потребитель трактует пользовательские данные как строку, без необходимости поиска компоновки события через TDH. EventWriteString позволяет быструю регистрацию строк без изменения манифеста.
#include <myevents.h>   // Header generated from manifest. 
                        // Contains MyProviderId and event descriptors.

REGHANDLE MyProvRegHandle;
ULONG MyInteger;
PWCHAR MyString;
ULONG MyStringLength;
EVENT_DATA_DESCRIPTOR DataDescriptor[2];

...

// Register the ETW provider.
Status = EventRegister(&MyProviderId,      // ProviderId (GUID)
                       NULL,               // Optional Callback 
                       NULL,               // OPtioanl Callback Context
                       &MyProvRegHandle);  // Registration Handle

...

// Construct DataDescriptor and write an event with 
// MyInteger and MyString.
EventDataDescCreate(&DataDescriptor[0],    // DataDescriptor
                    &MyInteger,            // Pointer to the data
                    sizeof(ULONG));        // Size of data
EventDataDescCreate(&DataDescriptor[1], &MyString, MyStringLength);

Status = EventWrite(MyProvRegHandle,       // Registration Handle
                    MyEventDescriptor1,    // EventDescriptor
                    2,                     // DataDescriptor array size
                    DataDescriptor);       // DataDescriptor array

...

// Write another event with no user data.
if (EventEnabled(MyProvRegHandle, MyEventDescriptor2)) {
    // Do extra work if enabled and write event.
    ...

    Status = EventWrite(MyProvRegHandle, MyEventDescriptor2, 0, NULL);
}

...

// Unregister the ETW provider. 
Status = EventUnregister(MyProvRegHandle);  
Интерфейсы API EventWriteTransfer и EventActivityIdControl используются для обеспечения нужд инструментария для сквозной трассировки. Как описывалось выше, сквозная трассировка – это инструментальная методология, ориентированная на серверные приложения, одновременно выполняющие различные задачи для некоторого числа пользовательских запросов. Например, запрос на исполнение сценария на веб-странице исходит от клиентского компьютера и появляется на сетевом уровне на сервере. Затем он проходит через драйвер HTTP, IIS, ASP.NET и, возможно, Exchange Server на другом компьютере. Целью является его сквозная трассировка для записи всех связанных с этим запросом действий при помощи событий ETW для последующей отладки и анализа производительности. Чтобы отличать запросы, требуется уникальный идентификатор. По этому уникальному идентификатору во время обработки можно построить взаимосвязи.
Для этого в ETW каждое событие снабжается ActivityId. Каждое событие, регистрируемое через новый API, автоматически получает текущий идентификатор задачи, хранимый в выполняющемся потоке. Он отображается в выгрузке XML в теге <Correlation ActivityId> раздела <System>. Поставщик может устанавливать, считывать и создавать идентификатор задачи для выполняющегося потока , используя API EventActivityIdControl. Идентификатор задачи может сопровождать запрос на его пути через разные компоненты. К сожалению, в некоторых случаях может оказаться невозможным распространение идентификатора задачи из-за ограничений в открытых протоколах, ограничениях, налагаемых планом, и так далее. Интерфейс API EventWriteTransfer API создает событие передачи, свидетельствующее о передаче идентификатора задачи. В дополнение к параметрам EventWrite, EventWriteTransfer принимает еще два аргумента – ActivityId и RelatedActivityId.
Каждое событие помечено идентификатором поставщика и снабжено дескриптором события, определяющим стандартные сведения события и предоставляющим дополнительные идентификацию и семантику. Разработчики определяют дескрипторы событий для мест применения инструментария на стадии разработки инструментария и заносят соответствующие записи в манифест событий. Потом компилятор сообщений Message Compiler в окружении разработки по данному манифесту событий создает дескрипторы событий в заголовочном файле, который в свою очередь включается в исходные файлы. С позиции программирования, дескриптор события – это структура, состоящая из полей Id, Version, Channel, Level, Opcode, Task и Keywords:
typedef struct _EVENT_DESCRIPTOR {
    USHORT Id;
    UCHAR Version;
    UCHAR Channel;
    UCHAR Level;
    UCHAR Opcode;
    USHORT Task;
    ULONGLONG Keyword;
} EVENT_DESCRIPTOR, *PEVENT_DESCRIPTOR;
Id используется в качестве уникального идентификатора события в поставщике. Когда событие определено в манифесте, идентификатор события – единственная обязательная запись. Получая событие, потребитель использует его идентификатор поставщика (GUID) и идентификатор события (USHORT), чтобы найти манифест к нему. Аналогично, Version позволяет изменять и дополнять события в следующих выпусках, сохраняя ту же семантику и идентификатор события. Поэтому идентификатор события вместе с версией и идентификатором поставщика позволяют однозначно идентифицировать событие.
Канал определяет группу событий для целевой аудитории. Каналы относятся к одному из четырех типов: административный, рабочий, аналитический и отладочный. События, поступающие по административному каналу, дают основание для действий; получив такое событие, администратору необходимо немедленно разобраться, чем было вызвано событие и как на него реагировать. События, поступающие по рабочему каналу, ориентированы на средства мониторинга высокого уровня и поддерживающий персонал; они содержат более детальную информацию и поступают чаще, чем события в административном канале. События, группируемые в административный и рабочий каналы, автоматически отправляются в службу регистрации событий и отображаются в Event Viewer. Аналитический канал предназначен для традиционной трассировки, ориентированной на первоклассных специалистов поддержки либо на детализированные средства диагностики и поиска неисправностей . Отладочный канал предназначен для использования под отладочные сообщения и содержит события, употребляемые разработчиками. Аналитический и отладочный каналы по умолчанию не включены. События, предназначенные для различных целей и аудиторий, могут быть добавлены к каналам с использованием одного набора API.
Включая поставщика, контроллер может задать уровень (однобайтовое целое число) и ключевые слова (восьмибайтовая битовая маска). Уровень и ключевые слова используются для добавления размерности в инструментарий ETW. Уровень предназначен для фильтрации на основе уровня серьезности или уровня детальности событий. Ключевые слова призваны указывать составные компоненты поставщика. Например, разработчики могут разделить события на информационные события и события, информирующие о критических ошибках. Также они могут назначит различные ключевые слова составным компонентам поставщика. Избирательное включение с различными уровнями и ключевыми словами позволяет контроллеру трассировки включать поставщиков для регистрации только ошибочных событий от компонента B либо всех событий от компонентов A и C, и т. д.. Для поставщиков, использующих старые API, размер ключевого слова всего 4 байта, а фильтрация по уровню и ключевым словам должна делаться в явном виде в коде поставщика. Важно отметить, что, когда контроллер включает определенный уровень, все события со значением уровня меньше или равным (большая или такая же степень серьезности) заданному контроллером также включаются. Хотя разработчики могут разработать и назначить событиям уровни и ключевые слова, есть предопределенные уровни, как показано на рис. 4.
<levels>
    <level name="win:LogAlways" symbol="WINEVENT_LEVEL_LOG_ALWAYS" 
        value="0" message="$(string.level.LogAlways)"> Log Always 
    </level>
    <level name="win:Critical" symbol="WINEVENT_LEVEL_CRITICAL" value="1" 
        message="$(string.level.Critical)"> Only critical errors </level>
    <level name="win:Error" symbol="WINEVENT_LEVEL_ERROR" value="2" 
        message="$(string.level.Error)"> All errors, includes win:
        Critical </level>
    <level name="win:Warning" symbol="WINEVENT_LEVEL_WARNING" value="3" 
        message="$(string.level.Warning)"> All warnings, includes
        win:Error </level>
    <level name="win:Informational" symbol="WINEVENT_LEVEL_INFO"
        value="4" 
        message="$(string.level.Informational)"> All informational 
        content, including win:Warning </level>
    <level name="win:Verbose" symbol="WINEVENT_LEVEL_VERBOSE" value="5" 
        message="$(string.level.Verbose)"> All tracing, including 
        previous levels </level>
</levels>
Задание и код операции используются для присоединения дополнительной информации к каждому событию. Задание указывает общий логический компонент или снабжаемое инструментарием задание. Часто задание представляет ключевые высокоуровневые шаги, предпринимаемые компонентом для достижения цели. Код операции указывает на конкретную операцию, выполнявшуюся в момент создания события. Например, поставщик ядра группирует все события файлового ввода-вывода в задании «FileIO». Код операции уточняет, какая операция выполнялась – Create, Open, Read или Write. В отличие от идентификатора, версия, канал, уровень и ключевые слова, задание и код операции используются только для добавления сведений; они никак не влияют на управление инструментарием или поиск метаданных.
Дескрипторы событий и компоновки задаются в манифесте событий. Разработчики создают манифест событий при планировании инструментария. Манифест событий пишется в формате XML, где определенные пользователем каналы, задания и коды операций, уровни и ключевые слова задаются соответствующими тегами. Предопределенные каналы, уровни и коды операций ETW также можно использовать; однако предопределенные каналы относятся к глобальным каналам, и их следует использовать только для событий, ориентированных на админитраторов. Определения различных полей метаданных объединяются для каждого события в теге <Event>, однозначно связанном с идентификатором события. Для события можно также задать строку сообщения, которая будет показана с подстановкой данных из события, когда событие читается потребителем. На рис. 5 показан фрагмент XML из примера манифеста событий. Позже мы приведем пошаговую инструкцию для разработки и реализации инструментария событий.
<provider name="Microsoft-Windows-Kernel-Registry" 
    guid="{70eb4f03-c1de-4f73-a051-33d13d5413bd}" 
    symbol="RegistryProvGuid"
    resourceFileName="%SystemRoot%\System32\advapi32.dll" 
    messageFileName="%SystemRoot%\System32\advapi32.dll">
  <channels>
    <channel name="Microsoft-Windows-Kernel-Registry/Analytic" 
        chid="RegistryEvents" symbol="REG_Events" type="Analytic" 
        isolation="System">This channel contains registry 
        events.</channel>
  </channels>
  <opcodes>
    <opcode value="32" name="CreateKey" symbol="" />

    ...

  </opcodes>
  <keywords>
    <keyword name="CreateKey" symbol="" mask="0x1000" />

    ...

  </keywords>
  <templates>
    <template tid="tid_RegOpenCreate">
      <data name="BaseObject" inType="win:Pointer" 
         outType="win:HexInt64" />
      <data name="KeyObject" inType="win:Pointer" 
          outType="win:HexInt64" />
      <data name="Status" inType="win:UInt32" 
          outType="win:HexInt32" />
      <data name="Disposition" inType="win:UInt32" />
      <data name="BaseName" inType="win:UnicodeString" 
          outType="xs:string" />
      <data name="RelativeName" inType="win:UnicodeString" 
          outType="xs:string" />
    </template>

    ...

  </templates>
  <events>
    <event value="1" symbol="ETW_REGISTRY_EVENT_CREATE_KEY" 
        template="tid_RegOpenCreate" opcode="CreateKey" 
        channel="RegistryEvents" level="win:Informational" 
        keywords="CreateKey" 
        message="$(string.Registry.RegOpenCreate)"/>

        ...

      </events>
    </provider>

      ...

  <localization>
    <resources culture="en-US">
      <stringTable>
        <string id="Registry.RegOpenCreate" 
            value="Registry key %6 was created with status %3." />
      </stringTable>
    </resources>
  </localization>
Интерфейсы API управляемого поставщика ETW будут доступны в Microsoft® .NET Framework 3.5, кодовое название «Orcas». Интерфейсы управляемых потребителя и контроллера в данный момент находятся на стадии разработки. System.Diagnostics.Eventing содержит класс EventProvider. EventProvider предлагает методы для всей вышеописанной функциональности для стандартных приложений. Пользователяим необходимо реализовать класс с GUID поставщика, и использовать экземпляр класса для регистрации событий. EventDescriptor является структурой, эквивалентной дескриптору события в стандартных API. В отличие от случая стандартных приложений, для управляемых приложений EventDescriptor не создается. Тем не менее, рассматривается средство, способное создавать код, направленный на достижение большей производительности и надежности. На рис. 6 показан образец использования класса EventProvider.
using System.Diagnostics.Eventing;
...

static void Main(string[] ArgRead)
{
    int MyInteger;
    string MyString;

    ...

    // Construct event descriptor.
    EventDescriptor Event1 = new EventDescriptor(5, 0, 0, 2, 0, 0, 0);

    // Instantiate event provider.
    EventProvider etwProvider = new EventProvider(
        new Guid("d58c126f-b309-11d1-969e-0000f875a5bc"));

    ...

    // Write an event with MyInteger and MyString.
    etwProvider.WriteEvent(ref Event1, MyInteger, MyString);

    ...
}
В дополнение, ETW в Windows Vista предлагает улучшенные возможности безопасности для поставщиков. По умолчанию любой поставщик может регистрировать и создавать события. Однако разработчики могут снабдить GUID ограничениями, так что только авторизованные пользователи могу зарегистрировать поставщика с этим GUID. Поставщик может также указать, кому позволяется его включать. Более того, ETW позволяет контроллеру объявить сеанс безопасным, что означает, что он будет получать события только от определенной группы пользователей.
Поставщик устанавливается, как только компилируется выполняемый файл с инструментарием поставщика событий. После этого пользователи могут получать события от поставщика, используя средство logman, входящее в комплект приложение-контроллер. Приведенная ниже команда logman.exe делает два вызова управляющего API ETW, которые создают сеанс ETW с именем «mysession» и включают поставщика:
> logman start mysession -p <provider name> -o mytest.etl -ets
Сеанс «mysession» записывают события в файл mytest.etl. У logman.exe есть параметры командной строки, позволяющие настройку режимов регистрации, буферов и т. д. Здесь <provider name> может быть именем поставщика в манифесте либо GUID, используемый поставщиком для API EventRegister . Если GUID поставщика 11223344-5566-7788-99aa-bbccddeeff00, собственно команда превращается в следующее:
> logman start mysession 
-p {11223344-5566-7788-99aa-bbccddeeff00} 
-o mytest.etl -ets
ETW позволяет контроллеру предварительно включить поставщика, прежде чем тот регистрируется и выполняется. Это позволяет пользователям получать трассировки запуска для динамически подгружаемых библиотек и драйверов, где загрузку двоичных образов тяжело контролировать. Контроллер запускает сеанс и включает поставщика, прежде чем начнется выполнение поставщика. Как только поставщик регистрируется, он включен.
После сбора событий сеанс может быть остановлен параметром командной строки logman.exe stop.
> logman stop mysession -ets
В том же каталоге, где была запущена команда logman, должен теперь существовать файл трассировки mytest.etl. Для выгрузки событий из этого файла можно воспользоваться Tracerpt.exe:
> tracerpt mytest.etl
Эта команда создает файл выгрузки ( dumpfile.xml) и текстовый файл заключения (по умолчанию summary.txt). Файл заключения содержит краткое обобщение статистики по событиям. Файл выгрузки содержит XML выгрузку событий, как показанное на рис. 2. Поля данных из заголовка события представлены в разделе <System>. Идентификаторы процесса, потока, процессора и загрузка CPU содержатся в теге <Execution> раздела <System>. Также раздел <System> содержит значения полей дескриптора события. Варианты имен файлов могут быть заданы пользователем как параметры команды tracerpt.exe. Существуют другие средства графического интерфейса пользователя в Windows Vista, которые мы обсудим позднее, могущие собирать и просматривать события.
В Windows Vista уже установлены сотни поставщиков событий. Пользователи могут увидеть перечень этих поставщиков в PerfMon или Event Viewer, или при помощи команды logman.exe:
> logman query providers

Рекомендации по разработке и реализации
В этом разделе обсуждаются шаги по созданию файла манифеста, определяющего события, созданию кода с вызовами ETW API и успешной сборке и установке поставщика событий.
Разработка манифеста инструментария Манифест инструментария является файлом формата XML, определяющим поставщика событий и события, которые поставщик регистрирует в ETW. Каждое событие состоит из стандартных метаданных и произвольных данных. Манифест можно создать вручную или при помощи средства создания манифестов, доступного в SDK (ecmangen.exe). Шаги таковы:
  1. Завести поставщика событий и определить его идентификатор (GUID).
  2. Определить логические задачи компонента и особые места применения инструментария внутри них. Так определяются задачи и коды операций для создаваемых поставщиком событий.
  3. Определить составные компоненты, которые будут регистрировать события и назначить каждому ключевое слово. Это позволяет контроллеру избирательно включать события.
  4. Оценить целевые аудитории разрабатываемых событий и определить каналы для каждой из них.
  5. Создать различные структуры, определяющие произвольные данные пользовательского контекста. Это делается в разделе <templates>.
  6. Собрать все воедино и определить события в разделе манифеста <events>. Назначить каждому событию символьный идентификатор, степень серьезности, ключевые слова, канал, задание, код операции и шаблон. Также можно добавить переводимое сообщение для данного события.
Создание кода с вызовами ETW API Когда события определены в манифесте, используйте унифицированные API для отправки событий ETW. Для стандартных приложений, сперва создайте заголовочный файл, содержащий определения событий из манифеста, для включения в код поставщика. Далее используйте API ETW для отправки разработанных ранее событий. Вот как это делается:
  1. Обработайте созданный ранее манифест компилятором сообщений (mc.exe). В результате создастся заголовочный файл, содержащий структуры дескрипторов событий, где имя каждой из структур совпадает с символьным идентификатором события. Компилятор сообщений также создает файл строк ресурсов. Созданный заголовочный файл необходимо включить в код поставщика.
  2. Прежде чем разрабатывать код для создания событий, вспомните, что поставщику необходимо зарегистрироваться, используя идентификатор поставщика, заданный в манифесте, вызывая функцию EventRegister. Регистрационный дескриптор, полученный от EventRegister, необходимо сохранить для использования в вызовах API регистрации событий и соответствующем вызове EventUnregister.
  3. Пользуйтесь EventWrite для регистрации событий, определенных в манифесте. Особые данные передаются через массив дескрипторов данных, как показано на рис. 3. (Разработчикам необходимо убедиться, что поля данных точно совпадают с соответствующими шаблонами событий в манифесте). Передавайте правильную структуру дескриптора события, определенную в заголовочном файле.
  4. После регистрации всех необходимых событий вызовите функцию EventUnregister, передавая ей регистрационный дескриптор.
Компиляция разработанной программы Сведения из манифеста, определяющие события, должны быть скомпилированы в процессе сборки и включены в качестве ресурса в исполняемый файл поставщика. Все локализуемые строки будут изъяты из этого скомпилированного манифеста и помещены в отдельный файл ресурсов, который будет компилироваться и собираться отдельно. Когда исполняемый файл поставщика развертывается, сведения из манифеста необходимо установить в систему, чтобы и контроллеры, и потребители могли найти поставщика и определения событий. Все необходимые средства доступны в Windows Vista или в SDK. Необходимые шаги:
  1. Если для событий определены локализуемые строки сообщений, необходимо вызвать rc.exe, чтобы скомпилировать файл ресурсов в .res.
  2. При использованииVisual Studio® разработчик может настроить предваряющую сборку команду, вызывающую rc.exe для компиляции ресурсов в .res, и включить скомпилированный файл ресурсов в проект.
  3. Скомпилируйте приложение или драйвер, включая скомпилированный файл ресурсов при помощи cl.exe или link.exe (для пользователей Visual Studio это делается на шаге 2).
  4. При установке поставщика запустите средство wevtutil.exe с параметром –im, чтобы установить манифест, определяющий события. Это позволяет контроллерам обнаруживать поставщиков событий, а также позволяет потребителям находить информацию для правильного декодирования событий.

Инструменты и поддержка
Мы уже пользовались контроллером с интерфейсом командной строки logman.exe и универсальным потребителем tracerpt.exe. У logman.exe есть параметры, позволяющие задавать расписание трассировки на удаленных компьютерах. tracerpt.exe умеет выгружать события в формате CSV, а также умеет строить отчеты по потреблению ресурсов для некоторых известных событий ОС, когда используется параметр -report. На рис. 7 показана таблица часто используемых файлов, построенная tracerpt.exe после обработки и связывания некоторых известных событий базовой ОС по принципу построения конечного автомата.
Рис. 7 Отчет по часто используемым файла, построенный Tracerpt (Щелкните изображение, чтобы увеличить его)
Монитор надежности и производительности (Reliability and Performance Monitor, RPM), включающий PerfMon вWindows Vista, предлагает графический интерфейс для сбора трассировок. Используя интерфейс Event Trace Session, пользователи могут задавать сеанс трассировки, выбирать включаемых поставщиков и запускать и останавливать сеансы ETW . На рис. 8 показан интерфейс RPM, когда пользователь просматривает список поставщиков для включения. В RPM введена концепция Data Collector Set, когда все необходимые сведения о трассировке событий, счетчиках производительности и сборе конфигурационных данных (системный реестр и классы WMI) объединяются в одну конфигурацию сбора данных. Пользователи могут создать конфигурацию сбора данных с включенными для трассировки поставщиками и данными о счетчиках. Настройки созданной конфигурации сбора данных хранятся в RPM, так что пользователи могут запускать сбор данных без необходимости каждый раз указывать источники данных. Эта возможность поддерживает часто встречающиеся ситуации диагностики, когда для известных ситуаций диагностики пользователи могут задать поставщиков для сбора данных и трассировок.
Рис. 8 Поставщики трассировок событий в RPM (Щелкните изображение, чтобы увеличить его)
Event Viewer предлагает другие средства для сбора и просмотра событий. Новые API были также разработаны для событий, направляемых в службу регистрации событий. События административного и рабочего каналов автоматически направляются и отображаются в Event Viewer, чтобы можно было наблюдать за важным программным обеспечением. Также расширена функциональность Event Viewer в отношении аналитических и отладочных событий. Возможность «View» позволяет просматривать все имеющиеся аналитические и отладочные каналы, пользователи могут разрешать трассировку щелчком правой кнопки мыши. На рис. 9 пользователь разрешает трассировку аналитического канала поставщика событий системного реестра.
В SDK есть примеры кода для интерфейсов API поставщика, контроллера и потребителя. Примеры поставщиков, использующих новый API поставщика событий, включают манифесты для регистрируемых событий. Также доступен пример драйвера.
Рис. 9 Интерфейс Event Viewer для сбора аналитических событий (Щелкните изображение, чтобы увеличить его)

Заключение
Мы представили единую модель поставщика событий и интерфейсы API ETW, обновленные в Windows Vista. Наша цель – представить API разработчикам и предложить примеры использования и общие рекомендации. Для многих разработчиков трассировка все еще является незнакомой технологией. Мы надеемся, что эта статья позволяет понять одну из ключевых инструментальных и диагностических инфраструктур платформы Windows. Через несколько лет после представления ETW в качестве общей технологии трассировки в операционной системе Windows 2000 значительное число компонентов и приложений использовали ETW в качестве основы для своих возможностей трассировки и управляемости, и эта тенденция, скорее всего, останется.
Мы ожидаем, что большее число разработчиков начнет задействовать технологию событийного инструментария для создания более надежных, более производительных и легче управляемых приложений. Инструментарий не только поможет этим разработчикам в отладке своих приложений, но также поможет людям, развертывающим и управляющим этими приложениями. Более того, инструментарий также поможет в планировании ресурсов, анализе тенденций и поиске узких мест в производительности. ETW предоставляет полезную трассировочную инфраструктуру, чтобы позволить приложениям обеспечить средства подхода ко всем вышеописанным ситуациям. Мы ожидаем, что эта технология будет играть фундаментальную роль базового строительного блока в попытке сделать приложения более управляемыми и диагнозируемыми.

Доктор Инсунг Парк (Insung Park) – ведущий разработчик в группе Windows Instrumentation Platform. У него опубликовано с десяток работ по анализу производительности, отслеживанию запросов, instrumentation технологии, методологии и поддержки программирования. Его адрес электронной почты insungp@microsoft.com.

Рики Буч (Ricky Buch) – руководитель программы в группе Windows Instrumentation Platform. Он работает над технологиями трассировки событий для Windows (Event Tracing for Windows, ETW) и библиотеки счетчиков производиетельности (Performance Counter Library). С ним можно связаться по адресу ricky.buch@microsoft.com.

Page view tracker