| 42 | } |
| 43 | |
| 44 | EtwLogSession::EtwLogSession(const std::wstring& loggerName, const std::filesystem::path& logFileDirectory, |
| 45 | std::span<const EtwProviderDescription> providers) |
| 46 | { |
| 47 | // assemble the path for the etl file |
| 48 | auto logFilePath = logFileDirectory / file::TempFile::MakeRandomName(); |
| 49 | // create / start the trace session that outputs to .etl file |
| 50 | traceProps_.Wnode.BufferSize = sizeof(traceProps_); |
| 51 | traceProps_.MaximumFileSize = 1024; |
| 52 | traceProps_.Wnode.Flags = WNODE_FLAG_TRACED_GUID; |
| 53 | traceProps_.Wnode.ClientContext = TIMESTAMP_TYPE_QPC; |
| 54 | traceProps_.LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL; |
| 55 | traceProps_.LoggerNameOffset = offsetof(TraceProperties_, LoggerName); |
| 56 | traceProps_.LogFileNameOffset = offsetof(TraceProperties_, LogFileName); |
| 57 | // consider zeroing this to match PresentData |
| 58 | traceProps_.BufferSize = 64; |
| 59 | wcscpy_s(traceProps_.LoggerName, std::size(traceProps_.LoggerName), loggerName.c_str()); |
| 60 | wcscpy_s(traceProps_.LogFileName, std::size(traceProps_.LogFileName), logFilePath.c_str()); |
| 61 | // create the trace logger session |
| 62 | if (auto sta = StartTraceW(&hTraceSession_, traceProps_.LoggerName, &traceProps_); |
| 63 | sta != ERROR_SUCCESS) { |
| 64 | pmlog_error("Failed to start ETL trace").hr(sta).raise<util::Exception>(); |
| 65 | } |
| 66 | // enable providers with various filter mechanisms base on the injected provider descriptions |
| 67 | for (auto& p : providers) { |
| 68 | // filter by event id if there are any ids captured by the listener (otherwise assume unfiltered) |
| 69 | if (!p.events.empty()) { |
| 70 | // event filter that filters by event ID whitelist, payload size is dynamic so allocate blob |
| 71 | // EVENT_FILTER_EVENT_ID contains a ushort placeholder representing start of array we subtract |
| 72 | const size_t eventIdFilterSize = sizeof(EVENT_FILTER_EVENT_ID) + |
| 73 | sizeof(USHORT) * (p.events.size() - ANYSIZE_ARRAY); |
| 74 | auto pEventIdFilter = static_cast<EVENT_FILTER_EVENT_ID*>(alloca(eventIdFilterSize)); |
| 75 | pEventIdFilter->FilterIn = TRUE; |
| 76 | pEventIdFilter->Reserved = 0; |
| 77 | pEventIdFilter->Count = (USHORT)p.events.size(); |
| 78 | rn::copy(p.events, pEventIdFilter->Events); |
| 79 | // descriptor for the event filter |
| 80 | EVENT_FILTER_DESCRIPTOR filterDesc{ |
| 81 | .Ptr = reinterpret_cast<ULONGLONG>(pEventIdFilter), |
| 82 | .Size = (ULONG)eventIdFilterSize, |
| 83 | .Type = EVENT_FILTER_TYPE_EVENT_ID, |
| 84 | }; |
| 85 | // parameter struct to feed our filter into the enable call |
| 86 | ENABLE_TRACE_PARAMETERS enableParams{ |
| 87 | .Version = ENABLE_TRACE_PARAMETERS_VERSION_2, |
| 88 | .EnableProperty = EVENT_ENABLE_PROPERTY_IGNORE_KEYWORD_0, |
| 89 | .SourceId = traceProps_.Wnode.Guid, |
| 90 | .EnableFilterDesc = &filterDesc, |
| 91 | .FilterDescCount = 1, |
| 92 | }; |
| 93 | // enable the provider with event id filter |
| 94 | if (auto sta = EnableTraceEx2(hTraceSession_, &p.providerGuid, p.controlCode, p.maxLevel, |
| 95 | p.anyKeyMask, p.allKeyMask, 0, &enableParams); sta != ERROR_SUCCESS) { |
| 96 | auto providerGuid = util::str::ToNarrow(util::win::GuidToString(p.providerGuid)); |
| 97 | pmlog_warn("Failed to enable ETW provider").hr(sta).pmwatch(providerGuid); |
| 98 | } |
| 99 | } |
| 100 | else { |
| 101 | // enable the provider without event filter |