-
Notifications
You must be signed in to change notification settings - Fork 2
/
etw-provider.h
234 lines (201 loc) · 8.37 KB
/
etw-provider.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// Copyright 2019 Bill Ticehurst. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Provides constants and a base class to use in the implementation of an ETW
// provider. To use, derive a class from EtwProvider which constructs the base
// class with the GUID and name for the provider, e.g.
//
// ExampleEtwProvider::ExampleEtwProvider()
// : EtwProvider(example_provider_guid, example_provider_name) {}
//
// To log events, implement member functions that define the necessary event
// metadata, and then call the LogEventData member function with the metadata
// and field values, e.g.
//
// void ExampleEtwProvider::Log3Fields(int val, const std::string& msg, void* addr)
// {
// constexpr static auto event_desc = EventDescriptor(100);
// constexpr static auto event_meta = EventMetadata("my1stEvent",
// Field("MyIntVal", etw::kTypeInt32),
// Field("MyMsg", etw::kTypeAnsiStr),
// Field("Address", etw::kTypePointer));
//
// LogEventData(&event_desc, &event_meta, val, msg, addr);
// }
#pragma once
#include <Windows.h>
#include <evntprov.h>
#include <string>
#include <vector>
#include "./etw-metadata.h"
namespace etw {
// Taken from the TRACE_LEVEL_* macros in <evntrace.h>
const UCHAR kLevelNone = 0;
const UCHAR kLevelFatal = 1;
const UCHAR kLevelError = 2;
const UCHAR kLevelWarning = 3;
const UCHAR kLevelInfo = 4;
const UCHAR kLevelVerbose = 5;
// Taken from the EVENT_TRACE_TYPE_* macros in <evntrace.h>
const UCHAR kOpCodeInfo = 0;
const UCHAR kOpCodeStart = 1;
const UCHAR kOpCodeStop = 2;
// See "enum TlgIn_t" in <TraceLoggingProvider.h>
const UCHAR kTypeAnsiStr = 2;
const UCHAR kTypeInt8 = 3;
const UCHAR kTypeInt32 = 7;
const UCHAR kTypeDouble = 12;
const UCHAR kTypePointer = 21;
// All "manifest-free" events should go to channel 11 by default
const UCHAR kManifestFreeChannel = 11;
// Creates a constexpr EVENT_DESCRIPTOR structure for use with ETW calls
constexpr auto EventDescriptor(USHORT id, UCHAR level = 0,
ULONGLONG keyword = 0, UCHAR opcode = 0,
USHORT task = 0) {
return EVENT_DESCRIPTOR{id,
0, // Version
kManifestFreeChannel,
level,
opcode,
task,
keyword};
}
class EtwProvider {
public:
// An event provider should be a singleton in a process. Disable copy/move.
EtwProvider(const EtwProvider&) = delete;
EtwProvider& operator=(const EtwProvider&) = delete;
// GCC/Clang supported builtin for branch hints
#if defined(__GNUC__)
#define LIKELY(condition) (__builtin_expect(!!(condition), 1))
#else
#define LIKELY(condition) (condition)
#endif
// For use by this class before calling EventWrite
bool IsEventEnabled(const EVENT_DESCRIPTOR* event_desc) {
if (LIKELY(this->enabled_ == false)) return false;
return (event_desc->Level <= this->level_) &&
(event_desc->Keyword == 0 ||
((event_desc->Keyword & this->keywords_) != 0));
}
// For use by user-code before constructing event data
bool IsEventEnabled(UCHAR level, ULONGLONG keywords = 0) {
if (LIKELY(this->enabled_ == false)) return false;
return (level <= this->level_) &&
(keywords == 0 || ((keywords & this->keywords_) != 0));
}
#undef LIKELY
void SetMetaDescriptors(EVENT_DATA_DESCRIPTOR* data_descriptor,
const void* metadata,
size_t size) {
// Note: May be able to just set the name on the provider if only Win10 or
// later can be supported. See the docs for EventSetInformation and
// https://docs.microsoft.com/en-us/windows/win32/etw/provider-traits
EventDataDescCreate(data_descriptor, traits_.data(), traits_.size());
data_descriptor->Type = EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA;
++data_descriptor;
EventDataDescCreate(data_descriptor, metadata, static_cast<ULONG>(size));
data_descriptor->Type = EVENT_DATA_DESCRIPTOR_TYPE_EVENT_METADATA;
}
ULONG LogEvent(const EVENT_DESCRIPTOR* event_descriptor,
EVENT_DATA_DESCRIPTOR* data_descriptor, ULONG desc_count) {
if (reg_handle_ == 0) return ERROR_SUCCESS;
return EventWriteTransfer(reg_handle_, event_descriptor,
NULL /* ActivityId */,
NULL /* RelatedActivityId */,
desc_count,
data_descriptor);
}
// One or more fields to set
template <typename T, typename... Ts>
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR *data_descriptors,
const T& value, const Ts&... rest) {
EventDataDescCreate(data_descriptors, &value, sizeof(value));
SetFieldDescriptors(++data_descriptors, rest...);
}
// Specialize for strings
template <typename... Ts>
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR *data_descriptors,
const std::string& value, const Ts&... rest) {
EventDataDescCreate(data_descriptors, value.data(),
static_cast<ULONG>(value.size() + 1));
SetFieldDescriptors(++data_descriptors, rest...);
}
// Base case, no fields left to set
void SetFieldDescriptors(EVENT_DATA_DESCRIPTOR *data_descriptors) {}
// Template LogEvent used to simplify call
template <typename T, typename... Fs>
void LogEventData(const EVENT_DESCRIPTOR* event_descriptor, T* meta,
const Fs&... fields) {
if (!IsEventEnabled(event_descriptor)) return;
const size_t descriptor_count = sizeof...(fields) + 2;
EVENT_DATA_DESCRIPTOR descriptors[sizeof...(fields) + 2];
SetMetaDescriptors(descriptors, meta->bytes, meta->size);
EVENT_DATA_DESCRIPTOR *data_descriptors = descriptors + 2;
SetFieldDescriptors(data_descriptors, fields...);
LogEvent(event_descriptor, descriptors, descriptor_count);
}
// Called whenever the the state of providers listening changes.
// Also called immediately on registering if there is already a listener.
static void NTAPI EnableCallback(
const GUID *source_id,
ULONG is_enabled,
UCHAR level, // Is 0xFF if not specified by the session
ULONGLONG match_any_keyword, // 0xFF...FF if not specified by the session
ULONGLONG match_all_keyword,
EVENT_FILTER_DESCRIPTOR *filter_data,
VOID *callback_context) {
if (callback_context == nullptr) return;
EtwProvider* the_provider = static_cast<EtwProvider*>(callback_context);
switch (is_enabled) {
case 0: // EVENT_CONTROL_CODE_DISABLE_PROVIDER
the_provider->enabled_ = false;
break;
case 1: // EVENT_CONTROL_CODE_ENABLE_PROVIDER
the_provider->enabled_ = true;
the_provider->level_ = level;
the_provider->keywords_ = match_any_keyword;
break;
}
}
bool enabled() { return enabled_; }
void set_enabled(bool value) { enabled_ = value; } // For testing only
protected:
// All creation/deletion should be via derived classes, hence protected.
EtwProvider(const GUID& provider_guid, const std::string& provider_name)
: enabled_(false),
provider_(provider_guid),
name_(provider_name),
reg_handle_(0),
level_(0),
keywords_(0) {
ULONG result =
EventRegister(&provider_,
EtwProvider::EnableCallback, this, ®_handle_);
if (result != ERROR_SUCCESS) {
// Note: Fail silenty here, rather than throw. Tracing is typically not
// critical, and this means no exception support is needed.
reg_handle_ = 0;
return;
}
// Copy the provider name, prefixed by a UINT16 length, to a buffer.
// The string in the buffer should be null terminated.
// See https://docs.microsoft.com/en-us/windows/win32/etw/provider-traits
size_t traits_bytes = sizeof(UINT16) + name_.size() + 1;
traits_.resize(traits_bytes, '\0'); // Trailing byte will already be null
*reinterpret_cast<UINT16*>(traits_.data()) = traits_bytes;
name_.copy(traits_.data() + sizeof(UINT16), name_.size(), 0);
}
~EtwProvider() {
if (reg_handle_ != 0) EventUnregister(reg_handle_);
}
private:
bool enabled_;
const GUID provider_;
const std::string name_;
REGHANDLE reg_handle_;
std::vector<char> traits_;
UCHAR level_;
ULONGLONG keywords_;
};
} // namespace etw