ETW: отправка события через существующего поставщика

У меня есть приложение, которое использует собственные плагины. У меня есть собственный двоичный формат для этих плагинов. Каждый подключаемый модуль загружается во время выполнения с использованием метода, аналогичного сопоставлению DLL с пространством процесса. Это означает, что каждый плагин имеет свой собственный ImageBase, такие разделы, как .text или .data, обрабатываются так же, как и обычные библиотеки DLL. Единственное, что отличается, это двоичный формат плагина (это не файл PE) и код загрузчика, который сопоставляет плагин с пространством процесса.

Теперь я знаю, что ETW при выполнении трассировки с помощью этой командной строки:

xperf -on latency -stackwalk profile -buffersize 1024 -minbuffers 300 -start tracea1 -on Microsoft-Windows-Win32k:::'stack' 

будут генерировать события, которые можно использовать для восстановления среды процесса во время захвата трассировки. То есть он будет генерировать такие события, как «добавить процесс», «добавить поток в процесс», «добавить модуль DLL в процесс», чтобы такие инструменты, как xperfview, могли построить виртуальную среду состояния процессов в системе и построить информация, такая как текущее дерево процессов. Эти события, например, События ImageLoad, которые предоставляют информацию о каждой библиотеке DLL, загружаемой до или во время трассировки.

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

Что я хотел бы сделать, так это написать свои собственные EventWrites в моем коде загрузчика плагинов и генерировать эти события ImageLoad с необходимой информацией, чтобы xperfview и подобные инструменты могли интерпретировать мои плагины как обычные библиотеки DLL. Я бы заполнил необходимую информацию, такую ​​​​как ImageBase, ProcessId, ImageSize и т. д.

Для этого я понимаю, что мне нужно зарегистрировать провайдера события MSNT_SystemTrace, который является владельцем ImageLoad событий, построить событие с такой структурой:

    <Data Name="ImageBase">0x7FEFDBD0000</Data>
    <Data Name="ImageSize">0x12D000</Data>
    <Data Name="ProcessId">     548</Data>
    ...
    <Data Name="Reserved0">       0</Data>
    <Data Name="DefaultBase">0x7FEFDBD0000</Data>

и излучать событие.

Проблема в том, что я получаю ERROR_ACCESS_DENIED при попытке зарегистрировать еще один MSNT_SystemTrace, что логично, так как этот провайдер уже существует.

Но это заставляет меня задать вопрос, а поддерживается ли ETW тем, что я пытаюсь сделать?


person antonone    schedule 23.10.2013    source источник


Ответы (1)


Думаю, я нашел решение.

Хотя я не знаю, как передать событие через существующий провайдер в реальном времени, Windows 8 предоставляет интерфейс, который позволяет изменять журналы трассировки ETL, поэтому можно изменить ProviderId события на другое значение. Речь идет об интерфейсе ITraceRelogger. Вам нужны эти руководства:

EXTERN_GUID(CLSID_TraceRelogger, 0x7b40792d, 0x05ff, 0x44c4, 0x90, 0x58, 0xf4, 0x40, 0xc7, 0x1f, 0x17, 0xd4);
DEFINE_GUID(IID_ITraceRelogger, 0xF754AD43, 0x3BCC, 0x4286, 0x80, 0x09,0x9C, 0x5D, 0xA2, 0x14, 0xE8, 0x4E); // {F754AD43-3BCC-4286-8009-9C5DA214E84E}
DEFINE_GUID(IID_ITraceEventCallback, 0x3ED25501, 0x593F, 0x43E9, 0x8F, 0x38,0x3A, 0xB4, 0x6F, 0x5A, 0x4A, 0x52); // {3ED25501-593F-43E9-8F38-3AB46F5A4A52}

и relogger.h файл из Windows 8 SDK (c:\Program Files (x86)\Windows Kits\8.0\Include\um\relogger.h). Исходный relogger.h кажется каким-то образом сломанным, потому что он ссылается на некоторые внешние символы, но, похоже, нет файла LIB, чтобы дополнить его. Я уверен, что вы сумеете решить эту проблему!

Чтобы использовать его, просто создайте экземпляр:

ITraceRelogger *relog = NULL;
hres = CoCreateInstance(CLSID_TraceRelogger, 0, CLSCTX_INPROC_SERVER, IID_ITraceRelogger2, (LPVOID *)& relog);

добавьте вам файлы input.etl и output.etl:

#include <windows.h>
#include <cguid.h>
#include <atlbase.h>
#include <comdef.h>
...
CComBSTR input = "input.etl";
CComBSTR output = "output.etl";
...
hres = relog->AddLogfileTraceStream(input, NULL, & trace);
...
hres = relog->SetOutputFilename(output);

Затем вам нужно зарегистрировать обратный вызов, который будет обрабатывать модификацию события. Пример реализации обратного вызова события помещен в конце этого ответа. Вот код того, как использовать его с текущим ITraceRelogger:

EventCallback *ec = new EventCallback();
hres = relog->RegisterCallback(ec);
...
hres = relog->ProcessTrace();

Предупреждение: ProcessTrace() вернет ошибку, если вы не зарегистрируете никаких обратных вызовов.

Вот пример рабочего обратного вызова:

class EventCallback: public ITraceEventCallback {
private:
    DWORD ref_count;
    DWORD64 evno;

public:
    EventCallback() {
        ref_count = 0;
        evno = 0;
    }

    STDMETHODIMP QueryInterface(const IID& iid, void **obj) {
        if(iid == IID_IUnknown) {
            *obj = dynamic_cast<IUnknown *>(this);
        } else if(iid == IID_ITraceEventCallback) {
            *obj = dynamic_cast<ITraceEventCallback *>(this);
        } else {
            *obj = NULL;
            return E_NOINTERFACE;
        }

        return S_OK;
    }

    STDMETHODIMP_ (ULONG) AddRef(void) {
        return InterlockedIncrement(& ref_count);
    }

    STDMETHODIMP_ (ULONG) Release() {
        ULONG ucount = InterlockedDecrement(& ref_count);
        if(ucount == 0) {
            delete this;
        }

        return ucount;
    }

    HRESULT STDMETHODCALLTYPE OnBeginProcessTrace(ITraceEvent *HeaderEvent, ITraceRelogger *Relogger)   {
        return S_OK;
    }

    HRESULT STDMETHODCALLTYPE OnEvent(ITraceEvent *Event, ITraceRelogger *Relogger) {
        // Your main method.
        evno++;
        Relogger->Inject(Event);
    }


    HRESULT STDMETHODCALLTYPE OnFinalizeProcessTrace(ITraceRelogger *Relogger) {
        return S_OK;
    }
};

Relogger->Inject скопирует текущий Event в выходной файл. Вы можете использовать MSDN для ITraceRelogger, чтобы проверить доступные методы, которые позволят вам изменить желаемые свойства события. Меня заинтересовал метод SetProviderId().

Кроме того, имейте в виду, что MSDN утверждает, что ITraceRelogger доступен в Windows 7. Это не то, с чем я столкнулся - я не могу создать экземпляр этого класса в Windows 7, поэтому MSDN может иметь неверную информацию по этому вопросу.

person antonone    schedule 26.11.2013
comment
Похоже, что после установки этого обновления: support.microsoft.com/kb/2882822 вы сможет использовать этот класс из Win7. - person antonone; 28.01.2014