use std::alloc::Layout;
use super::etw_types::*;
use crate::traits::*;
use crate::native::tdh_types::Property;
use crate::native::etw_types::event_record::EventRecord;
use windows::Win32::System::Diagnostics::Etw::{self, TRACE_EVENT_INFO, EVENT_PROPERTY_INFO};
use windows::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
use windows::core::GUID;
use widestring::U16CStr;
#[derive(Debug)]
pub enum TdhNativeError {
AllocationError,
IoError(std::io::Error),
}
pub type TdhNativeResult<T> = Result<T, TdhNativeError>;
pub struct TraceEventInfo{
data: *const u8,
mut_data_for_dealloc: *mut u8,
layout: Layout,
}
unsafe impl Send for TraceEventInfo {}
unsafe impl Sync for TraceEventInfo {}
macro_rules! extract_utf16_string {
($self: ident, $member_name: ident) => {
let provider_name_offset = $self.as_raw().$member_name;
let provider_name_ptr = unsafe {
$self.data.offset(provider_name_offset as isize)
};
if provider_name_offset == 0 || provider_name_ptr.is_null() {
return String::new();
}
let provider_name = unsafe {
U16CStr::from_ptr_str(provider_name_ptr as *const u16)
};
return provider_name.to_string_lossy();
}
}
impl TraceEventInfo {
pub fn build_from_event(event: &EventRecord) -> TdhNativeResult<Self> {
let mut buffer_size = 0;
let status = unsafe {
Etw::TdhGetEventInformation(
event.as_raw_ptr(),
None,
None,
&mut buffer_size,
)
};
if status != ERROR_INSUFFICIENT_BUFFER.0 {
return Err(TdhNativeError::IoError(std::io::Error::from_raw_os_error(status as i32)));
}
if buffer_size == 0 {
return Err(TdhNativeError::AllocationError);
}
let layout = Layout::from_size_align(buffer_size as usize, std::mem::align_of::<Etw::TRACE_EVENT_INFO>())
.map_err(|_| TdhNativeError::AllocationError)?;
let data = unsafe {
std::alloc::alloc(layout)
};
if data.is_null() {
return Err(TdhNativeError::AllocationError);
}
let status = unsafe {
Etw::TdhGetEventInformation(
event.as_raw_ptr(),
None,
Some(data.cast::<TRACE_EVENT_INFO>()),
&mut buffer_size,
)
};
if status != 0 {
return Err(TdhNativeError::IoError(std::io::Error::from_raw_os_error(status as i32)));
}
Ok(Self { data, mut_data_for_dealloc: data, layout })
}
fn as_raw(&self) -> &TRACE_EVENT_INFO {
let p = self.data.cast::<TRACE_EVENT_INFO>();
unsafe {
p.as_ref().unwrap()
}
}
pub fn provider_guid(&self) -> GUID {
self.as_raw().ProviderGuid
}
pub fn event_id(&self) -> u16 {
self.as_raw().EventDescriptor.Id
}
pub fn event_version(&self) -> u8 {
self.as_raw().EventDescriptor.Version
}
pub fn decoding_source(&self) -> DecodingSource {
let ds = self.as_raw().DecodingSource;
DecodingSource::from(ds)
}
pub fn provider_name(&self) -> String {
extract_utf16_string!(self, ProviderNameOffset);
}
pub fn task_name(&self) -> String {
extract_utf16_string!(self, TaskNameOffset);
}
pub fn opcode_name(&self) -> String {
extract_utf16_string!(self, OpcodeNameOffset);
}
pub fn properties(&self) -> PropertyIterator {
PropertyIterator::new(self)
}
}
impl Drop for TraceEventInfo {
fn drop(&mut self) {
unsafe {
std::alloc::dealloc(self.mut_data_for_dealloc, self.layout);
}
}
}
pub struct PropertyIterator<'info> {
next_index: u32,
count: u32,
te_info: &'info TraceEventInfo,
}
impl<'info> PropertyIterator<'info> {
fn new(te_info: &'info TraceEventInfo) -> Self {
let count = te_info.as_raw().PropertyCount;
Self { next_index: 0, count, te_info }
}
}
impl<'info> Iterator for PropertyIterator<'info> {
type Item = Result<Property, crate::native::tdh_types::PropertyError>;
fn next(&mut self) -> Option<Self::Item> {
if self.next_index == self.count {
return None;
}
let properties_array = &self.te_info.as_raw().EventPropertyInfoArray;
let properties_array = properties_array as *const EVENT_PROPERTY_INFO;
let cur_property_ptr = unsafe {
properties_array.offset(self.next_index as isize) };
let curr_prop = unsafe {
match cur_property_ptr.as_ref() {
None => {
return None;
}
Some(r) => r,
}
};
let te_info_data = self.te_info.as_raw() as *const TRACE_EVENT_INFO as *const u8;
let property_name_offset = curr_prop.NameOffset;
let property_name_ptr = unsafe {
te_info_data.offset(property_name_offset as isize)
};
if property_name_ptr.is_null() {
return None;
}
let property_name = unsafe {
U16CStr::from_ptr_str(property_name_ptr as *const u16)
};
let property_name = property_name.to_string_lossy();
self.next_index += 1;
Some(Property::new(property_name, curr_prop))
}
}
pub fn property_size(event: &EventRecord, name: &str) -> TdhNativeResult<u32> {
let mut property_size = 0;
let name = name.into_utf16();
let desc = Etw::PROPERTY_DATA_DESCRIPTOR{
ArrayIndex: u32::MAX,
PropertyName: name.as_ptr() as u64,
..Default::default()
};
unsafe {
let status = Etw::TdhGetPropertySize(
event.as_raw_ptr(),
None,
&[desc],
&mut property_size,
);
if status != 0 {
return Err(TdhNativeError::IoError(std::io::Error::from_raw_os_error(
status as i32,
)));
}
}
Ok(property_size)
}