use crate::provider::event_filter::EventFilterDescriptor;
use crate::provider::TraceFlags;
use crate::trace::{TraceProperties, TraceTrait};
use crate::trace::callback_data::CallbackData;
use std::ffi::{c_void, OsString};
use std::fmt::Formatter;
use std::marker::PhantomData;
use std::sync::Arc;
use windows::core::GUID;
use windows::core::PWSTR;
use windows::Win32::System::Diagnostics::Etw;
use windows::Win32::System::Diagnostics::Etw::EVENT_FILTER_DESCRIPTOR;
use widestring::{U16CStr, U16CString};
pub(crate) mod event_record;
pub(crate) mod extended_data;
pub const TRACE_NAME_MAX_CHARS: usize = 200;
#[derive(Debug, Copy, Clone)]
#[allow(dead_code)]
#[non_exhaustive]
#[repr(i32)]
pub enum TraceInformation {
TraceGuidQueryList,
TraceGuidQueryInfo,
TraceGuidQueryProcess,
TraceStackTracingInfo,
TraceSystemTraceEnableFlagsInfo,
TraceSampledProfileIntervalInfo,
TraceProfileSourceConfigInfo,
TraceProfileSourceListInfo,
TracePmcEventListInfo,
TracePmcCounterListInfo,
TraceSetDisallowList,
TraceVersionInfo,
TraceGroupQueryList,
TraceGroupQueryInfo,
TraceDisallowListQuery,
TraceInfoReserved15,
TracePeriodicCaptureStateListInfo,
TracePeriodicCaptureStateInfo,
TraceProviderBinaryTracking,
TraceMaxLoggersQuery,
TraceLbrConfigurationInfo,
TraceLbrEventListInfo,
TraceMaxPmcCounterQuery,
TraceStreamCount,
TraceStackCachingInfo,
TracePmcCounterOwners,
TraceUnifiedStackCachingInfo,
TracePmcSessionInformation,
MaxTraceSetInfoClass,
}
#[allow(dead_code)]
pub(crate) enum ControlValues {
Query = 0,
Stop = 1,
Update = 2,
}
bitflags! {
pub struct LoggingMode: u32 {
const EVENT_TRACE_FILE_MODE_NONE = Etw::EVENT_TRACE_FILE_MODE_NONE;
const EVENT_TRACE_FILE_MODE_SEQUENTIAL = Etw::EVENT_TRACE_FILE_MODE_SEQUENTIAL;
const EVENT_TRACE_FILE_MODE_CIRCULAR = Etw::EVENT_TRACE_FILE_MODE_CIRCULAR;
const EVENT_TRACE_FILE_MODE_APPEND = Etw::EVENT_TRACE_FILE_MODE_APPEND;
const EVENT_TRACE_FILE_MODE_NEWFILE = Etw::EVENT_TRACE_FILE_MODE_NEWFILE;
const EVENT_TRACE_FILE_MODE_PREALLOCATE = Etw::EVENT_TRACE_FILE_MODE_PREALLOCATE;
const EVENT_TRACE_NONSTOPPABLE_MODE = Etw::EVENT_TRACE_NONSTOPPABLE_MODE;
const EVENT_TRACE_SECURE_MODE = Etw::EVENT_TRACE_SECURE_MODE;
const EVENT_TRACE_REAL_TIME_MODE = Etw::EVENT_TRACE_REAL_TIME_MODE;
const EVENT_TRACE_DELAY_OPEN_FILE_MODE = Etw::EVENT_TRACE_DELAY_OPEN_FILE_MODE;
const EVENT_TRACE_BUFFERING_MODE = Etw::EVENT_TRACE_BUFFERING_MODE;
const EVENT_TRACE_PRIVATE_LOGGER_MODE = Etw::EVENT_TRACE_PRIVATE_LOGGER_MODE;
const EVENT_TRACE_USE_KBYTES_FOR_SIZE = Etw::EVENT_TRACE_USE_KBYTES_FOR_SIZE;
const EVENT_TRACE_USE_GLOBAL_SEQUENCE = Etw::EVENT_TRACE_USE_GLOBAL_SEQUENCE;
const EVENT_TRACE_USE_LOCAL_SEQUENCE = Etw::EVENT_TRACE_USE_LOCAL_SEQUENCE;
const EVENT_TRACE_PRIVATE_IN_PROC = Etw::EVENT_TRACE_PRIVATE_IN_PROC;
const EVENT_TRACE_MODE_RESERVED = Etw::EVENT_TRACE_MODE_RESERVED;
const EVENT_TRACE_STOP_ON_HYBRID_SHUTDOWN = Etw::EVENT_TRACE_STOP_ON_HYBRID_SHUTDOWN;
const EVENT_TRACE_PERSIST_ON_HYBRID_SHUTDOWN = Etw::EVENT_TRACE_PERSIST_ON_HYBRID_SHUTDOWN;
const EVENT_TRACE_USE_PAGED_MEMORY = Etw::EVENT_TRACE_USE_PAGED_MEMORY;
const EVENT_TRACE_SYSTEM_LOGGER_MODE = Etw::EVENT_TRACE_SYSTEM_LOGGER_MODE;
const EVENT_TRACE_INDEPENDENT_SESSION_MODE = Etw::EVENT_TRACE_INDEPENDENT_SESSION_MODE;
const EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING = Etw::EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING;
const EVENT_TRACE_ADDTO_TRIAGE_DUMP = Etw::EVENT_TRACE_ADDTO_TRIAGE_DUMP;
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EventTraceProperties {
etw_trace_properties: Etw::EVENT_TRACE_PROPERTIES,
wide_trace_name: [u16; TRACE_NAME_MAX_CHARS+1], wide_log_file_name: [u16; TRACE_NAME_MAX_CHARS+1], }
impl std::fmt::Debug for EventTraceProperties {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let name = U16CString::from_vec_truncate(self.wide_trace_name).to_string_lossy();
f.debug_struct("EventTraceProperties")
.field("name", &name)
.finish()
}
}
impl EventTraceProperties {
pub(crate) fn new<T>(
trace_name: &U16CStr,
trace_properties: &TraceProperties,
enable_flags: Etw::EVENT_TRACE_FLAG,
) -> Self
where
T: TraceTrait
{
let mut etw_trace_properties = Etw::EVENT_TRACE_PROPERTIES::default();
etw_trace_properties.Wnode.BufferSize = std::mem::size_of::<EventTraceProperties>() as u32;
etw_trace_properties.Wnode.Guid = T::trace_guid();
etw_trace_properties.Wnode.Flags = Etw::WNODE_FLAG_TRACED_GUID;
etw_trace_properties.Wnode.ClientContext = 1; etw_trace_properties.BufferSize = trace_properties.buffer_size;
etw_trace_properties.MinimumBuffers = trace_properties.min_buffer;
etw_trace_properties.MaximumBuffers = trace_properties.max_buffer;
etw_trace_properties.FlushTimer = trace_properties.flush_timer.as_secs().clamp(1, u32::MAX as u64) as u32;
if trace_properties.log_file_mode.is_empty() == false {
etw_trace_properties.LogFileMode = trace_properties.log_file_mode.bits();
} else {
etw_trace_properties.LogFileMode =
(LoggingMode::EVENT_TRACE_REAL_TIME_MODE | LoggingMode::EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING).bits()
}
etw_trace_properties.LogFileMode |= T::augmented_file_mode();
etw_trace_properties.EnableFlags = enable_flags;
etw_trace_properties.LoggerNameOffset = offset_of!(EventTraceProperties, wide_log_file_name) as u32;
let mut s = Self {
etw_trace_properties,
wide_trace_name: [0u16; TRACE_NAME_MAX_CHARS+1],
wide_log_file_name: [0u16; TRACE_NAME_MAX_CHARS+1],
};
let name_len = trace_name.len().min(TRACE_NAME_MAX_CHARS);
s.wide_trace_name[..name_len].copy_from_slice(&trace_name.as_slice()[..name_len]);
s
}
pub unsafe fn as_mut_ptr(&mut self) -> *mut Etw::EVENT_TRACE_PROPERTIES {
&mut self.etw_trace_properties as *mut Etw::EVENT_TRACE_PROPERTIES
}
pub fn trace_name_array(&self) -> &[u16] {
&self.wide_trace_name
}
pub fn name(&self) -> OsString {
widestring::U16CStr::from_slice_truncate(&self.wide_trace_name)
.map(|ws| ws.to_os_string())
.unwrap_or_else(|_| OsString::from("<invalid name>"))
}
}
#[repr(C)]
#[derive(Clone)]
pub struct EventTraceLogfile<'callbackdata> {
native: Etw::EVENT_TRACE_LOGFILEW,
wide_logger_name: U16CString,
lifetime: PhantomData<&'callbackdata CallbackData>,
}
impl<'callbackdata> EventTraceLogfile<'callbackdata> {
#[allow(clippy::borrowed_box)] pub fn create(callback_data: &'callbackdata Box<Arc<CallbackData>>, mut wide_logger_name: U16CString, callback: unsafe extern "system" fn(*mut Etw::EVENT_RECORD)) -> Self {
let not_really_mut_ptr = callback_data.as_ref() as *const Arc<CallbackData> as *const c_void as *mut c_void;
let native = Etw::EVENT_TRACE_LOGFILEW {
LoggerName: PWSTR(wide_logger_name.as_mut_ptr()),
Anonymous1: Etw::EVENT_TRACE_LOGFILEW_0 {
ProcessTraceMode: Etw::PROCESS_TRACE_MODE_REAL_TIME | Etw::PROCESS_TRACE_MODE_EVENT_RECORD
},
Anonymous2: Etw::EVENT_TRACE_LOGFILEW_1 {
EventRecordCallback: Some(callback)
},
Context: not_really_mut_ptr,
..Default::default()
};
Self {
native,
wide_logger_name,
lifetime: PhantomData,
}
}
pub(crate) unsafe fn as_mut_ptr(&mut self) -> *mut Etw::EVENT_TRACE_LOGFILEW {
&mut self.native as *mut Etw::EVENT_TRACE_LOGFILEW
}
pub fn context_ptr(&self) -> *const std::ffi::c_void {
self.native.Context
}
}
#[repr(C)]
#[derive(Clone, Default)]
pub struct EnableTraceParameters<'filters>{
native: Etw::ENABLE_TRACE_PARAMETERS,
array_of_event_filter_descriptor: Vec<EVENT_FILTER_DESCRIPTOR>,
lifetime: PhantomData<&'filters EventFilterDescriptor>,
}
impl<'filters> EnableTraceParameters<'filters> {
pub fn create(guid: GUID, trace_flags: TraceFlags, filters: &'filters [EventFilterDescriptor]) -> Self {
let mut params = EnableTraceParameters::default();
params.native.ControlFlags = 0;
params.native.Version = Etw::ENABLE_TRACE_PARAMETERS_VERSION_2;
params.native.SourceId = guid;
params.native.EnableProperty = trace_flags.bits();
params.array_of_event_filter_descriptor = filters
.iter()
.map(|efd| efd.as_event_filter_descriptor())
.collect();
params.native.FilterDescCount = params.array_of_event_filter_descriptor.len() as u32; if filters.is_empty() {
params.native.EnableFilterDesc = std::ptr::null_mut();
} else {
params.native.EnableFilterDesc = params.array_of_event_filter_descriptor.as_mut_ptr();
}
params
}
pub fn as_ptr(&self) -> *const Etw::ENABLE_TRACE_PARAMETERS {
&self.native as *const _
}
}
#[derive(Debug)]
pub enum DecodingSource {
DecodingSourceXMLFile,
DecodingSourceWbem,
DecodingSourceWPP,
DecodingSourceTlg,
DecodingSourceMax,
}
impl From<Etw::DECODING_SOURCE> for DecodingSource {
fn from(val: Etw::DECODING_SOURCE) -> Self {
match val {
Etw::DecodingSourceXMLFile => DecodingSource::DecodingSourceXMLFile,
Etw::DecodingSourceWbem => DecodingSource::DecodingSourceWbem,
Etw::DecodingSourceWPP => DecodingSource::DecodingSourceWPP,
Etw::DecodingSourceTlg => DecodingSource::DecodingSourceTlg,
_ => DecodingSource::DecodingSourceMax,
}
}
}
#[doc(hidden)]
pub const EVENT_HEADER_FLAG_32_BIT_HEADER: u16 = Etw::EVENT_HEADER_FLAG_32_BIT_HEADER as u16;