Document of c-modernization-kit (util) 1.0.0

Loading...
Searching...
No Matches
etw-session.c
Go to the documentation of this file.
1#ifdef _WIN32
2
3#include <windows.h>
4#include <evntrace.h>
5#include <evntcons.h>
6#pragma comment(lib, "Advapi32.lib")
7#include <trace-etw-util.h>
8#include <stdlib.h>
9#include <stdio.h>
10
11#ifndef INVALID_PROCESSTRACE_HANDLE
12#define INVALID_PROCESSTRACE_HANDLE ((TRACEHANDLE)INVALID_HANDLE_VALUE)
13#endif
14
18 struct etw_session
19{
21 TRACEHANDLE session_handle;
23 TRACEHANDLE trace_handle;
25 HANDLE thread_handle;
27 etw_event_callback_t callback;
29 void *context;
31 EVENT_TRACE_PROPERTIES *properties;
33 wchar_t *session_name_w;
35 GUID provider_guid;
36};
37
42 static void zero_bytes(void *ptr, size_t size)
43{
44 unsigned char *p = (unsigned char *)ptr;
45 size_t i;
46 for (i = 0; i < size; i++)
47 {
48 p[i] = 0;
49 }
50}
51
56 static int parse_guid(const char *str, GUID *guid)
57{
58 unsigned int d[11];
59 int n;
60
61 if (str == NULL || guid == NULL)
62 {
63 return -1;
64 }
65
66 n = sscanf_s(str,
67 "%8x-%4x-%4x-%2x%2x-%2x%2x%2x%2x%2x%2x",
68 &d[0], &d[1], &d[2], &d[3], &d[4],
69 &d[5], &d[6], &d[7], &d[8], &d[9], &d[10]);
70 if (n != 11)
71 {
72 return -1;
73 }
74
75 guid->Data1 = (ULONG)d[0];
76 guid->Data2 = (USHORT)d[1];
77 guid->Data3 = (USHORT)d[2];
78 guid->Data4[0] = (UCHAR)d[3];
79 guid->Data4[1] = (UCHAR)d[4];
80 guid->Data4[2] = (UCHAR)d[5];
81 guid->Data4[3] = (UCHAR)d[6];
82 guid->Data4[4] = (UCHAR)d[7];
83 guid->Data4[5] = (UCHAR)d[8];
84 guid->Data4[6] = (UCHAR)d[9];
85 guid->Data4[7] = (UCHAR)d[10];
86 return 0;
87}
88
92 static int guid_equal(const GUID *a, const GUID *b)
93{
94 return a->Data1 == b->Data1 &&
95 a->Data2 == b->Data2 &&
96 a->Data3 == b->Data3 &&
97 a->Data4[0] == b->Data4[0] &&
98 a->Data4[1] == b->Data4[1] &&
99 a->Data4[2] == b->Data4[2] &&
100 a->Data4[3] == b->Data4[3] &&
101 a->Data4[4] == b->Data4[4] &&
102 a->Data4[5] == b->Data4[5] &&
103 a->Data4[6] == b->Data4[6] &&
104 a->Data4[7] == b->Data4[7];
105}
106
113 static VOID WINAPI event_record_callback(PEVENT_RECORD pEvent)
114{
115 etw_session_t *session;
116 const char *message;
117 int level;
118
119 if (pEvent == NULL)
120 {
121 return;
122 }
123
124 session = (etw_session_t *)pEvent->UserContext;
125 if (session == NULL || session->callback == NULL)
126 {
127 return;
128 }
129
130 /* プロバイダ GUID でフィルタリング */
131 if (!guid_equal(&pEvent->EventHeader.ProviderId, &session->provider_guid))
132 {
133 return;
134 }
135
136 level = pEvent->EventHeader.EventDescriptor.Level;
137
138 message = NULL;
139 if (pEvent->UserData != NULL && pEvent->UserDataLength > 0)
140 {
141 message = (const char *)pEvent->UserData;
142 }
143
144 session->callback(level, message, session->context);
145}
146
150 static DWORD WINAPI trace_thread_proc(LPVOID param)
151{
152 etw_session_t *session = (etw_session_t *)param;
153
154 ProcessTrace(&session->trace_handle, 1, NULL, NULL);
155 return 0;
156}
157
161 static void set_status(int *out_status, int value)
162{
163 if (out_status != NULL)
164 {
165 *out_status = value;
166 }
167}
168
169 int TRACE_ETW_UTIL_API
170 etw_session_check_access(void)
171{
172 static const wchar_t probe_name[] = L"EtwUtil_AccessProbe";
173 size_t props_size;
174 EVENT_TRACE_PROPERTIES *props;
175 TRACEHANDLE handle = 0;
176 ULONG status;
177 int result;
178
179 props_size = sizeof(EVENT_TRACE_PROPERTIES)
180 + sizeof(probe_name);
181 props = (EVENT_TRACE_PROPERTIES *)malloc(props_size);
182 if (props == NULL)
183 {
184 return ETW_SESSION_ERR_SYSTEM;
185 }
186
187 zero_bytes(props, props_size);
188 props->Wnode.BufferSize = (ULONG)props_size;
189 props->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
190 props->Wnode.ClientContext = 1;
191 props->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
192 props->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
193
194 status = StartTraceW(&handle, probe_name, props);
195
196 if (status == ERROR_SUCCESS)
197 {
198 ControlTraceW(handle, NULL, props, EVENT_TRACE_CONTROL_STOP);
199 result = ETW_SESSION_OK;
200 }
201 else if (status == ERROR_ACCESS_DENIED)
202 {
203 result = ETW_SESSION_ERR_ACCESS;
204 }
205 else
206 {
207 result = ETW_SESSION_ERR_SYSTEM;
208 }
209
210 free(props);
211 return result;
212}
213
214etw_session_t *TRACE_ETW_UTIL_API
215 etw_session_start(const char *session_name,
216 const char *provider_guid_str,
217 etw_event_callback_t callback,
218 void *context,
219 int *out_status)
220{
221 etw_session_t *session = NULL;
222 GUID provider_guid;
223 ULONG status;
224 int name_len_w;
225 size_t props_size;
226 ENABLE_TRACE_PARAMETERS etp = {0};
227
228 if (session_name == NULL || provider_guid_str == NULL || callback == NULL)
229 {
230 set_status(out_status, ETW_SESSION_ERR_PARAM);
231 return NULL;
232 }
233
234 if (parse_guid(provider_guid_str, &provider_guid) != 0)
235 {
236 set_status(out_status, ETW_SESSION_ERR_PARAM);
237 return NULL;
238 }
239
240 /* セッション名をワイド文字列に変換 */
241 name_len_w = MultiByteToWideChar(CP_UTF8, 0, session_name, -1, NULL, 0);
242 if (name_len_w <= 0)
243 {
244 set_status(out_status, ETW_SESSION_ERR_PARAM);
245 return NULL;
246 }
247
248 session = (etw_session_t *)malloc(sizeof(etw_session_t));
249 if (session == NULL)
250 {
251 set_status(out_status, ETW_SESSION_ERR_SYSTEM);
252 return NULL;
253 }
254
255 zero_bytes(session, sizeof(etw_session_t));
256 session->callback = callback;
257 session->context = context;
258 session->session_handle = 0;
259 session->trace_handle = INVALID_PROCESSTRACE_HANDLE;
260 session->thread_handle = NULL;
261 session->properties = NULL;
262 session->session_name_w = NULL;
263 session->provider_guid = provider_guid;
264
265 /* ワイド文字列セッション名を確保 */
266 session->session_name_w = (wchar_t *)malloc((size_t)name_len_w * sizeof(wchar_t));
267 if (session->session_name_w == NULL)
268 {
269 set_status(out_status, ETW_SESSION_ERR_SYSTEM);
270 goto cleanup;
271 }
272 MultiByteToWideChar(CP_UTF8, 0, session_name, -1,
273 session->session_name_w, name_len_w);
274
275 /* EVENT_TRACE_PROPERTIES を確保 (セッション名領域を含む) */
276 props_size = sizeof(EVENT_TRACE_PROPERTIES) + ((size_t)name_len_w * sizeof(wchar_t));
277 session->properties = (EVENT_TRACE_PROPERTIES *)malloc(props_size);
278 if (session->properties == NULL)
279 {
280 set_status(out_status, ETW_SESSION_ERR_SYSTEM);
281 goto cleanup;
282 }
283
284 /* リアルタイムセッションを開始 */
285 zero_bytes(session->properties, props_size);
286 session->properties->Wnode.BufferSize = (ULONG)props_size;
287 session->properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
288 session->properties->Wnode.ClientContext = 1; /* QPC */
289 session->properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
290 session->properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
291 session->properties->FlushTimer = 1;
292
293 status = StartTraceW(&session->session_handle,
294 session->session_name_w,
295 session->properties);
296
297 if (status == ERROR_ACCESS_DENIED)
298 {
299 session->session_handle = 0;
300 set_status(out_status, ETW_SESSION_ERR_ACCESS);
301 goto cleanup;
302 }
303
304 if (status != ERROR_SUCCESS)
305 {
306 session->session_handle = 0;
307 set_status(out_status, ETW_SESSION_ERR_SYSTEM);
308 goto cleanup;
309 }
310
311 /* プロバイダを有効化 */
312 etp.Version = ENABLE_TRACE_PARAMETERS_VERSION_2;
313 status = EnableTraceEx2(session->session_handle,
314 &provider_guid,
315 EVENT_CONTROL_CODE_ENABLE_PROVIDER,
316 TRACE_LEVEL_VERBOSE,
317 0xFFFFFFFFFFFFFFFF, 0, 0, &etp);
318 if (status != ERROR_SUCCESS)
319 {
320 set_status(out_status, ETW_SESSION_ERR_SYSTEM);
321 goto cleanup;
322 }
323
324 /* トレースをオープンしワーカースレッドを起動 */
325 {
326 EVENT_TRACE_LOGFILEW trace_logfile = {0};
327 trace_logfile.LoggerName = session->session_name_w;
328 trace_logfile.ProcessTraceMode =
329 PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
330 trace_logfile.EventRecordCallback = event_record_callback;
331 trace_logfile.Context = session;
332
333 session->trace_handle = OpenTraceW(&trace_logfile);
334 }
335 if (session->trace_handle == INVALID_PROCESSTRACE_HANDLE)
336 {
337 set_status(out_status, ETW_SESSION_ERR_SYSTEM);
338 goto cleanup;
339 }
340
341 session->thread_handle = CreateThread(
342 NULL, 0, trace_thread_proc, session, 0, NULL);
343 if (session->thread_handle == NULL)
344 {
345 set_status(out_status, ETW_SESSION_ERR_SYSTEM);
346 goto cleanup;
347 }
348
349 set_status(out_status, ETW_SESSION_OK);
350 return session;
351
352cleanup:
353 if (session != NULL)
354 {
355 if (session->trace_handle != INVALID_PROCESSTRACE_HANDLE)
356 {
357 CloseTrace(session->trace_handle);
358 }
359 if (session->session_handle != 0)
360 {
361 ControlTraceW(session->session_handle, NULL,
362 session->properties, EVENT_TRACE_CONTROL_STOP);
363 }
364 free(session->session_name_w);
365 free(session->properties);
366 free(session);
367 }
368 return NULL;
369}
370
371 void TRACE_ETW_UTIL_API
372 etw_session_stop(etw_session_t *session)
373{
374 if (session == NULL)
375 {
376 return;
377 }
378
379 /* セッション停止 (バッファフラッシュ → ProcessTrace が残イベントを処理して戻る) */
380 if (session->session_handle != 0 && session->properties != NULL)
381 {
382 ControlTraceW(session->session_handle, NULL,
383 session->properties, EVENT_TRACE_CONTROL_STOP);
384 }
385
386 /* ワーカースレッド join */
387 if (session->thread_handle != NULL)
388 {
389 WaitForSingleObject(session->thread_handle, INFINITE);
390 CloseHandle(session->thread_handle);
391 }
392
393 /* トレースハンドルクローズ */
394 if (session->trace_handle != INVALID_PROCESSTRACE_HANDLE)
395 {
396 CloseTrace(session->trace_handle);
397 }
398
399 free(session->session_name_w);
400 free(session->properties);
401 free(session);
402}
403
404#endif /* _WIN32 */
trace-etw-util.h
ETW (Event Tracing for Windows) ヘルパーライブラリ。